• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 %                                  Cristy                                     %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2016 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 
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/cache-private.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/composite.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/delegate.h"
56 #include "MagickCore/display.h"
57 #include "MagickCore/display-private.h"
58 #include "MagickCore/distort.h"
59 #include "MagickCore/draw.h"
60 #include "MagickCore/effect.h"
61 #include "MagickCore/enhance.h"
62 #include "MagickCore/exception.h"
63 #include "MagickCore/exception-private.h"
64 #include "MagickCore/fx.h"
65 #include "MagickCore/geometry.h"
66 #include "MagickCore/image.h"
67 #include "MagickCore/image-private.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/memory_.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/montage.h"
75 #include "MagickCore/nt-base-private.h"
76 #include "MagickCore/option.h"
77 #include "MagickCore/paint.h"
78 #include "MagickCore/pixel.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/property.h"
81 #include "MagickCore/quantum.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/resize.h"
84 #include "MagickCore/resource_.h"
85 #include "MagickCore/shear.h"
86 #include "MagickCore/segment.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/string-private.h"
90 #include "MagickCore/transform.h"
91 #include "MagickCore/transform-private.h"
92 #include "MagickCore/threshold.h"
93 #include "MagickCore/utility.h"
94 #include "MagickCore/utility-private.h"
95 #include "MagickCore/version.h"
96 #include "MagickCore/widget.h"
97 #include "MagickCore/widget-private.h"
98 #include "MagickCore/xwindow.h"
99 #include "MagickCore/xwindow-private.h"
100 
101 #if defined(MAGICKCORE_X11_DELEGATE)
102 /*
103   Define declarations.
104 */
105 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
106 
107 /*
108   Constant declarations.
109 */
110 static const unsigned char
111   HighlightBitmap[8] =
112   {
113     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
114   },
115   OpaqueBitmap[8] =
116   {
117     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
118   },
119   ShadowBitmap[8] =
120   {
121     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
122   };
123 
124 static const char
125   *PageSizes[] =
126   {
127     "Letter",
128     "Tabloid",
129     "Ledger",
130     "Legal",
131     "Statement",
132     "Executive",
133     "A3",
134     "A4",
135     "A5",
136     "B4",
137     "B5",
138     "Folio",
139     "Quarto",
140     "10x14",
141     (char *) NULL
142   };
143 
144 /*
145   Help widget declarations.
146 */
147 static const char
148   *ImageAnnotateHelp[] =
149   {
150     "In annotate mode, the Command widget has these options:",
151     "",
152     "    Font Name",
153     "      fixed",
154     "      variable",
155     "      5x8",
156     "      6x10",
157     "      7x13bold",
158     "      8x13bold",
159     "      9x15bold",
160     "      10x20",
161     "      12x24",
162     "      Browser...",
163     "    Font Color",
164     "      black",
165     "      blue",
166     "      cyan",
167     "      green",
168     "      gray",
169     "      red",
170     "      magenta",
171     "      yellow",
172     "      white",
173     "      transparent",
174     "      Browser...",
175     "    Font Color",
176     "      black",
177     "      blue",
178     "      cyan",
179     "      green",
180     "      gray",
181     "      red",
182     "      magenta",
183     "      yellow",
184     "      white",
185     "      transparent",
186     "      Browser...",
187     "    Rotate Text",
188     "      -90",
189     "      -45",
190     "      -30",
191     "      0",
192     "      30",
193     "      45",
194     "      90",
195     "      180",
196     "      Dialog...",
197     "    Help",
198     "    Dismiss",
199     "",
200     "Choose a font name from the Font Name sub-menu.  Additional",
201     "font names can be specified with the font browser.  You can",
202     "change the menu names by setting the X resources font1",
203     "through font9.",
204     "",
205     "Choose a font color from the Font Color sub-menu.",
206     "Additional font colors can be specified with the color",
207     "browser.  You can change the menu colors by setting the X",
208     "resources pen1 through pen9.",
209     "",
210     "If you select the color browser and press Grab, you can",
211     "choose the font color by moving the pointer to the desired",
212     "color on the screen and press any button.",
213     "",
214     "If you choose to rotate the text, choose Rotate Text from the",
215     "menu and select an angle.  Typically you will only want to",
216     "rotate one line of text at a time.  Depending on the angle you",
217     "choose, subsequent lines may end up overwriting each other.",
218     "",
219     "Choosing a font and its color is optional.  The default font",
220     "is fixed and the default color is black.  However, you must",
221     "choose a location to begin entering text and press button 1.",
222     "An underscore character will appear at the location of the",
223     "pointer.  The cursor changes to a pencil to indicate you are",
224     "in text mode.  To exit immediately, press Dismiss.",
225     "",
226     "In text mode, any key presses will display the character at",
227     "the location of the underscore and advance the underscore",
228     "cursor.  Enter your text and once completed press Apply to",
229     "finish your image annotation.  To correct errors press BACK",
230     "SPACE.  To delete an entire line of text, press DELETE.  Any",
231     "text that exceeds the boundaries of the image window is",
232     "automagically continued onto the next line.",
233     "",
234     "The actual color you request for the font is saved in the",
235     "image.  However, the color that appears in your image window",
236     "may be different.  For example, on a monochrome screen the",
237     "text will appear black or white even if you choose the color",
238     "red as the font color.  However, the image saved to a file",
239     "with -write is written with red lettering.  To assure the",
240     "correct color text in the final image, any PseudoClass image",
241     "is promoted to DirectClass (see miff(5)).  To force a",
242     "PseudoClass image to remain PseudoClass, use -colors.",
243     (char *) NULL,
244   },
245   *ImageChopHelp[] =
246   {
247     "In chop mode, the Command widget has these options:",
248     "",
249     "    Direction",
250     "      horizontal",
251     "      vertical",
252     "    Help",
253     "    Dismiss",
254     "",
255     "If the you choose the horizontal direction (this the",
256     "default), the area of the image between the two horizontal",
257     "endpoints of the chop line is removed.  Otherwise, the area",
258     "of the image between the two vertical endpoints of the chop",
259     "line is removed.",
260     "",
261     "Select a location within the image window to begin your chop,",
262     "press and hold any button.  Next, move the pointer to",
263     "another location in the image.  As you move a line will",
264     "connect the initial location and the pointer.  When you",
265     "release the button, the area within the image to chop is",
266     "determined by which direction you choose from the Command",
267     "widget.",
268     "",
269     "To cancel the image chopping, move the pointer back to the",
270     "starting point of the line and release the button.",
271     (char *) NULL,
272   },
273   *ImageColorEditHelp[] =
274   {
275     "In color edit mode, the Command widget has these options:",
276     "",
277     "    Method",
278     "      point",
279     "      replace",
280     "      floodfill",
281     "      filltoborder",
282     "      reset",
283     "    Pixel Color",
284     "      black",
285     "      blue",
286     "      cyan",
287     "      green",
288     "      gray",
289     "      red",
290     "      magenta",
291     "      yellow",
292     "      white",
293     "      Browser...",
294     "    Border Color",
295     "      black",
296     "      blue",
297     "      cyan",
298     "      green",
299     "      gray",
300     "      red",
301     "      magenta",
302     "      yellow",
303     "      white",
304     "      Browser...",
305     "    Fuzz",
306     "      0%",
307     "      2%",
308     "      5%",
309     "      10%",
310     "      15%",
311     "      Dialog...",
312     "    Undo",
313     "    Help",
314     "    Dismiss",
315     "",
316     "Choose a color editing method from the Method sub-menu",
317     "of the Command widget.  The point method recolors any pixel",
318     "selected with the pointer until the button is released.  The",
319     "replace method recolors any pixel that matches the color of",
320     "the pixel you select with a button press.  Floodfill recolors",
321     "any pixel that matches the color of the pixel you select with",
322     "a button press and is a neighbor.  Whereas filltoborder recolors",
323     "any neighbor pixel that is not the border color.  Finally reset",
324     "changes the entire image to the designated color.",
325     "",
326     "Next, choose a pixel color from the Pixel Color sub-menu.",
327     "Additional pixel colors can be specified with the color",
328     "browser.  You can change the menu colors by setting the X",
329     "resources pen1 through pen9.",
330     "",
331     "Now press button 1 to select a pixel within the image window",
332     "to change its color.  Additional pixels may be recolored as",
333     "prescribed by the method you choose.",
334     "",
335     "If the Magnify widget is mapped, it can be helpful in positioning",
336     "your pointer within the image (refer to button 2).",
337     "",
338     "The actual color you request for the pixels is saved in the",
339     "image.  However, the color that appears in your image window",
340     "may be different.  For example, on a monochrome screen the",
341     "pixel will appear black or white even if you choose the",
342     "color red as the pixel color.  However, the image saved to a",
343     "file with -write is written with red pixels.  To assure the",
344     "correct color text in the final image, any PseudoClass image",
345     "is promoted to DirectClass (see miff(5)).  To force a",
346     "PseudoClass image to remain PseudoClass, use -colors.",
347     (char *) NULL,
348   },
349   *ImageCompositeHelp[] =
350   {
351     "First a widget window is displayed requesting you to enter an",
352     "image name. Press Composite, Grab or type a file name.",
353     "Press Cancel if you choose not to create a composite image.",
354     "When you choose Grab, move the pointer to the desired window",
355     "and press any button.",
356     "",
357     "If the Composite image does not have any matte information,",
358     "you are informed and the file browser is displayed again.",
359     "Enter the name of a mask image.  The image is typically",
360     "grayscale and the same size as the composite image.  If the",
361     "image is not grayscale, it is converted to grayscale and the",
362     "resulting intensities are used as matte information.",
363     "",
364     "A small window appears showing the location of the cursor in",
365     "the image window. You are now in composite mode.  To exit",
366     "immediately, press Dismiss.  In composite mode, the Command",
367     "widget has these options:",
368     "",
369     "    Operators",
370     "      Over",
371     "      In",
372     "      Out",
373     "      Atop",
374     "      Xor",
375     "      Plus",
376     "      Minus",
377     "      Add",
378     "      Subtract",
379     "      Difference",
380     "      Multiply",
381     "      Bumpmap",
382     "      Copy",
383     "      CopyRed",
384     "      CopyGreen",
385     "      CopyBlue",
386     "      CopyOpacity",
387     "      Clear",
388     "    Dissolve",
389     "    Displace",
390     "    Help",
391     "    Dismiss",
392     "",
393     "Choose a composite operation from the Operators sub-menu of",
394     "the Command widget.  How each operator behaves is described",
395     "below.  Image window is the image currently displayed on",
396     "your X server and image is the image obtained with the File",
397     "Browser widget.",
398     "",
399     "Over     The result is the union of the two image shapes,",
400     "         with image obscuring image window in the region of",
401     "         overlap.",
402     "",
403     "In       The result is simply image cut by the shape of",
404     "         image window.  None of the image data of image",
405     "         window is in the result.",
406     "",
407     "Out      The resulting image is image with the shape of",
408     "         image window cut out.",
409     "",
410     "Atop     The result is the same shape as image image window,",
411     "         with image obscuring image window where the image",
412     "         shapes overlap.  Note this differs from over",
413     "         because the portion of image outside image window's",
414     "         shape does not appear in the result.",
415     "",
416     "Xor      The result is the image data from both image and",
417     "         image window that is outside the overlap region.",
418     "         The overlap region is blank.",
419     "",
420     "Plus     The result is just the sum of the image data.",
421     "         Output values are cropped to QuantumRange (no overflow).",
422     "",
423     "Minus    The result of image - image window, with underflow",
424     "         cropped to zero.",
425     "",
426     "Add      The result of image + image window, with overflow",
427     "         wrapping around (mod 256).",
428     "",
429     "Subtract The result of image - image window, with underflow",
430     "         wrapping around (mod 256).  The add and subtract",
431     "         operators can be used to perform reversible",
432     "         transformations.",
433     "",
434     "Difference",
435     "         The result of abs(image - image window).  This",
436     "         useful for comparing two very similar images.",
437     "",
438     "Multiply",
439     "         The result of image * image window.  This",
440     "         useful for the creation of drop-shadows.",
441     "",
442     "Bumpmap  The result of surface normals from image * image",
443     "         window.",
444     "",
445     "Copy     The resulting image is image window replaced with",
446     "         image.  Here the matte information is ignored.",
447     "",
448     "CopyRed  The red layer of the image window is replace with",
449     "         the red layer of the image.  The other layers are",
450     "         untouched.",
451     "",
452     "CopyGreen",
453     "         The green layer of the image window is replace with",
454     "         the green layer of the image.  The other layers are",
455     "         untouched.",
456     "",
457     "CopyBlue The blue layer of the image window is replace with",
458     "         the blue layer of the image.  The other layers are",
459     "         untouched.",
460     "",
461     "CopyOpacity",
462     "         The matte layer of the image window is replace with",
463     "         the matte layer of the image.  The other layers are",
464     "         untouched.",
465     "",
466     "The image compositor requires a matte, or alpha channel in",
467     "the image for some operations.  This extra channel usually",
468     "defines a mask which represents a sort of a cookie-cutter",
469     "for the image.  This the case when matte is opaque (full",
470     "coverage) for pixels inside the shape, zero outside, and",
471     "between 0 and QuantumRange on the boundary.  If image does not",
472     "have a matte channel, it is initialized with 0 for any pixel",
473     "matching in color to pixel location (0,0), otherwise QuantumRange.",
474     "",
475     "If you choose Dissolve, the composite operator becomes Over.  The",
476     "image matte channel percent transparency is initialized to factor.",
477     "The image window is initialized to (100-factor). Where factor is the",
478     "value you specify in the Dialog widget.",
479     "",
480     "Displace shifts the image pixels as defined by a displacement",
481     "map.  With this option, image is used as a displacement map.",
482     "Black, within the displacement map, is a maximum positive",
483     "displacement.  White is a maximum negative displacement and",
484     "middle gray is neutral.  The displacement is scaled to determine",
485     "the pixel shift.  By default, the displacement applies in both the",
486     "horizontal and vertical directions.  However, if you specify a mask,",
487     "image is the horizontal X displacement and mask the vertical Y",
488     "displacement.",
489     "",
490     "Note that matte information for image window is not retained",
491     "for colormapped X server visuals (e.g. StaticColor,",
492     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
493     "behavior may require a TrueColor or DirectColor visual or a",
494     "Standard Colormap.",
495     "",
496     "Choosing a composite operator is optional.  The default",
497     "operator is replace.  However, you must choose a location to",
498     "composite your image and press button 1.  Press and hold the",
499     "button before releasing and an outline of the image will",
500     "appear to help you identify your location.",
501     "",
502     "The actual colors of the composite image is saved.  However,",
503     "the color that appears in image window may be different.",
504     "For example, on a monochrome screen image window will appear",
505     "black or white even though your composited image may have",
506     "many colors.  If the image is saved to a file it is written",
507     "with the correct colors.  To assure the correct colors are",
508     "saved in the final image, any PseudoClass image is promoted",
509     "to DirectClass (see miff(5)).  To force a PseudoClass image",
510     "to remain PseudoClass, use -colors.",
511     (char *) NULL,
512   },
513   *ImageCutHelp[] =
514   {
515     "In cut mode, the Command widget has these options:",
516     "",
517     "    Help",
518     "    Dismiss",
519     "",
520     "To define a cut region, press button 1 and drag.  The",
521     "cut region is defined by a highlighted rectangle that",
522     "expands or contracts as it follows the pointer.  Once you",
523     "are satisfied with the cut region, release the button.",
524     "You are now in rectify mode.  In rectify mode, the Command",
525     "widget has these options:",
526     "",
527     "    Cut",
528     "    Help",
529     "    Dismiss",
530     "",
531     "You can make adjustments by moving the pointer to one of the",
532     "cut rectangle corners, pressing a button, and dragging.",
533     "Finally, press Cut to commit your copy region.  To",
534     "exit without cutting the image, press Dismiss.",
535     (char *) NULL,
536   },
537   *ImageCopyHelp[] =
538   {
539     "In copy mode, the Command widget has these options:",
540     "",
541     "    Help",
542     "    Dismiss",
543     "",
544     "To define a copy region, press button 1 and drag.  The",
545     "copy region is defined by a highlighted rectangle that",
546     "expands or contracts as it follows the pointer.  Once you",
547     "are satisfied with the copy region, release the button.",
548     "You are now in rectify mode.  In rectify mode, the Command",
549     "widget has these options:",
550     "",
551     "    Copy",
552     "    Help",
553     "    Dismiss",
554     "",
555     "You can make adjustments by moving the pointer to one of the",
556     "copy rectangle corners, pressing a button, and dragging.",
557     "Finally, press Copy to commit your copy region.  To",
558     "exit without copying the image, press Dismiss.",
559     (char *) NULL,
560   },
561   *ImageCropHelp[] =
562   {
563     "In crop mode, the Command widget has these options:",
564     "",
565     "    Help",
566     "    Dismiss",
567     "",
568     "To define a cropping region, press button 1 and drag.  The",
569     "cropping region is defined by a highlighted rectangle that",
570     "expands or contracts as it follows the pointer.  Once you",
571     "are satisfied with the cropping region, release the button.",
572     "You are now in rectify mode.  In rectify mode, the Command",
573     "widget has these options:",
574     "",
575     "    Crop",
576     "    Help",
577     "    Dismiss",
578     "",
579     "You can make adjustments by moving the pointer to one of the",
580     "cropping rectangle corners, pressing a button, and dragging.",
581     "Finally, press Crop to commit your cropping region.  To",
582     "exit without cropping the image, press Dismiss.",
583     (char *) NULL,
584   },
585   *ImageDrawHelp[] =
586   {
587     "The cursor changes to a crosshair to indicate you are in",
588     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
589     "the Command widget has these options:",
590     "",
591     "    Element",
592     "      point",
593     "      line",
594     "      rectangle",
595     "      fill rectangle",
596     "      circle",
597     "      fill circle",
598     "      ellipse",
599     "      fill ellipse",
600     "      polygon",
601     "      fill polygon",
602     "    Color",
603     "      black",
604     "      blue",
605     "      cyan",
606     "      green",
607     "      gray",
608     "      red",
609     "      magenta",
610     "      yellow",
611     "      white",
612     "      transparent",
613     "      Browser...",
614     "    Stipple",
615     "      Brick",
616     "      Diagonal",
617     "      Scales",
618     "      Vertical",
619     "      Wavy",
620     "      Translucent",
621     "      Opaque",
622     "      Open...",
623     "    Width",
624     "      1",
625     "      2",
626     "      4",
627     "      8",
628     "      16",
629     "      Dialog...",
630     "    Undo",
631     "    Help",
632     "    Dismiss",
633     "",
634     "Choose a drawing primitive from the Element sub-menu.",
635     "",
636     "Choose a color from the Color sub-menu.  Additional",
637     "colors can be specified with the color browser.",
638     "",
639     "If you choose the color browser and press Grab, you can",
640     "select the color by moving the pointer to the desired",
641     "color on the screen and press any button.  The transparent",
642     "color updates the image matte channel and is useful for",
643     "image compositing.",
644     "",
645     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
646     "Additional stipples can be specified with the file browser.",
647     "Stipples obtained from the file browser must be on disk in the",
648     "X11 bitmap format.",
649     "",
650     "Choose a width, if appropriate, from the Width sub-menu.  To",
651     "choose a specific width select the Dialog widget.",
652     "",
653     "Choose a point in the Image window and press button 1 and",
654     "hold.  Next, move the pointer to another location in the",
655     "image.  As you move, a line connects the initial location and",
656     "the pointer.  When you release the button, the image is",
657     "updated with the primitive you just drew.  For polygons, the",
658     "image is updated when you press and release the button without",
659     "moving the pointer.",
660     "",
661     "To cancel image drawing, move the pointer back to the",
662     "starting point of the line and release the button.",
663     (char *) NULL,
664   },
665   *DisplayHelp[] =
666   {
667     "BUTTONS",
668     "  The effects of each button press is described below.  Three",
669     "  buttons are required.  If you have a two button mouse,",
670     "  button 1 and 3 are returned.  Press ALT and button 3 to",
671     "  simulate button 2.",
672     "",
673     "  1    Press this button to map or unmap the Command widget.",
674     "",
675     "  2    Press and drag to define a region of the image to",
676     "       magnify.",
677     "",
678     "  3    Press and drag to choose from a select set of commands.",
679     "       This button behaves differently if the image being",
680     "       displayed is a visual image directory.  Here, choose a",
681     "       particular tile of the directory and press this button and",
682     "       drag to select a command from a pop-up menu.  Choose from",
683     "       these menu items:",
684     "",
685     "           Open",
686     "           Next",
687     "           Former",
688     "           Delete",
689     "           Update",
690     "",
691     "       If you choose Open, the image represented by the tile is",
692     "       displayed.  To return to the visual image directory, choose",
693     "       Next from the Command widget.  Next and Former moves to the",
694     "       next or former image respectively.  Choose Delete to delete",
695     "       a particular image tile.  Finally, choose Update to",
696     "       synchronize all the image tiles with their respective",
697     "       images.",
698     "",
699     "COMMAND WIDGET",
700     "  The Command widget lists a number of sub-menus and commands.",
701     "  They are",
702     "",
703     "      File",
704     "        Open...",
705     "        Next",
706     "        Former",
707     "        Select...",
708     "        Save...",
709     "        Print...",
710     "        Delete...",
711     "        New...",
712     "        Visual Directory...",
713     "        Quit",
714     "      Edit",
715     "        Undo",
716     "        Redo",
717     "        Cut",
718     "        Copy",
719     "        Paste",
720     "      View",
721     "        Half Size",
722     "        Original Size",
723     "        Double Size",
724     "        Resize...",
725     "        Apply",
726     "        Refresh",
727     "        Restore",
728     "      Transform",
729     "        Crop",
730     "        Chop",
731     "        Flop",
732     "        Flip",
733     "        Rotate Right",
734     "        Rotate Left",
735     "        Rotate...",
736     "        Shear...",
737     "        Roll...",
738     "        Trim Edges",
739     "      Enhance",
740     "        Brightness...",
741     "        Saturation...",
742     "        Hue...",
743     "        Gamma...",
744     "        Sharpen...",
745     "        Dull",
746     "        Contrast Stretch...",
747     "        Sigmoidal Contrast...",
748     "        Normalize",
749     "        Equalize",
750     "        Negate",
751     "        Grayscale",
752     "        Map...",
753     "        Quantize...",
754     "      Effects",
755     "        Despeckle",
756     "        Emboss",
757     "        Reduce Noise",
758     "        Add Noise",
759     "        Sharpen...",
760     "        Blur...",
761     "        Threshold...",
762     "        Edge Detect...",
763     "        Spread...",
764     "        Shade...",
765     "        Painting...",
766     "        Segment...",
767     "      F/X",
768     "        Solarize...",
769     "        Sepia Tone...",
770     "        Swirl...",
771     "        Implode...",
772     "        Vignette...",
773     "        Wave...",
774     "        Oil Painting...",
775     "        Charcoal Drawing...",
776     "      Image Edit",
777     "        Annotate...",
778     "        Draw...",
779     "        Color...",
780     "        Matte...",
781     "        Composite...",
782     "        Add Border...",
783     "        Add Frame...",
784     "        Comment...",
785     "        Launch...",
786     "        Region of Interest...",
787     "      Miscellany",
788     "        Image Info",
789     "        Zoom Image",
790     "        Show Preview...",
791     "        Show Histogram",
792     "        Show Matte",
793     "        Background...",
794     "        Slide Show",
795     "        Preferences...",
796     "      Help",
797     "        Overview",
798     "        Browse Documentation",
799     "        About Display",
800     "",
801     "  Menu items with a indented triangle have a sub-menu.  They",
802     "  are represented above as the indented items.  To access a",
803     "  sub-menu item, move the pointer to the appropriate menu and",
804     "  press a button and drag.  When you find the desired sub-menu",
805     "  item, release the button and the command is executed.  Move",
806     "  the pointer away from the sub-menu if you decide not to",
807     "  execute a particular command.",
808     "",
809     "KEYBOARD ACCELERATORS",
810     "  Accelerators are one or two key presses that effect a",
811     "  particular command.  The keyboard accelerators that",
812     "  display(1) understands is:",
813     "",
814     "  Ctl+O     Press to open an image from a file.",
815     "",
816     "  space     Press to display the next image.",
817     "",
818     "            If the image is a multi-paged document such as a Postscript",
819     "            document, you can skip ahead several pages by preceding",
820     "            this command with a number.  For example to display the",
821     "            third page beyond the current page, press 3<space>.",
822     "",
823     "  backspace Press to display the former image.",
824     "",
825     "            If the image is a multi-paged document such as a Postscript",
826     "            document, you can skip behind several pages by preceding",
827     "            this command with a number.  For example to display the",
828     "            third page preceding the current page, press 3<backspace>.",
829     "",
830     "  Ctl+S     Press to write the image to a file.",
831     "",
832     "  Ctl+P     Press to print the image to a Postscript printer.",
833     "",
834     "  Ctl+D     Press to delete an image file.",
835     "",
836     "  Ctl+N     Press to create a blank canvas.",
837     "",
838     "  Ctl+Q     Press to discard all images and exit program.",
839     "",
840     "  Ctl+Z     Press to undo last image transformation.",
841     "",
842     "  Ctl+R     Press to redo last image transformation.",
843     "",
844     "  Ctl+X     Press to cut a region of the image.",
845     "",
846     "  Ctl+C     Press to copy a region of the image.",
847     "",
848     "  Ctl+V     Press to paste a region to the image.",
849     "",
850     "  <         Press to half the image size.",
851     "",
852     "  -         Press to return to the original image size.",
853     "",
854     "  >         Press to double the image size.",
855     "",
856     "  %         Press to resize the image to a width and height you",
857     "            specify.",
858     "",
859     "Cmd-A       Press to make any image transformations permanent."
860     "",
861     "            By default, any image size transformations are applied",
862     "            to the original image to create the image displayed on",
863     "            the X server.  However, the transformations are not",
864     "            permanent (i.e. the original image does not change",
865     "            size only the X image does).  For example, if you",
866     "            press > the X image will appear to double in size,",
867     "            but the original image will in fact remain the same size.",
868     "            To force the original image to double in size, press >",
869     "            followed by Cmd-A.",
870     "",
871     "  @         Press to refresh the image window.",
872     "",
873     "  C         Press to cut out a rectangular region of the image.",
874     "",
875     "  [         Press to chop the image.",
876     "",
877     "  H         Press to flop image in the horizontal direction.",
878     "",
879     "  V         Press to flip image in the vertical direction.",
880     "",
881     "  /         Press to rotate the image 90 degrees clockwise.",
882     "",
883     " \\         Press to rotate the image 90 degrees counter-clockwise.",
884     "",
885     "  *         Press to rotate the image the number of degrees you",
886     "            specify.",
887     "",
888     "  S         Press to shear the image the number of degrees you",
889     "            specify.",
890     "",
891     "  R         Press to roll the image.",
892     "",
893     "  T         Press to trim the image edges.",
894     "",
895     "  Shft-H    Press to vary the image hue.",
896     "",
897     "  Shft-S    Press to vary the color saturation.",
898     "",
899     "  Shft-L    Press to vary the color brightness.",
900     "",
901     "  Shft-G    Press to gamma correct the image.",
902     "",
903     "  Shft-C    Press to sharpen the image contrast.",
904     "",
905     "  Shft-Z    Press to dull the image contrast.",
906     "",
907     "  =         Press to perform histogram equalization on the image.",
908     "",
909     "  Shft-N    Press to perform histogram normalization on the image.",
910     "",
911     "  Shft-~    Press to negate the colors of the image.",
912     "",
913     "  .         Press to convert the image colors to gray.",
914     "",
915     "  Shft-#    Press to set the maximum number of unique colors in the",
916     "            image.",
917     "",
918     "  F2        Press to reduce the speckles in an image.",
919     "",
920     "  F3        Press to eliminate peak noise from an image.",
921     "",
922     "  F4        Press to add noise to an image.",
923     "",
924     "  F5        Press to sharpen an image.",
925     "",
926     "  F6        Press to delete an image file.",
927     "",
928     "  F7        Press to threshold the image.",
929     "",
930     "  F8        Press to detect edges within an image.",
931     "",
932     "  F9        Press to emboss an image.",
933     "",
934     "  F10       Press to displace pixels by a random amount.",
935     "",
936     "  F11       Press to negate all pixels above the threshold level.",
937     "",
938     "  F12       Press to shade the image using a distant light source.",
939     "",
940     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
941     "",
942     "  F14       Press to segment the image by color.",
943     "",
944     "  Meta-S    Press to swirl image pixels about the center.",
945     "",
946     "  Meta-I    Press to implode image pixels about the center.",
947     "",
948     "  Meta-W    Press to alter an image along a sine wave.",
949     "",
950     "  Meta-P    Press to simulate an oil painting.",
951     "",
952     "  Meta-C    Press to simulate a charcoal drawing.",
953     "",
954     "  Alt-A     Press to annotate the image with text.",
955     "",
956     "  Alt-D     Press to draw on an image.",
957     "",
958     "  Alt-P     Press to edit an image pixel color.",
959     "",
960     "  Alt-M     Press to edit the image matte information.",
961     "",
962     "  Alt-V     Press to composite the image with another.",
963     "",
964     "  Alt-B     Press to add a border to the image.",
965     "",
966     "  Alt-F     Press to add an ornamental border to the image.",
967     "",
968     "  Alt-Shft-!",
969     "            Press to add an image comment.",
970     "",
971     "  Ctl-A     Press to apply image processing techniques to a region",
972     "            of interest.",
973     "",
974     "  Shft-?    Press to display information about the image.",
975     "",
976     "  Shft-+    Press to map the zoom image window.",
977     "",
978     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
979     "",
980     "  F1        Press to display helpful information about display(1).",
981     "",
982     "  Find      Press to browse documentation about ImageMagick.",
983     "",
984     "  1-9       Press to change the level of magnification.",
985     "",
986     "  Use the arrow keys to move the image one pixel up, down,",
987     "  left, or right within the magnify window.  Be sure to first",
988     "  map the magnify window by pressing button 2.",
989     "",
990     "  Press ALT and one of the arrow keys to trim off one pixel",
991     "  from any side of the image.",
992     (char *) NULL,
993   },
994   *ImageMatteEditHelp[] =
995   {
996     "Matte information within an image is useful for some",
997     "operations such as image compositing (See IMAGE",
998     "COMPOSITING).  This extra channel usually defines a mask",
999     "which represents a sort of a cookie-cutter for the image.",
1000     "This the case when matte is opaque (full coverage) for",
1001     "pixels inside the shape, zero outside, and between 0 and",
1002     "QuantumRange on the boundary.",
1003     "",
1004     "A small window appears showing the location of the cursor in",
1005     "the image window. You are now in matte edit mode.  To exit",
1006     "immediately, press Dismiss.  In matte edit mode, the Command",
1007     "widget has these options:",
1008     "",
1009     "    Method",
1010     "      point",
1011     "      replace",
1012     "      floodfill",
1013     "      filltoborder",
1014     "      reset",
1015     "    Border Color",
1016     "      black",
1017     "      blue",
1018     "      cyan",
1019     "      green",
1020     "      gray",
1021     "      red",
1022     "      magenta",
1023     "      yellow",
1024     "      white",
1025     "      Browser...",
1026     "    Fuzz",
1027     "      0%",
1028     "      2%",
1029     "      5%",
1030     "      10%",
1031     "      15%",
1032     "      Dialog...",
1033     "    Matte",
1034     "      Opaque",
1035     "      Transparent",
1036     "      Dialog...",
1037     "    Undo",
1038     "    Help",
1039     "    Dismiss",
1040     "",
1041     "Choose a matte editing method from the Method sub-menu of",
1042     "the Command widget.  The point method changes the matte value",
1043     "of any pixel selected with the pointer until the button is",
1044     "is released.  The replace method changes the matte value of",
1045     "any pixel that matches the color of the pixel you select with",
1046     "a button press.  Floodfill changes the matte value of any pixel",
1047     "that matches the color of the pixel you select with a button",
1048     "press and is a neighbor.  Whereas filltoborder changes the matte",
1049     "value any neighbor pixel that is not the border color.  Finally",
1050     "reset changes the entire image to the designated matte value.",
1051     "",
1052     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1053     "select the Dialog entry.  Here a dialog appears requesting a matte",
1054     "value.  The value you select is assigned as the opacity value of the",
1055     "selected pixel or pixels.",
1056     "",
1057     "Now, press any button to select a pixel within the image",
1058     "window to change its matte value.",
1059     "",
1060     "If the Magnify widget is mapped, it can be helpful in positioning",
1061     "your pointer within the image (refer to button 2).",
1062     "",
1063     "Matte information is only valid in a DirectClass image.",
1064     "Therefore, any PseudoClass image is promoted to DirectClass",
1065     "(see miff(5)).  Note that matte information for PseudoClass",
1066     "is not retained for colormapped X server visuals (e.g.",
1067     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1068     "immediately save your image to a file (refer to Write).",
1069     "Correct matte editing behavior may require a TrueColor or",
1070     "DirectColor visual or a Standard Colormap.",
1071     (char *) NULL,
1072   },
1073   *ImagePanHelp[] =
1074   {
1075     "When an image exceeds the width or height of the X server",
1076     "screen, display maps a small panning icon.  The rectangle",
1077     "within the panning icon shows the area that is currently",
1078     "displayed in the image window.  To pan about the image,",
1079     "press any button and drag the pointer within the panning",
1080     "icon.  The pan rectangle moves with the pointer and the",
1081     "image window is updated to reflect the location of the",
1082     "rectangle within the panning icon.  When you have selected",
1083     "the area of the image you wish to view, release the button.",
1084     "",
1085     "Use the arrow keys to pan the image one pixel up, down,",
1086     "left, or right within the image window.",
1087     "",
1088     "The panning icon is withdrawn if the image becomes smaller",
1089     "than the dimensions of the X server screen.",
1090     (char *) NULL,
1091   },
1092   *ImagePasteHelp[] =
1093   {
1094     "A small window appears showing the location of the cursor in",
1095     "the image window. You are now in paste mode.  To exit",
1096     "immediately, press Dismiss.  In paste mode, the Command",
1097     "widget has these options:",
1098     "",
1099     "    Operators",
1100     "      over",
1101     "      in",
1102     "      out",
1103     "      atop",
1104     "      xor",
1105     "      plus",
1106     "      minus",
1107     "      add",
1108     "      subtract",
1109     "      difference",
1110     "      replace",
1111     "    Help",
1112     "    Dismiss",
1113     "",
1114     "Choose a composite operation from the Operators sub-menu of",
1115     "the Command widget.  How each operator behaves is described",
1116     "below.  Image window is the image currently displayed on",
1117     "your X server and image is the image obtained with the File",
1118     "Browser widget.",
1119     "",
1120     "Over     The result is the union of the two image shapes,",
1121     "         with image obscuring image window in the region of",
1122     "         overlap.",
1123     "",
1124     "In       The result is simply image cut by the shape of",
1125     "         image window.  None of the image data of image",
1126     "         window is in the result.",
1127     "",
1128     "Out      The resulting image is image with the shape of",
1129     "         image window cut out.",
1130     "",
1131     "Atop     The result is the same shape as image image window,",
1132     "         with image obscuring image window where the image",
1133     "         shapes overlap.  Note this differs from over",
1134     "         because the portion of image outside image window's",
1135     "         shape does not appear in the result.",
1136     "",
1137     "Xor      The result is the image data from both image and",
1138     "         image window that is outside the overlap region.",
1139     "         The overlap region is blank.",
1140     "",
1141     "Plus     The result is just the sum of the image data.",
1142     "         Output values are cropped to QuantumRange (no overflow).",
1143     "         This operation is independent of the matte",
1144     "         channels.",
1145     "",
1146     "Minus    The result of image - image window, with underflow",
1147     "         cropped to zero.",
1148     "",
1149     "Add      The result of image + image window, with overflow",
1150     "         wrapping around (mod 256).",
1151     "",
1152     "Subtract The result of image - image window, with underflow",
1153     "         wrapping around (mod 256).  The add and subtract",
1154     "         operators can be used to perform reversible",
1155     "         transformations.",
1156     "",
1157     "Difference",
1158     "         The result of abs(image - image window).  This",
1159     "         useful for comparing two very similar images.",
1160     "",
1161     "Copy     The resulting image is image window replaced with",
1162     "         image.  Here the matte information is ignored.",
1163     "",
1164     "CopyRed  The red layer of the image window is replace with",
1165     "         the red layer of the image.  The other layers are",
1166     "         untouched.",
1167     "",
1168     "CopyGreen",
1169     "         The green layer of the image window is replace with",
1170     "         the green layer of the image.  The other layers are",
1171     "         untouched.",
1172     "",
1173     "CopyBlue The blue layer of the image window is replace with",
1174     "         the blue layer of the image.  The other layers are",
1175     "         untouched.",
1176     "",
1177     "CopyOpacity",
1178     "         The matte layer of the image window is replace with",
1179     "         the matte layer of the image.  The other layers are",
1180     "         untouched.",
1181     "",
1182     "The image compositor requires a matte, or alpha channel in",
1183     "the image for some operations.  This extra channel usually",
1184     "defines a mask which represents a sort of a cookie-cutter",
1185     "for the image.  This the case when matte is opaque (full",
1186     "coverage) for pixels inside the shape, zero outside, and",
1187     "between 0 and QuantumRange on the boundary.  If image does not",
1188     "have a matte channel, it is initialized with 0 for any pixel",
1189     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1190     "",
1191     "Note that matte information for image window is not retained",
1192     "for colormapped X server visuals (e.g. StaticColor,",
1193     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1194     "behavior may require a TrueColor or DirectColor visual or a",
1195     "Standard Colormap.",
1196     "",
1197     "Choosing a composite operator is optional.  The default",
1198     "operator is replace.  However, you must choose a location to",
1199     "paste your image and press button 1.  Press and hold the",
1200     "button before releasing and an outline of the image will",
1201     "appear to help you identify your location.",
1202     "",
1203     "The actual colors of the pasted image is saved.  However,",
1204     "the color that appears in image window may be different.",
1205     "For example, on a monochrome screen image window will appear",
1206     "black or white even though your pasted image may have",
1207     "many colors.  If the image is saved to a file it is written",
1208     "with the correct colors.  To assure the correct colors are",
1209     "saved in the final image, any PseudoClass image is promoted",
1210     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1211     "to remain PseudoClass, use -colors.",
1212     (char *) NULL,
1213   },
1214   *ImageROIHelp[] =
1215   {
1216     "In region of interest mode, the Command widget has these",
1217     "options:",
1218     "",
1219     "    Help",
1220     "    Dismiss",
1221     "",
1222     "To define a region of interest, press button 1 and drag.",
1223     "The region of interest is defined by a highlighted rectangle",
1224     "that expands or contracts as it follows the pointer.  Once",
1225     "you are satisfied with the region of interest, release the",
1226     "button.  You are now in apply mode.  In apply mode the",
1227     "Command widget has these options:",
1228     "",
1229     "      File",
1230     "        Save...",
1231     "        Print...",
1232     "      Edit",
1233     "        Undo",
1234     "        Redo",
1235     "      Transform",
1236     "        Flop",
1237     "        Flip",
1238     "        Rotate Right",
1239     "        Rotate Left",
1240     "      Enhance",
1241     "        Hue...",
1242     "        Saturation...",
1243     "        Brightness...",
1244     "        Gamma...",
1245     "        Spiff",
1246     "        Dull",
1247     "        Contrast Stretch",
1248     "        Sigmoidal Contrast...",
1249     "        Normalize",
1250     "        Equalize",
1251     "        Negate",
1252     "        Grayscale",
1253     "        Map...",
1254     "        Quantize...",
1255     "      Effects",
1256     "        Despeckle",
1257     "        Emboss",
1258     "        Reduce Noise",
1259     "        Sharpen...",
1260     "        Blur...",
1261     "        Threshold...",
1262     "        Edge Detect...",
1263     "        Spread...",
1264     "        Shade...",
1265     "        Raise...",
1266     "        Segment...",
1267     "      F/X",
1268     "        Solarize...",
1269     "        Sepia Tone...",
1270     "        Swirl...",
1271     "        Implode...",
1272     "        Vignette...",
1273     "        Wave...",
1274     "        Oil Painting...",
1275     "        Charcoal Drawing...",
1276     "      Miscellany",
1277     "        Image Info",
1278     "        Zoom Image",
1279     "        Show Preview...",
1280     "        Show Histogram",
1281     "        Show Matte",
1282     "      Help",
1283     "      Dismiss",
1284     "",
1285     "You can make adjustments to the region of interest by moving",
1286     "the pointer to one of the rectangle corners, pressing a",
1287     "button, and dragging.  Finally, choose an image processing",
1288     "technique from the Command widget.  You can choose more than",
1289     "one image processing technique to apply to an area.",
1290     "Alternatively, you can move the region of interest before",
1291     "applying another image processing technique.  To exit, press",
1292     "Dismiss.",
1293     (char *) NULL,
1294   },
1295   *ImageRotateHelp[] =
1296   {
1297     "In rotate mode, the Command widget has these options:",
1298     "",
1299     "    Pixel Color",
1300     "      black",
1301     "      blue",
1302     "      cyan",
1303     "      green",
1304     "      gray",
1305     "      red",
1306     "      magenta",
1307     "      yellow",
1308     "      white",
1309     "      Browser...",
1310     "    Direction",
1311     "      horizontal",
1312     "      vertical",
1313     "    Help",
1314     "    Dismiss",
1315     "",
1316     "Choose a background color from the Pixel Color sub-menu.",
1317     "Additional background colors can be specified with the color",
1318     "browser.  You can change the menu colors by setting the X",
1319     "resources pen1 through pen9.",
1320     "",
1321     "If you choose the color browser and press Grab, you can",
1322     "select the background color by moving the pointer to the",
1323     "desired color on the screen and press any button.",
1324     "",
1325     "Choose a point in the image window and press this button and",
1326     "hold.  Next, move the pointer to another location in the",
1327     "image.  As you move a line connects the initial location and",
1328     "the pointer.  When you release the button, the degree of",
1329     "image rotation is determined by the slope of the line you",
1330     "just drew.  The slope is relative to the direction you",
1331     "choose from the Direction sub-menu of the Command widget.",
1332     "",
1333     "To cancel the image rotation, move the pointer back to the",
1334     "starting point of the line and release the button.",
1335     (char *) NULL,
1336   };
1337 
1338 /*
1339   Enumeration declarations.
1340 */
1341 typedef enum
1342 {
1343   CopyMode,
1344   CropMode,
1345   CutMode
1346 } ClipboardMode;
1347 
1348 typedef enum
1349 {
1350   OpenCommand,
1351   NextCommand,
1352   FormerCommand,
1353   SelectCommand,
1354   SaveCommand,
1355   PrintCommand,
1356   DeleteCommand,
1357   NewCommand,
1358   VisualDirectoryCommand,
1359   QuitCommand,
1360   UndoCommand,
1361   RedoCommand,
1362   CutCommand,
1363   CopyCommand,
1364   PasteCommand,
1365   HalfSizeCommand,
1366   OriginalSizeCommand,
1367   DoubleSizeCommand,
1368   ResizeCommand,
1369   ApplyCommand,
1370   RefreshCommand,
1371   RestoreCommand,
1372   CropCommand,
1373   ChopCommand,
1374   FlopCommand,
1375   FlipCommand,
1376   RotateRightCommand,
1377   RotateLeftCommand,
1378   RotateCommand,
1379   ShearCommand,
1380   RollCommand,
1381   TrimCommand,
1382   HueCommand,
1383   SaturationCommand,
1384   BrightnessCommand,
1385   GammaCommand,
1386   SpiffCommand,
1387   DullCommand,
1388   ContrastStretchCommand,
1389   SigmoidalContrastCommand,
1390   NormalizeCommand,
1391   EqualizeCommand,
1392   NegateCommand,
1393   GrayscaleCommand,
1394   MapCommand,
1395   QuantizeCommand,
1396   DespeckleCommand,
1397   EmbossCommand,
1398   ReduceNoiseCommand,
1399   AddNoiseCommand,
1400   SharpenCommand,
1401   BlurCommand,
1402   ThresholdCommand,
1403   EdgeDetectCommand,
1404   SpreadCommand,
1405   ShadeCommand,
1406   RaiseCommand,
1407   SegmentCommand,
1408   SolarizeCommand,
1409   SepiaToneCommand,
1410   SwirlCommand,
1411   ImplodeCommand,
1412   VignetteCommand,
1413   WaveCommand,
1414   OilPaintCommand,
1415   CharcoalDrawCommand,
1416   AnnotateCommand,
1417   DrawCommand,
1418   ColorCommand,
1419   MatteCommand,
1420   CompositeCommand,
1421   AddBorderCommand,
1422   AddFrameCommand,
1423   CommentCommand,
1424   LaunchCommand,
1425   RegionofInterestCommand,
1426   ROIHelpCommand,
1427   ROIDismissCommand,
1428   InfoCommand,
1429   ZoomCommand,
1430   ShowPreviewCommand,
1431   ShowHistogramCommand,
1432   ShowMatteCommand,
1433   BackgroundCommand,
1434   SlideShowCommand,
1435   PreferencesCommand,
1436   HelpCommand,
1437   BrowseDocumentationCommand,
1438   VersionCommand,
1439   SaveToUndoBufferCommand,
1440   FreeBuffersCommand,
1441   NullCommand
1442 } CommandType;
1443 
1444 typedef enum
1445 {
1446   AnnotateNameCommand,
1447   AnnotateFontColorCommand,
1448   AnnotateBackgroundColorCommand,
1449   AnnotateRotateCommand,
1450   AnnotateHelpCommand,
1451   AnnotateDismissCommand,
1452   TextHelpCommand,
1453   TextApplyCommand,
1454   ChopDirectionCommand,
1455   ChopHelpCommand,
1456   ChopDismissCommand,
1457   HorizontalChopCommand,
1458   VerticalChopCommand,
1459   ColorEditMethodCommand,
1460   ColorEditColorCommand,
1461   ColorEditBorderCommand,
1462   ColorEditFuzzCommand,
1463   ColorEditUndoCommand,
1464   ColorEditHelpCommand,
1465   ColorEditDismissCommand,
1466   CompositeOperatorsCommand,
1467   CompositeDissolveCommand,
1468   CompositeDisplaceCommand,
1469   CompositeHelpCommand,
1470   CompositeDismissCommand,
1471   CropHelpCommand,
1472   CropDismissCommand,
1473   RectifyCopyCommand,
1474   RectifyHelpCommand,
1475   RectifyDismissCommand,
1476   DrawElementCommand,
1477   DrawColorCommand,
1478   DrawStippleCommand,
1479   DrawWidthCommand,
1480   DrawUndoCommand,
1481   DrawHelpCommand,
1482   DrawDismissCommand,
1483   MatteEditMethod,
1484   MatteEditBorderCommand,
1485   MatteEditFuzzCommand,
1486   MatteEditValueCommand,
1487   MatteEditUndoCommand,
1488   MatteEditHelpCommand,
1489   MatteEditDismissCommand,
1490   PasteOperatorsCommand,
1491   PasteHelpCommand,
1492   PasteDismissCommand,
1493   RotateColorCommand,
1494   RotateDirectionCommand,
1495   RotateCropCommand,
1496   RotateSharpenCommand,
1497   RotateHelpCommand,
1498   RotateDismissCommand,
1499   HorizontalRotateCommand,
1500   VerticalRotateCommand,
1501   TileLoadCommand,
1502   TileNextCommand,
1503   TileFormerCommand,
1504   TileDeleteCommand,
1505   TileUpdateCommand
1506 } ModeType;
1507 
1508 /*
1509   Stipples.
1510 */
1511 #define BricksWidth  20
1512 #define BricksHeight  20
1513 #define DiagonalWidth  16
1514 #define DiagonalHeight  16
1515 #define HighlightWidth  8
1516 #define HighlightHeight  8
1517 #define OpaqueWidth  8
1518 #define OpaqueHeight  8
1519 #define ScalesWidth  16
1520 #define ScalesHeight  16
1521 #define ShadowWidth  8
1522 #define ShadowHeight  8
1523 #define VerticalWidth  16
1524 #define VerticalHeight  16
1525 #define WavyWidth  16
1526 #define WavyHeight  16
1527 
1528 /*
1529   Constant declaration.
1530 */
1531 static const int
1532   RoiDelta = 8;
1533 
1534 static const unsigned char
1535   BricksBitmap[] =
1536   {
1537     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1538     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1539     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1540     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1541     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1542   },
1543   DiagonalBitmap[] =
1544   {
1545     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1546     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1547     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1548   },
1549   ScalesBitmap[] =
1550   {
1551     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1552     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1553     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1554   },
1555   VerticalBitmap[] =
1556   {
1557     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1558     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1559     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1560   },
1561   WavyBitmap[] =
1562   {
1563     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1564     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1565     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1566   };
1567 
1568 /*
1569   Function prototypes.
1570 */
1571 static CommandType
1572   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1573     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1574 
1575 static Image
1576   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1577     Image **,ExceptionInfo *),
1578   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1579   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1580     ExceptionInfo *),
1581   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1582     ExceptionInfo *);
1583 
1584 static MagickBooleanType
1585   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1586     ExceptionInfo *),
1587   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1588     ExceptionInfo *),
1589   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1590     ExceptionInfo *),
1591   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1592     ExceptionInfo *),
1593   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1594     ExceptionInfo *),
1595   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1596     ExceptionInfo *),
1597   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1599     ExceptionInfo *),
1600   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1601     ExceptionInfo *),
1602   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1604   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1605     ExceptionInfo *),
1606   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1607   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1608   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1609 
1610 static void
1611   XDrawPanRectangle(Display *,XWindows *),
1612   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1613     ExceptionInfo *),
1614   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1615   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1616   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1617   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1618     const KeySym,ExceptionInfo *),
1619   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1620   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1621   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1622 
1623 /*
1624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625 %                                                                             %
1626 %                                                                             %
1627 %                                                                             %
1628 %   D i s p l a y I m a g e s                                                 %
1629 %                                                                             %
1630 %                                                                             %
1631 %                                                                             %
1632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633 %
1634 %  DisplayImages() displays an image sequence to any X window screen.  It
1635 %  returns a value other than 0 if successful.  Check the exception member
1636 %  of image to determine the reason for any failure.
1637 %
1638 %  The format of the DisplayImages method is:
1639 %
1640 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1641 %        Image *images,ExceptionInfo *exception)
1642 %
1643 %  A description of each parameter follows:
1644 %
1645 %    o image_info: the image info.
1646 %
1647 %    o image: the image.
1648 %
1649 %    o exception: return any errors or warnings in this structure.
1650 %
1651 */
DisplayImages(const ImageInfo * image_info,Image * images,ExceptionInfo * exception)1652 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1653   Image *images,ExceptionInfo *exception)
1654 {
1655   char
1656     *argv[1];
1657 
1658   Display
1659     *display;
1660 
1661   Image
1662     *image;
1663 
1664   register ssize_t
1665     i;
1666 
1667   size_t
1668     state;
1669 
1670   XrmDatabase
1671     resource_database;
1672 
1673   XResourceInfo
1674     resource_info;
1675 
1676   assert(image_info != (const ImageInfo *) NULL);
1677   assert(image_info->signature == MagickCoreSignature);
1678   assert(images != (Image *) NULL);
1679   assert(images->signature == MagickCoreSignature);
1680   if (images->debug != MagickFalse )
1681     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1682   display=XOpenDisplay(image_info->server_name);
1683   if (display == (Display *) NULL)
1684     {
1685       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1686         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1687       return(MagickFalse);
1688     }
1689   if (exception->severity != UndefinedException)
1690     CatchException(exception);
1691   (void) XSetErrorHandler(XError);
1692   resource_database=XGetResourceDatabase(display,GetClientName());
1693   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1694   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1695   if (image_info->page != (char *) NULL)
1696     resource_info.image_geometry=AcquireString(image_info->page);
1697   resource_info.immutable=MagickTrue;
1698   argv[0]=AcquireString(GetClientName());
1699   state=DefaultState;
1700   for (i=0; (state & ExitState) == 0; i++)
1701   {
1702     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1703       break;
1704     image=GetImageFromList(images,i % GetImageListLength(images));
1705     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1706   }
1707   (void) SetErrorHandler((ErrorHandler) NULL);
1708   (void) SetWarningHandler((WarningHandler) NULL);
1709   argv[0]=DestroyString(argv[0]);
1710   (void) XCloseDisplay(display);
1711   XDestroyResourceInfo(&resource_info);
1712   if (exception->severity != UndefinedException)
1713     return(MagickFalse);
1714   return(MagickTrue);
1715 }
1716 
1717 /*
1718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1719 %                                                                             %
1720 %                                                                             %
1721 %                                                                             %
1722 %   R e m o t e D i s p l a y C o m m a n d                                   %
1723 %                                                                             %
1724 %                                                                             %
1725 %                                                                             %
1726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1727 %
1728 %  RemoteDisplayCommand() encourages a remote display program to display the
1729 %  specified image filename.
1730 %
1731 %  The format of the RemoteDisplayCommand method is:
1732 %
1733 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1734 %        const char *window,const char *filename,ExceptionInfo *exception)
1735 %
1736 %  A description of each parameter follows:
1737 %
1738 %    o image_info: the image info.
1739 %
1740 %    o window: Specifies the name or id of an X window.
1741 %
1742 %    o filename: the name of the image filename to display.
1743 %
1744 %    o exception: return any errors or warnings in this structure.
1745 %
1746 */
RemoteDisplayCommand(const ImageInfo * image_info,const char * window,const char * filename,ExceptionInfo * exception)1747 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1748   const char *window,const char *filename,ExceptionInfo *exception)
1749 {
1750   Display
1751     *display;
1752 
1753   MagickStatusType
1754     status;
1755 
1756   assert(image_info != (const ImageInfo *) NULL);
1757   assert(image_info->signature == MagickCoreSignature);
1758   assert(filename != (char *) NULL);
1759   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1760   display=XOpenDisplay(image_info->server_name);
1761   if (display == (Display *) NULL)
1762     {
1763       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1764         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1765       return(MagickFalse);
1766     }
1767   (void) XSetErrorHandler(XError);
1768   status=XRemoteCommand(display,window,filename);
1769   (void) XCloseDisplay(display);
1770   return(status != 0 ? MagickTrue : MagickFalse);
1771 }
1772 
1773 /*
1774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1775 %                                                                             %
1776 %                                                                             %
1777 %                                                                             %
1778 +   X A n n o t a t e E d i t I m a g e                                       %
1779 %                                                                             %
1780 %                                                                             %
1781 %                                                                             %
1782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1783 %
1784 %  XAnnotateEditImage() annotates the image with text.
1785 %
1786 %  The format of the XAnnotateEditImage method is:
1787 %
1788 %      MagickBooleanType XAnnotateEditImage(Display *display,
1789 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1790 %        ExceptionInfo *exception)
1791 %
1792 %  A description of each parameter follows:
1793 %
1794 %    o display: Specifies a connection to an X server;  returned from
1795 %      XOpenDisplay.
1796 %
1797 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1798 %
1799 %    o windows: Specifies a pointer to a XWindows structure.
1800 %
1801 %    o image: the image; returned from ReadImage.
1802 %
1803 */
1804 
XAnnotateEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)1805 static MagickBooleanType XAnnotateEditImage(Display *display,
1806   XResourceInfo *resource_info,XWindows *windows,Image *image,
1807   ExceptionInfo *exception)
1808 {
1809   static const char
1810     *AnnotateMenu[] =
1811     {
1812       "Font Name",
1813       "Font Color",
1814       "Box Color",
1815       "Rotate Text",
1816       "Help",
1817       "Dismiss",
1818       (char *) NULL
1819     },
1820     *TextMenu[] =
1821     {
1822       "Help",
1823       "Apply",
1824       (char *) NULL
1825     };
1826 
1827   static const ModeType
1828     AnnotateCommands[] =
1829     {
1830       AnnotateNameCommand,
1831       AnnotateFontColorCommand,
1832       AnnotateBackgroundColorCommand,
1833       AnnotateRotateCommand,
1834       AnnotateHelpCommand,
1835       AnnotateDismissCommand
1836     },
1837     TextCommands[] =
1838     {
1839       TextHelpCommand,
1840       TextApplyCommand
1841     };
1842 
1843   static MagickBooleanType
1844     transparent_box = MagickTrue,
1845     transparent_pen = MagickFalse;
1846 
1847   static double
1848     degrees = 0.0;
1849 
1850   static unsigned int
1851     box_id = MaxNumberPens-2,
1852     font_id = 0,
1853     pen_id = 0;
1854 
1855   char
1856     command[MagickPathExtent],
1857     text[MagickPathExtent];
1858 
1859   const char
1860     *ColorMenu[MaxNumberPens+1];
1861 
1862   Cursor
1863     cursor;
1864 
1865   GC
1866     annotate_context;
1867 
1868   int
1869     id,
1870     pen_number,
1871     status,
1872     x,
1873     y;
1874 
1875   KeySym
1876     key_symbol;
1877 
1878   register char
1879     *p;
1880 
1881   register ssize_t
1882     i;
1883 
1884   unsigned int
1885     height,
1886     width;
1887 
1888   size_t
1889     state;
1890 
1891   XAnnotateInfo
1892     *annotate_info,
1893     *previous_info;
1894 
1895   XColor
1896     color;
1897 
1898   XFontStruct
1899     *font_info;
1900 
1901   XEvent
1902     event,
1903     text_event;
1904 
1905   /*
1906     Map Command widget.
1907   */
1908   (void) CloneString(&windows->command.name,"Annotate");
1909   windows->command.data=4;
1910   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1911   (void) XMapRaised(display,windows->command.id);
1912   XClientMessage(display,windows->image.id,windows->im_protocols,
1913     windows->im_update_widget,CurrentTime);
1914   /*
1915     Track pointer until button 1 is pressed.
1916   */
1917   XQueryPosition(display,windows->image.id,&x,&y);
1918   (void) XSelectInput(display,windows->image.id,
1919     windows->image.attributes.event_mask | PointerMotionMask);
1920   cursor=XCreateFontCursor(display,XC_left_side);
1921   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1922   state=DefaultState;
1923   do
1924   {
1925     if (windows->info.mapped != MagickFalse )
1926       {
1927         /*
1928           Display pointer position.
1929         */
1930         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1931           x+windows->image.x,y+windows->image.y);
1932         XInfoWidget(display,windows,text);
1933       }
1934     /*
1935       Wait for next event.
1936     */
1937     XScreenEvent(display,windows,&event,exception);
1938     if (event.xany.window == windows->command.id)
1939       {
1940         /*
1941           Select a command from the Command widget.
1942         */
1943         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1944         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1945         if (id < 0)
1946           continue;
1947         switch (AnnotateCommands[id])
1948         {
1949           case AnnotateNameCommand:
1950           {
1951             const char
1952               *FontMenu[MaxNumberFonts];
1953 
1954             int
1955               font_number;
1956 
1957             /*
1958               Initialize menu selections.
1959             */
1960             for (i=0; i < MaxNumberFonts; i++)
1961               FontMenu[i]=resource_info->font_name[i];
1962             FontMenu[MaxNumberFonts-2]="Browser...";
1963             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1964             /*
1965               Select a font name from the pop-up menu.
1966             */
1967             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1968               (const char **) FontMenu,command);
1969             if (font_number < 0)
1970               break;
1971             if (font_number == (MaxNumberFonts-2))
1972               {
1973                 static char
1974                   font_name[MagickPathExtent] = "fixed";
1975 
1976                 /*
1977                   Select a font name from a browser.
1978                 */
1979                 resource_info->font_name[font_number]=font_name;
1980                 XFontBrowserWidget(display,windows,"Select",font_name);
1981                 if (*font_name == '\0')
1982                   break;
1983               }
1984             /*
1985               Initialize font info.
1986             */
1987             font_info=XLoadQueryFont(display,resource_info->font_name[
1988               font_number]);
1989             if (font_info == (XFontStruct *) NULL)
1990               {
1991                 XNoticeWidget(display,windows,"Unable to load font:",
1992                   resource_info->font_name[font_number]);
1993                 break;
1994               }
1995             font_id=(unsigned int) font_number;
1996             (void) XFreeFont(display,font_info);
1997             break;
1998           }
1999           case AnnotateFontColorCommand:
2000           {
2001             /*
2002               Initialize menu selections.
2003             */
2004             for (i=0; i < (int) (MaxNumberPens-2); i++)
2005               ColorMenu[i]=resource_info->pen_colors[i];
2006             ColorMenu[MaxNumberPens-2]="transparent";
2007             ColorMenu[MaxNumberPens-1]="Browser...";
2008             ColorMenu[MaxNumberPens]=(const char *) NULL;
2009             /*
2010               Select a pen color from the pop-up menu.
2011             */
2012             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2013               (const char **) ColorMenu,command);
2014             if (pen_number < 0)
2015               break;
2016             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2017               MagickFalse;
2018             if (transparent_pen != MagickFalse )
2019               break;
2020             if (pen_number == (MaxNumberPens-1))
2021               {
2022                 static char
2023                   color_name[MagickPathExtent] = "gray";
2024 
2025                 /*
2026                   Select a pen color from a dialog.
2027                 */
2028                 resource_info->pen_colors[pen_number]=color_name;
2029                 XColorBrowserWidget(display,windows,"Select",color_name);
2030                 if (*color_name == '\0')
2031                   break;
2032               }
2033             /*
2034               Set pen color.
2035             */
2036             (void) XParseColor(display,windows->map_info->colormap,
2037               resource_info->pen_colors[pen_number],&color);
2038             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2039               (unsigned int) MaxColors,&color);
2040             windows->pixel_info->pen_colors[pen_number]=color;
2041             pen_id=(unsigned int) pen_number;
2042             break;
2043           }
2044           case AnnotateBackgroundColorCommand:
2045           {
2046             /*
2047               Initialize menu selections.
2048             */
2049             for (i=0; i < (int) (MaxNumberPens-2); i++)
2050               ColorMenu[i]=resource_info->pen_colors[i];
2051             ColorMenu[MaxNumberPens-2]="transparent";
2052             ColorMenu[MaxNumberPens-1]="Browser...";
2053             ColorMenu[MaxNumberPens]=(const char *) NULL;
2054             /*
2055               Select a pen color from the pop-up menu.
2056             */
2057             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2058               (const char **) ColorMenu,command);
2059             if (pen_number < 0)
2060               break;
2061             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2062               MagickFalse;
2063             if (transparent_box != MagickFalse )
2064               break;
2065             if (pen_number == (MaxNumberPens-1))
2066               {
2067                 static char
2068                   color_name[MagickPathExtent] = "gray";
2069 
2070                 /*
2071                   Select a pen color from a dialog.
2072                 */
2073                 resource_info->pen_colors[pen_number]=color_name;
2074                 XColorBrowserWidget(display,windows,"Select",color_name);
2075                 if (*color_name == '\0')
2076                   break;
2077               }
2078             /*
2079               Set pen color.
2080             */
2081             (void) XParseColor(display,windows->map_info->colormap,
2082               resource_info->pen_colors[pen_number],&color);
2083             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2084               (unsigned int) MaxColors,&color);
2085             windows->pixel_info->pen_colors[pen_number]=color;
2086             box_id=(unsigned int) pen_number;
2087             break;
2088           }
2089           case AnnotateRotateCommand:
2090           {
2091             int
2092               entry;
2093 
2094             static char
2095               angle[MagickPathExtent] = "30.0";
2096 
2097             static const char
2098               *RotateMenu[] =
2099               {
2100                 "-90",
2101                 "-45",
2102                 "-30",
2103                 "0",
2104                 "30",
2105                 "45",
2106                 "90",
2107                 "180",
2108                 "Dialog...",
2109                 (char *) NULL,
2110               };
2111 
2112             /*
2113               Select a command from the pop-up menu.
2114             */
2115             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2116               command);
2117             if (entry < 0)
2118               break;
2119             if (entry != 8)
2120               {
2121                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2122                 break;
2123               }
2124             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2125               angle);
2126             if (*angle == '\0')
2127               break;
2128             degrees=StringToDouble(angle,(char **) NULL);
2129             break;
2130           }
2131           case AnnotateHelpCommand:
2132           {
2133             XTextViewWidget(display,resource_info,windows,MagickFalse,
2134               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2135             break;
2136           }
2137           case AnnotateDismissCommand:
2138           {
2139             /*
2140               Prematurely exit.
2141             */
2142             state|=EscapeState;
2143             state|=ExitState;
2144             break;
2145           }
2146           default:
2147             break;
2148         }
2149         continue;
2150       }
2151     switch (event.type)
2152     {
2153       case ButtonPress:
2154       {
2155         if (event.xbutton.button != Button1)
2156           break;
2157         if (event.xbutton.window != windows->image.id)
2158           break;
2159         /*
2160           Change to text entering mode.
2161         */
2162         x=event.xbutton.x;
2163         y=event.xbutton.y;
2164         state|=ExitState;
2165         break;
2166       }
2167       case ButtonRelease:
2168         break;
2169       case Expose:
2170         break;
2171       case KeyPress:
2172       {
2173         if (event.xkey.window != windows->image.id)
2174           break;
2175         /*
2176           Respond to a user key press.
2177         */
2178         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2179           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2180         switch ((int) key_symbol)
2181         {
2182           case XK_Escape:
2183           case XK_F20:
2184           {
2185             /*
2186               Prematurely exit.
2187             */
2188             state|=EscapeState;
2189             state|=ExitState;
2190             break;
2191           }
2192           case XK_F1:
2193           case XK_Help:
2194           {
2195             XTextViewWidget(display,resource_info,windows,MagickFalse,
2196               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2197             break;
2198           }
2199           default:
2200           {
2201             (void) XBell(display,0);
2202             break;
2203           }
2204         }
2205         break;
2206       }
2207       case MotionNotify:
2208       {
2209         /*
2210           Map and unmap Info widget as cursor crosses its boundaries.
2211         */
2212         x=event.xmotion.x;
2213         y=event.xmotion.y;
2214         if (windows->info.mapped != MagickFalse )
2215           {
2216             if ((x < (int) (windows->info.x+windows->info.width)) &&
2217                 (y < (int) (windows->info.y+windows->info.height)))
2218               (void) XWithdrawWindow(display,windows->info.id,
2219                 windows->info.screen);
2220           }
2221         else
2222           if ((x > (int) (windows->info.x+windows->info.width)) ||
2223               (y > (int) (windows->info.y+windows->info.height)))
2224             (void) XMapWindow(display,windows->info.id);
2225         break;
2226       }
2227       default:
2228         break;
2229     }
2230   } while ((state & ExitState) == 0);
2231   (void) XSelectInput(display,windows->image.id,
2232     windows->image.attributes.event_mask);
2233   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2234   if ((state & EscapeState) != 0)
2235     return(MagickTrue);
2236   /*
2237     Set font info and check boundary conditions.
2238   */
2239   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2240   if (font_info == (XFontStruct *) NULL)
2241     {
2242       XNoticeWidget(display,windows,"Unable to load font:",
2243         resource_info->font_name[font_id]);
2244       font_info=windows->font_info;
2245     }
2246   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2247     x=(int) windows->image.width-font_info->max_bounds.width;
2248   if (y < (int) (font_info->ascent+font_info->descent))
2249     y=(int) font_info->ascent+font_info->descent;
2250   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2251       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2252     return(MagickFalse);
2253   /*
2254     Initialize annotate structure.
2255   */
2256   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2257   if (annotate_info == (XAnnotateInfo *) NULL)
2258     return(MagickFalse);
2259   XGetAnnotateInfo(annotate_info);
2260   annotate_info->x=x;
2261   annotate_info->y=y;
2262   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2263     annotate_info->stencil=OpaqueStencil;
2264   else
2265     if (transparent_box == MagickFalse)
2266       annotate_info->stencil=BackgroundStencil;
2267     else
2268       annotate_info->stencil=ForegroundStencil;
2269   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2270   annotate_info->degrees=degrees;
2271   annotate_info->font_info=font_info;
2272   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2273     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2274     sizeof(*annotate_info->text));
2275   if (annotate_info->text == (char *) NULL)
2276     return(MagickFalse);
2277   /*
2278     Create cursor and set graphic context.
2279   */
2280   cursor=XCreateFontCursor(display,XC_pencil);
2281   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2282   annotate_context=windows->image.annotate_context;
2283   (void) XSetFont(display,annotate_context,font_info->fid);
2284   (void) XSetBackground(display,annotate_context,
2285     windows->pixel_info->pen_colors[box_id].pixel);
2286   (void) XSetForeground(display,annotate_context,
2287     windows->pixel_info->pen_colors[pen_id].pixel);
2288   /*
2289     Begin annotating the image with text.
2290   */
2291   (void) CloneString(&windows->command.name,"Text");
2292   windows->command.data=0;
2293   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2294   state=DefaultState;
2295   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2296   text_event.xexpose.width=(int) font_info->max_bounds.width;
2297   text_event.xexpose.height=font_info->max_bounds.ascent+
2298     font_info->max_bounds.descent;
2299   p=annotate_info->text;
2300   do
2301   {
2302     /*
2303       Display text cursor.
2304     */
2305     *p='\0';
2306     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2307     /*
2308       Wait for next event.
2309     */
2310     XScreenEvent(display,windows,&event,exception);
2311     if (event.xany.window == windows->command.id)
2312       {
2313         /*
2314           Select a command from the Command widget.
2315         */
2316         (void) XSetBackground(display,annotate_context,
2317           windows->pixel_info->background_color.pixel);
2318         (void) XSetForeground(display,annotate_context,
2319           windows->pixel_info->foreground_color.pixel);
2320         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2321         (void) XSetBackground(display,annotate_context,
2322           windows->pixel_info->pen_colors[box_id].pixel);
2323         (void) XSetForeground(display,annotate_context,
2324           windows->pixel_info->pen_colors[pen_id].pixel);
2325         if (id < 0)
2326           continue;
2327         switch (TextCommands[id])
2328         {
2329           case TextHelpCommand:
2330           {
2331             XTextViewWidget(display,resource_info,windows,MagickFalse,
2332               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2333             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2334             break;
2335           }
2336           case TextApplyCommand:
2337           {
2338             /*
2339               Finished annotating.
2340             */
2341             annotate_info->width=(unsigned int) XTextWidth(font_info,
2342               annotate_info->text,(int) strlen(annotate_info->text));
2343             XRefreshWindow(display,&windows->image,&text_event);
2344             state|=ExitState;
2345             break;
2346           }
2347           default:
2348             break;
2349         }
2350         continue;
2351       }
2352     /*
2353       Erase text cursor.
2354     */
2355     text_event.xexpose.x=x;
2356     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2357     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2358       (unsigned int) text_event.xexpose.width,(unsigned int)
2359       text_event.xexpose.height,MagickFalse);
2360     XRefreshWindow(display,&windows->image,&text_event);
2361     switch (event.type)
2362     {
2363       case ButtonPress:
2364       {
2365         if (event.xbutton.window != windows->image.id)
2366           break;
2367         if (event.xbutton.button == Button2)
2368           {
2369             /*
2370               Request primary selection.
2371             */
2372             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2373               windows->image.id,CurrentTime);
2374             break;
2375           }
2376         break;
2377       }
2378       case Expose:
2379       {
2380         if (event.xexpose.count == 0)
2381           {
2382             XAnnotateInfo
2383               *text_info;
2384 
2385             /*
2386               Refresh Image window.
2387             */
2388             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2389             text_info=annotate_info;
2390             while (text_info != (XAnnotateInfo *) NULL)
2391             {
2392               if (annotate_info->stencil == ForegroundStencil)
2393                 (void) XDrawString(display,windows->image.id,annotate_context,
2394                   text_info->x,text_info->y,text_info->text,
2395                   (int) strlen(text_info->text));
2396               else
2397                 (void) XDrawImageString(display,windows->image.id,
2398                   annotate_context,text_info->x,text_info->y,text_info->text,
2399                   (int) strlen(text_info->text));
2400               text_info=text_info->previous;
2401             }
2402             (void) XDrawString(display,windows->image.id,annotate_context,
2403               x,y,"_",1);
2404           }
2405         break;
2406       }
2407       case KeyPress:
2408       {
2409         int
2410           length;
2411 
2412         if (event.xkey.window != windows->image.id)
2413           break;
2414         /*
2415           Respond to a user key press.
2416         */
2417         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2418           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2419         *(command+length)='\0';
2420         if (((event.xkey.state & ControlMask) != 0) ||
2421             ((event.xkey.state & Mod1Mask) != 0))
2422           state|=ModifierState;
2423         if ((state & ModifierState) != 0)
2424           switch ((int) key_symbol)
2425           {
2426             case XK_u:
2427             case XK_U:
2428             {
2429               key_symbol=DeleteCommand;
2430               break;
2431             }
2432             default:
2433               break;
2434           }
2435         switch ((int) key_symbol)
2436         {
2437           case XK_BackSpace:
2438           {
2439             /*
2440               Erase one character.
2441             */
2442             if (p == annotate_info->text)
2443               {
2444                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2445                   break;
2446                 else
2447                   {
2448                     /*
2449                       Go to end of the previous line of text.
2450                     */
2451                     annotate_info=annotate_info->previous;
2452                     p=annotate_info->text;
2453                     x=annotate_info->x+annotate_info->width;
2454                     y=annotate_info->y;
2455                     if (annotate_info->width != 0)
2456                       p+=strlen(annotate_info->text);
2457                     break;
2458                   }
2459               }
2460             p--;
2461             x-=XTextWidth(font_info,p,1);
2462             text_event.xexpose.x=x;
2463             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2464             XRefreshWindow(display,&windows->image,&text_event);
2465             break;
2466           }
2467           case XK_bracketleft:
2468           {
2469             key_symbol=XK_Escape;
2470             break;
2471           }
2472           case DeleteCommand:
2473           {
2474             /*
2475               Erase the entire line of text.
2476             */
2477             while (p != annotate_info->text)
2478             {
2479               p--;
2480               x-=XTextWidth(font_info,p,1);
2481               text_event.xexpose.x=x;
2482               XRefreshWindow(display,&windows->image,&text_event);
2483             }
2484             break;
2485           }
2486           case XK_Escape:
2487           case XK_F20:
2488           {
2489             /*
2490               Finished annotating.
2491             */
2492             annotate_info->width=(unsigned int) XTextWidth(font_info,
2493               annotate_info->text,(int) strlen(annotate_info->text));
2494             XRefreshWindow(display,&windows->image,&text_event);
2495             state|=ExitState;
2496             break;
2497           }
2498           default:
2499           {
2500             /*
2501               Draw a single character on the Image window.
2502             */
2503             if ((state & ModifierState) != 0)
2504               break;
2505             if (*command == '\0')
2506               break;
2507             *p=(*command);
2508             if (annotate_info->stencil == ForegroundStencil)
2509               (void) XDrawString(display,windows->image.id,annotate_context,
2510                 x,y,p,1);
2511             else
2512               (void) XDrawImageString(display,windows->image.id,
2513                 annotate_context,x,y,p,1);
2514             x+=XTextWidth(font_info,p,1);
2515             p++;
2516             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2517               break;
2518           }
2519           case XK_Return:
2520           case XK_KP_Enter:
2521           {
2522             /*
2523               Advance to the next line of text.
2524             */
2525             *p='\0';
2526             annotate_info->width=(unsigned int) XTextWidth(font_info,
2527               annotate_info->text,(int) strlen(annotate_info->text));
2528             if (annotate_info->next != (XAnnotateInfo *) NULL)
2529               {
2530                 /*
2531                   Line of text already exists.
2532                 */
2533                 annotate_info=annotate_info->next;
2534                 x=annotate_info->x;
2535                 y=annotate_info->y;
2536                 p=annotate_info->text;
2537                 break;
2538               }
2539             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2540               sizeof(*annotate_info->next));
2541             if (annotate_info->next == (XAnnotateInfo *) NULL)
2542               return(MagickFalse);
2543             *annotate_info->next=(*annotate_info);
2544             annotate_info->next->previous=annotate_info;
2545             annotate_info=annotate_info->next;
2546             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2547               windows->image.width/MagickMax((ssize_t)
2548               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2549             if (annotate_info->text == (char *) NULL)
2550               return(MagickFalse);
2551             annotate_info->y+=annotate_info->height;
2552             if (annotate_info->y > (int) windows->image.height)
2553               annotate_info->y=(int) annotate_info->height;
2554             annotate_info->next=(XAnnotateInfo *) NULL;
2555             x=annotate_info->x;
2556             y=annotate_info->y;
2557             p=annotate_info->text;
2558             break;
2559           }
2560         }
2561         break;
2562       }
2563       case KeyRelease:
2564       {
2565         /*
2566           Respond to a user key release.
2567         */
2568         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2569           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2570         state&=(~ModifierState);
2571         break;
2572       }
2573       case SelectionNotify:
2574       {
2575         Atom
2576           type;
2577 
2578         int
2579           format;
2580 
2581         unsigned char
2582           *data;
2583 
2584         unsigned long
2585           after,
2586           length;
2587 
2588         /*
2589           Obtain response from primary selection.
2590         */
2591         if (event.xselection.property == (Atom) None)
2592           break;
2593         status=XGetWindowProperty(display,event.xselection.requestor,
2594           event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2595           &type,&format,&length,&after,&data);
2596         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2597             (length == 0))
2598           break;
2599         /*
2600           Annotate Image window with primary selection.
2601         */
2602         for (i=0; i < (ssize_t) length; i++)
2603         {
2604           if ((char) data[i] != '\n')
2605             {
2606               /*
2607                 Draw a single character on the Image window.
2608               */
2609               *p=(char) data[i];
2610               (void) XDrawString(display,windows->image.id,annotate_context,
2611                 x,y,p,1);
2612               x+=XTextWidth(font_info,p,1);
2613               p++;
2614               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2615                 continue;
2616             }
2617           /*
2618             Advance to the next line of text.
2619           */
2620           *p='\0';
2621           annotate_info->width=(unsigned int) XTextWidth(font_info,
2622             annotate_info->text,(int) strlen(annotate_info->text));
2623           if (annotate_info->next != (XAnnotateInfo *) NULL)
2624             {
2625               /*
2626                 Line of text already exists.
2627               */
2628               annotate_info=annotate_info->next;
2629               x=annotate_info->x;
2630               y=annotate_info->y;
2631               p=annotate_info->text;
2632               continue;
2633             }
2634           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2635             sizeof(*annotate_info->next));
2636           if (annotate_info->next == (XAnnotateInfo *) NULL)
2637             return(MagickFalse);
2638           *annotate_info->next=(*annotate_info);
2639           annotate_info->next->previous=annotate_info;
2640           annotate_info=annotate_info->next;
2641           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2642             windows->image.width/MagickMax((ssize_t)
2643             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2644           if (annotate_info->text == (char *) NULL)
2645             return(MagickFalse);
2646           annotate_info->y+=annotate_info->height;
2647           if (annotate_info->y > (int) windows->image.height)
2648             annotate_info->y=(int) annotate_info->height;
2649           annotate_info->next=(XAnnotateInfo *) NULL;
2650           x=annotate_info->x;
2651           y=annotate_info->y;
2652           p=annotate_info->text;
2653         }
2654         (void) XFree((void *) data);
2655         break;
2656       }
2657       default:
2658         break;
2659     }
2660   } while ((state & ExitState) == 0);
2661   (void) XFreeCursor(display,cursor);
2662   /*
2663     Annotation is relative to image configuration.
2664   */
2665   width=(unsigned int) image->columns;
2666   height=(unsigned int) image->rows;
2667   x=0;
2668   y=0;
2669   if (windows->image.crop_geometry != (char *) NULL)
2670     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2671   /*
2672     Initialize annotated image.
2673   */
2674   XSetCursorState(display,windows,MagickTrue);
2675   XCheckRefreshWindows(display,windows);
2676   while (annotate_info != (XAnnotateInfo *) NULL)
2677   {
2678     if (annotate_info->width == 0)
2679       {
2680         /*
2681           No text on this line--  go to the next line of text.
2682         */
2683         previous_info=annotate_info->previous;
2684         annotate_info->text=(char *)
2685           RelinquishMagickMemory(annotate_info->text);
2686         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2687         annotate_info=previous_info;
2688         continue;
2689       }
2690     /*
2691       Determine pixel index for box and pen color.
2692     */
2693     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2694     if (windows->pixel_info->colors != 0)
2695       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2696         if (windows->pixel_info->pixels[i] ==
2697             windows->pixel_info->pen_colors[box_id].pixel)
2698           {
2699             windows->pixel_info->box_index=(unsigned short) i;
2700             break;
2701           }
2702     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2703     if (windows->pixel_info->colors != 0)
2704       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2705         if (windows->pixel_info->pixels[i] ==
2706             windows->pixel_info->pen_colors[pen_id].pixel)
2707           {
2708             windows->pixel_info->pen_index=(unsigned short) i;
2709             break;
2710           }
2711     /*
2712       Define the annotate geometry string.
2713     */
2714     annotate_info->x=(int)
2715       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2716     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2717       windows->image.y)/windows->image.ximage->height;
2718     (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2719       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2720       height*annotate_info->height/windows->image.ximage->height,
2721       annotate_info->x+x,annotate_info->y+y);
2722     /*
2723       Annotate image with text.
2724     */
2725     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2726       exception);
2727     if (status == 0)
2728       return(MagickFalse);
2729     /*
2730       Free up memory.
2731     */
2732     previous_info=annotate_info->previous;
2733     annotate_info->text=DestroyString(annotate_info->text);
2734     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2735     annotate_info=previous_info;
2736   }
2737   (void) XSetForeground(display,annotate_context,
2738     windows->pixel_info->foreground_color.pixel);
2739   (void) XSetBackground(display,annotate_context,
2740     windows->pixel_info->background_color.pixel);
2741   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2742   XSetCursorState(display,windows,MagickFalse);
2743   (void) XFreeFont(display,font_info);
2744   /*
2745     Update image configuration.
2746   */
2747   XConfigureImageColormap(display,resource_info,windows,image,exception);
2748   (void) XConfigureImage(display,resource_info,windows,image,exception);
2749   return(MagickTrue);
2750 }
2751 
2752 /*
2753 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2754 %                                                                             %
2755 %                                                                             %
2756 %                                                                             %
2757 +   X B a c k g r o u n d I m a g e                                           %
2758 %                                                                             %
2759 %                                                                             %
2760 %                                                                             %
2761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2762 %
2763 %  XBackgroundImage() displays the image in the background of a window.
2764 %
2765 %  The format of the XBackgroundImage method is:
2766 %
2767 %      MagickBooleanType XBackgroundImage(Display *display,
2768 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2769 %        ExceptionInfo *exception)
2770 %
2771 %  A description of each parameter follows:
2772 %
2773 %    o display: Specifies a connection to an X server; returned from
2774 %      XOpenDisplay.
2775 %
2776 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2777 %
2778 %    o windows: Specifies a pointer to a XWindows structure.
2779 %
2780 %    o image: the image.
2781 %
2782 %    o exception: return any errors or warnings in this structure.
2783 %
2784 */
XBackgroundImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)2785 static MagickBooleanType XBackgroundImage(Display *display,
2786   XResourceInfo *resource_info,XWindows *windows,Image **image,
2787   ExceptionInfo *exception)
2788 {
2789 #define BackgroundImageTag  "Background/Image"
2790 
2791   int
2792     status;
2793 
2794   static char
2795     window_id[MagickPathExtent] = "root";
2796 
2797   XResourceInfo
2798     background_resources;
2799 
2800   /*
2801     Put image in background.
2802   */
2803   status=XDialogWidget(display,windows,"Background",
2804     "Enter window id (id 0x00 selects window with pointer):",window_id);
2805   if (*window_id == '\0')
2806     return(MagickFalse);
2807   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2808     exception);
2809   XInfoWidget(display,windows,BackgroundImageTag);
2810   XSetCursorState(display,windows,MagickTrue);
2811   XCheckRefreshWindows(display,windows);
2812   background_resources=(*resource_info);
2813   background_resources.window_id=window_id;
2814   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2815   status=XDisplayBackgroundImage(display,&background_resources,*image,
2816     exception);
2817   if (status != MagickFalse)
2818     XClientMessage(display,windows->image.id,windows->im_protocols,
2819       windows->im_retain_colors,CurrentTime);
2820   XSetCursorState(display,windows,MagickFalse);
2821   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2822     exception);
2823   return(MagickTrue);
2824 }
2825 
2826 /*
2827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2828 %                                                                             %
2829 %                                                                             %
2830 %                                                                             %
2831 +   X C h o p I m a g e                                                       %
2832 %                                                                             %
2833 %                                                                             %
2834 %                                                                             %
2835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2836 %
2837 %  XChopImage() chops the X image.
2838 %
2839 %  The format of the XChopImage method is:
2840 %
2841 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2842 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2843 %
2844 %  A description of each parameter follows:
2845 %
2846 %    o display: Specifies a connection to an X server; returned from
2847 %      XOpenDisplay.
2848 %
2849 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2850 %
2851 %    o windows: Specifies a pointer to a XWindows structure.
2852 %
2853 %    o image: the image.
2854 %
2855 %    o exception: return any errors or warnings in this structure.
2856 %
2857 */
XChopImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)2858 static MagickBooleanType XChopImage(Display *display,
2859   XResourceInfo *resource_info,XWindows *windows,Image **image,
2860   ExceptionInfo *exception)
2861 {
2862   static const char
2863     *ChopMenu[] =
2864     {
2865       "Direction",
2866       "Help",
2867       "Dismiss",
2868       (char *) NULL
2869     };
2870 
2871   static ModeType
2872     direction = HorizontalChopCommand;
2873 
2874   static const ModeType
2875     ChopCommands[] =
2876     {
2877       ChopDirectionCommand,
2878       ChopHelpCommand,
2879       ChopDismissCommand
2880     },
2881     DirectionCommands[] =
2882     {
2883       HorizontalChopCommand,
2884       VerticalChopCommand
2885     };
2886 
2887   char
2888     text[MagickPathExtent];
2889 
2890   Image
2891     *chop_image;
2892 
2893   int
2894     id,
2895     x,
2896     y;
2897 
2898   double
2899     scale_factor;
2900 
2901   RectangleInfo
2902     chop_info;
2903 
2904   unsigned int
2905     distance,
2906     height,
2907     width;
2908 
2909   size_t
2910     state;
2911 
2912   XEvent
2913     event;
2914 
2915   XSegment
2916     segment_info;
2917 
2918   /*
2919     Map Command widget.
2920   */
2921   (void) CloneString(&windows->command.name,"Chop");
2922   windows->command.data=1;
2923   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2924   (void) XMapRaised(display,windows->command.id);
2925   XClientMessage(display,windows->image.id,windows->im_protocols,
2926     windows->im_update_widget,CurrentTime);
2927   /*
2928     Track pointer until button 1 is pressed.
2929   */
2930   XQueryPosition(display,windows->image.id,&x,&y);
2931   (void) XSelectInput(display,windows->image.id,
2932     windows->image.attributes.event_mask | PointerMotionMask);
2933   state=DefaultState;
2934   (void) ResetMagickMemory(&segment_info,0,sizeof(segment_info));
2935   do
2936   {
2937     if (windows->info.mapped != MagickFalse )
2938       {
2939         /*
2940           Display pointer position.
2941         */
2942         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2943           x+windows->image.x,y+windows->image.y);
2944         XInfoWidget(display,windows,text);
2945       }
2946     /*
2947       Wait for next event.
2948     */
2949     XScreenEvent(display,windows,&event,exception);
2950     if (event.xany.window == windows->command.id)
2951       {
2952         /*
2953           Select a command from the Command widget.
2954         */
2955         id=XCommandWidget(display,windows,ChopMenu,&event);
2956         if (id < 0)
2957           continue;
2958         switch (ChopCommands[id])
2959         {
2960           case ChopDirectionCommand:
2961           {
2962             char
2963               command[MagickPathExtent];
2964 
2965             static const char
2966               *Directions[] =
2967               {
2968                 "horizontal",
2969                 "vertical",
2970                 (char *) NULL,
2971               };
2972 
2973             /*
2974               Select a command from the pop-up menu.
2975             */
2976             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2977             if (id >= 0)
2978               direction=DirectionCommands[id];
2979             break;
2980           }
2981           case ChopHelpCommand:
2982           {
2983             XTextViewWidget(display,resource_info,windows,MagickFalse,
2984               "Help Viewer - Image Chop",ImageChopHelp);
2985             break;
2986           }
2987           case ChopDismissCommand:
2988           {
2989             /*
2990               Prematurely exit.
2991             */
2992             state|=EscapeState;
2993             state|=ExitState;
2994             break;
2995           }
2996           default:
2997             break;
2998         }
2999         continue;
3000       }
3001     switch (event.type)
3002     {
3003       case ButtonPress:
3004       {
3005         if (event.xbutton.button != Button1)
3006           break;
3007         if (event.xbutton.window != windows->image.id)
3008           break;
3009         /*
3010           User has committed to start point of chopping line.
3011         */
3012         segment_info.x1=(short int) event.xbutton.x;
3013         segment_info.x2=(short int) event.xbutton.x;
3014         segment_info.y1=(short int) event.xbutton.y;
3015         segment_info.y2=(short int) event.xbutton.y;
3016         state|=ExitState;
3017         break;
3018       }
3019       case ButtonRelease:
3020         break;
3021       case Expose:
3022         break;
3023       case KeyPress:
3024       {
3025         char
3026           command[MagickPathExtent];
3027 
3028         KeySym
3029           key_symbol;
3030 
3031         if (event.xkey.window != windows->image.id)
3032           break;
3033         /*
3034           Respond to a user key press.
3035         */
3036         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3037           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3038         switch ((int) key_symbol)
3039         {
3040           case XK_Escape:
3041           case XK_F20:
3042           {
3043             /*
3044               Prematurely exit.
3045             */
3046             state|=EscapeState;
3047             state|=ExitState;
3048             break;
3049           }
3050           case XK_F1:
3051           case XK_Help:
3052           {
3053             (void) XSetFunction(display,windows->image.highlight_context,
3054               GXcopy);
3055             XTextViewWidget(display,resource_info,windows,MagickFalse,
3056               "Help Viewer - Image Chop",ImageChopHelp);
3057             (void) XSetFunction(display,windows->image.highlight_context,
3058               GXinvert);
3059             break;
3060           }
3061           default:
3062           {
3063             (void) XBell(display,0);
3064             break;
3065           }
3066         }
3067         break;
3068       }
3069       case MotionNotify:
3070       {
3071         /*
3072           Map and unmap Info widget as text cursor crosses its boundaries.
3073         */
3074         x=event.xmotion.x;
3075         y=event.xmotion.y;
3076         if (windows->info.mapped != MagickFalse )
3077           {
3078             if ((x < (int) (windows->info.x+windows->info.width)) &&
3079                 (y < (int) (windows->info.y+windows->info.height)))
3080               (void) XWithdrawWindow(display,windows->info.id,
3081                 windows->info.screen);
3082           }
3083         else
3084           if ((x > (int) (windows->info.x+windows->info.width)) ||
3085               (y > (int) (windows->info.y+windows->info.height)))
3086             (void) XMapWindow(display,windows->info.id);
3087       }
3088     }
3089   } while ((state & ExitState) == 0);
3090   (void) XSelectInput(display,windows->image.id,
3091     windows->image.attributes.event_mask);
3092   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3093   if ((state & EscapeState) != 0)
3094     return(MagickTrue);
3095   /*
3096     Draw line as pointer moves until the mouse button is released.
3097   */
3098   chop_info.width=0;
3099   chop_info.height=0;
3100   chop_info.x=0;
3101   chop_info.y=0;
3102   distance=0;
3103   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3104   state=DefaultState;
3105   do
3106   {
3107     if (distance > 9)
3108       {
3109         /*
3110           Display info and draw chopping line.
3111         */
3112         if (windows->info.mapped == MagickFalse)
3113           (void) XMapWindow(display,windows->info.id);
3114         (void) FormatLocaleString(text,MagickPathExtent,
3115           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3116           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3117         XInfoWidget(display,windows,text);
3118         XHighlightLine(display,windows->image.id,
3119           windows->image.highlight_context,&segment_info);
3120       }
3121     else
3122       if (windows->info.mapped != MagickFalse )
3123         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3124     /*
3125       Wait for next event.
3126     */
3127     XScreenEvent(display,windows,&event,exception);
3128     if (distance > 9)
3129       XHighlightLine(display,windows->image.id,
3130         windows->image.highlight_context,&segment_info);
3131     switch (event.type)
3132     {
3133       case ButtonPress:
3134       {
3135         segment_info.x2=(short int) event.xmotion.x;
3136         segment_info.y2=(short int) event.xmotion.y;
3137         break;
3138       }
3139       case ButtonRelease:
3140       {
3141         /*
3142           User has committed to chopping line.
3143         */
3144         segment_info.x2=(short int) event.xbutton.x;
3145         segment_info.y2=(short int) event.xbutton.y;
3146         state|=ExitState;
3147         break;
3148       }
3149       case Expose:
3150         break;
3151       case MotionNotify:
3152       {
3153         segment_info.x2=(short int) event.xmotion.x;
3154         segment_info.y2=(short int) event.xmotion.y;
3155       }
3156       default:
3157         break;
3158     }
3159     /*
3160       Check boundary conditions.
3161     */
3162     if (segment_info.x2 < 0)
3163       segment_info.x2=0;
3164     else
3165       if (segment_info.x2 > windows->image.ximage->width)
3166         segment_info.x2=windows->image.ximage->width;
3167     if (segment_info.y2 < 0)
3168       segment_info.y2=0;
3169     else
3170       if (segment_info.y2 > windows->image.ximage->height)
3171         segment_info.y2=windows->image.ximage->height;
3172     distance=(unsigned int)
3173       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3174        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3175     /*
3176       Compute chopping geometry.
3177     */
3178     if (direction == HorizontalChopCommand)
3179       {
3180         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3181         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3182         chop_info.height=0;
3183         chop_info.y=0;
3184         if (segment_info.x1 > (int) segment_info.x2)
3185           {
3186             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3187             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3188           }
3189       }
3190     else
3191       {
3192         chop_info.width=0;
3193         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3194         chop_info.x=0;
3195         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3196         if (segment_info.y1 > segment_info.y2)
3197           {
3198             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3199             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3200           }
3201       }
3202   } while ((state & ExitState) == 0);
3203   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3204   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3205   if (distance <= 9)
3206     return(MagickTrue);
3207   /*
3208     Image chopping is relative to image configuration.
3209   */
3210   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3211     exception);
3212   XSetCursorState(display,windows,MagickTrue);
3213   XCheckRefreshWindows(display,windows);
3214   windows->image.window_changes.width=windows->image.ximage->width-
3215     (unsigned int) chop_info.width;
3216   windows->image.window_changes.height=windows->image.ximage->height-
3217     (unsigned int) chop_info.height;
3218   width=(unsigned int) (*image)->columns;
3219   height=(unsigned int) (*image)->rows;
3220   x=0;
3221   y=0;
3222   if (windows->image.crop_geometry != (char *) NULL)
3223     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3224   scale_factor=(double) width/windows->image.ximage->width;
3225   chop_info.x+=x;
3226   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3227   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3228   scale_factor=(double) height/windows->image.ximage->height;
3229   chop_info.y+=y;
3230   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3231   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3232   /*
3233     Chop image.
3234   */
3235   chop_image=ChopImage(*image,&chop_info,exception);
3236   XSetCursorState(display,windows,MagickFalse);
3237   if (chop_image == (Image *) NULL)
3238     return(MagickFalse);
3239   *image=DestroyImage(*image);
3240   *image=chop_image;
3241   /*
3242     Update image configuration.
3243   */
3244   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3245   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3246   return(MagickTrue);
3247 }
3248 
3249 /*
3250 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3251 %                                                                             %
3252 %                                                                             %
3253 %                                                                             %
3254 +   X C o l o r E d i t I m a g e                                             %
3255 %                                                                             %
3256 %                                                                             %
3257 %                                                                             %
3258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3259 %
3260 %  XColorEditImage() allows the user to interactively change the color of one
3261 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3262 %
3263 %  The format of the XColorEditImage method is:
3264 %
3265 %      MagickBooleanType XColorEditImage(Display *display,
3266 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3267 %          ExceptionInfo *exception)
3268 %
3269 %  A description of each parameter follows:
3270 %
3271 %    o display: Specifies a connection to an X server;  returned from
3272 %      XOpenDisplay.
3273 %
3274 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3275 %
3276 %    o windows: Specifies a pointer to a XWindows structure.
3277 %
3278 %    o image: the image; returned from ReadImage.
3279 %
3280 %    o exception: return any errors or warnings in this structure.
3281 %
3282 */
XColorEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)3283 static MagickBooleanType XColorEditImage(Display *display,
3284   XResourceInfo *resource_info,XWindows *windows,Image **image,
3285   ExceptionInfo *exception)
3286 {
3287   static const char
3288     *ColorEditMenu[] =
3289     {
3290       "Method",
3291       "Pixel Color",
3292       "Border Color",
3293       "Fuzz",
3294       "Undo",
3295       "Help",
3296       "Dismiss",
3297       (char *) NULL
3298     };
3299 
3300   static const ModeType
3301     ColorEditCommands[] =
3302     {
3303       ColorEditMethodCommand,
3304       ColorEditColorCommand,
3305       ColorEditBorderCommand,
3306       ColorEditFuzzCommand,
3307       ColorEditUndoCommand,
3308       ColorEditHelpCommand,
3309       ColorEditDismissCommand
3310     };
3311 
3312   static PaintMethod
3313     method = PointMethod;
3314 
3315   static unsigned int
3316     pen_id = 0;
3317 
3318   static XColor
3319     border_color = { 0, 0, 0, 0, 0, 0 };
3320 
3321   char
3322     command[MagickPathExtent],
3323     text[MagickPathExtent];
3324 
3325   Cursor
3326     cursor;
3327 
3328   int
3329     entry,
3330     id,
3331     x,
3332     x_offset,
3333     y,
3334     y_offset;
3335 
3336   register Quantum
3337     *q;
3338 
3339   register ssize_t
3340     i;
3341 
3342   unsigned int
3343     height,
3344     width;
3345 
3346   size_t
3347     state;
3348 
3349   XColor
3350     color;
3351 
3352   XEvent
3353     event;
3354 
3355   /*
3356     Map Command widget.
3357   */
3358   (void) CloneString(&windows->command.name,"Color Edit");
3359   windows->command.data=4;
3360   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3361   (void) XMapRaised(display,windows->command.id);
3362   XClientMessage(display,windows->image.id,windows->im_protocols,
3363     windows->im_update_widget,CurrentTime);
3364   /*
3365     Make cursor.
3366   */
3367   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3368     resource_info->background_color,resource_info->foreground_color);
3369   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3370   /*
3371     Track pointer until button 1 is pressed.
3372   */
3373   XQueryPosition(display,windows->image.id,&x,&y);
3374   (void) XSelectInput(display,windows->image.id,
3375     windows->image.attributes.event_mask | PointerMotionMask);
3376   state=DefaultState;
3377   do
3378   {
3379     if (windows->info.mapped != MagickFalse )
3380       {
3381         /*
3382           Display pointer position.
3383         */
3384         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3385           x+windows->image.x,y+windows->image.y);
3386         XInfoWidget(display,windows,text);
3387       }
3388     /*
3389       Wait for next event.
3390     */
3391     XScreenEvent(display,windows,&event,exception);
3392     if (event.xany.window == windows->command.id)
3393       {
3394         /*
3395           Select a command from the Command widget.
3396         */
3397         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3398         if (id < 0)
3399           {
3400             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3401             continue;
3402           }
3403         switch (ColorEditCommands[id])
3404         {
3405           case ColorEditMethodCommand:
3406           {
3407             char
3408               **methods;
3409 
3410             /*
3411               Select a method from the pop-up menu.
3412             */
3413             methods=(char **) GetCommandOptions(MagickMethodOptions);
3414             if (methods == (char **) NULL)
3415               break;
3416             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3417               (const char **) methods,command);
3418             if (entry >= 0)
3419               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3420                 MagickFalse,methods[entry]);
3421             methods=DestroyStringList(methods);
3422             break;
3423           }
3424           case ColorEditColorCommand:
3425           {
3426             const char
3427               *ColorMenu[MaxNumberPens];
3428 
3429             int
3430               pen_number;
3431 
3432             /*
3433               Initialize menu selections.
3434             */
3435             for (i=0; i < (int) (MaxNumberPens-2); i++)
3436               ColorMenu[i]=resource_info->pen_colors[i];
3437             ColorMenu[MaxNumberPens-2]="Browser...";
3438             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3439             /*
3440               Select a pen color from the pop-up menu.
3441             */
3442             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3443               (const char **) ColorMenu,command);
3444             if (pen_number < 0)
3445               break;
3446             if (pen_number == (MaxNumberPens-2))
3447               {
3448                 static char
3449                   color_name[MagickPathExtent] = "gray";
3450 
3451                 /*
3452                   Select a pen color from a dialog.
3453                 */
3454                 resource_info->pen_colors[pen_number]=color_name;
3455                 XColorBrowserWidget(display,windows,"Select",color_name);
3456                 if (*color_name == '\0')
3457                   break;
3458               }
3459             /*
3460               Set pen color.
3461             */
3462             (void) XParseColor(display,windows->map_info->colormap,
3463               resource_info->pen_colors[pen_number],&color);
3464             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3465               (unsigned int) MaxColors,&color);
3466             windows->pixel_info->pen_colors[pen_number]=color;
3467             pen_id=(unsigned int) pen_number;
3468             break;
3469           }
3470           case ColorEditBorderCommand:
3471           {
3472             const char
3473               *ColorMenu[MaxNumberPens];
3474 
3475             int
3476               pen_number;
3477 
3478             /*
3479               Initialize menu selections.
3480             */
3481             for (i=0; i < (int) (MaxNumberPens-2); i++)
3482               ColorMenu[i]=resource_info->pen_colors[i];
3483             ColorMenu[MaxNumberPens-2]="Browser...";
3484             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3485             /*
3486               Select a pen color from the pop-up menu.
3487             */
3488             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3489               (const char **) ColorMenu,command);
3490             if (pen_number < 0)
3491               break;
3492             if (pen_number == (MaxNumberPens-2))
3493               {
3494                 static char
3495                   color_name[MagickPathExtent] = "gray";
3496 
3497                 /*
3498                   Select a pen color from a dialog.
3499                 */
3500                 resource_info->pen_colors[pen_number]=color_name;
3501                 XColorBrowserWidget(display,windows,"Select",color_name);
3502                 if (*color_name == '\0')
3503                   break;
3504               }
3505             /*
3506               Set border color.
3507             */
3508             (void) XParseColor(display,windows->map_info->colormap,
3509               resource_info->pen_colors[pen_number],&border_color);
3510             break;
3511           }
3512           case ColorEditFuzzCommand:
3513           {
3514             static char
3515               fuzz[MagickPathExtent];
3516 
3517             static const char
3518               *FuzzMenu[] =
3519               {
3520                 "0%",
3521                 "2%",
3522                 "5%",
3523                 "10%",
3524                 "15%",
3525                 "Dialog...",
3526                 (char *) NULL,
3527               };
3528 
3529             /*
3530               Select a command from the pop-up menu.
3531             */
3532             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3533               command);
3534             if (entry < 0)
3535               break;
3536             if (entry != 5)
3537               {
3538                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3539                   QuantumRange+1.0);
3540                 break;
3541               }
3542             (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3543             (void) XDialogWidget(display,windows,"Ok",
3544               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3545             if (*fuzz == '\0')
3546               break;
3547             (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3548             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3549               1.0);
3550             break;
3551           }
3552           case ColorEditUndoCommand:
3553           {
3554             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3555               image,exception);
3556             break;
3557           }
3558           case ColorEditHelpCommand:
3559           default:
3560           {
3561             XTextViewWidget(display,resource_info,windows,MagickFalse,
3562               "Help Viewer - Image Annotation",ImageColorEditHelp);
3563             break;
3564           }
3565           case ColorEditDismissCommand:
3566           {
3567             /*
3568               Prematurely exit.
3569             */
3570             state|=EscapeState;
3571             state|=ExitState;
3572             break;
3573           }
3574         }
3575         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3576         continue;
3577       }
3578     switch (event.type)
3579     {
3580       case ButtonPress:
3581       {
3582         if (event.xbutton.button != Button1)
3583           break;
3584         if ((event.xbutton.window != windows->image.id) &&
3585             (event.xbutton.window != windows->magnify.id))
3586           break;
3587         /*
3588           exit loop.
3589         */
3590         x=event.xbutton.x;
3591         y=event.xbutton.y;
3592         (void) XMagickCommand(display,resource_info,windows,
3593           SaveToUndoBufferCommand,image,exception);
3594         state|=UpdateConfigurationState;
3595         break;
3596       }
3597       case ButtonRelease:
3598       {
3599         if (event.xbutton.button != Button1)
3600           break;
3601         if ((event.xbutton.window != windows->image.id) &&
3602             (event.xbutton.window != windows->magnify.id))
3603           break;
3604         /*
3605           Update colormap information.
3606         */
3607         x=event.xbutton.x;
3608         y=event.xbutton.y;
3609         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3610         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3611         XInfoWidget(display,windows,text);
3612         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3613         state&=(~UpdateConfigurationState);
3614         break;
3615       }
3616       case Expose:
3617         break;
3618       case KeyPress:
3619       {
3620         KeySym
3621           key_symbol;
3622 
3623         if (event.xkey.window == windows->magnify.id)
3624           {
3625             Window
3626               window;
3627 
3628             window=windows->magnify.id;
3629             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3630           }
3631         if (event.xkey.window != windows->image.id)
3632           break;
3633         /*
3634           Respond to a user key press.
3635         */
3636         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3637           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3638         switch ((int) key_symbol)
3639         {
3640           case XK_Escape:
3641           case XK_F20:
3642           {
3643             /*
3644               Prematurely exit.
3645             */
3646             state|=ExitState;
3647             break;
3648           }
3649           case XK_F1:
3650           case XK_Help:
3651           {
3652             XTextViewWidget(display,resource_info,windows,MagickFalse,
3653               "Help Viewer - Image Annotation",ImageColorEditHelp);
3654             break;
3655           }
3656           default:
3657           {
3658             (void) XBell(display,0);
3659             break;
3660           }
3661         }
3662         break;
3663       }
3664       case MotionNotify:
3665       {
3666         /*
3667           Map and unmap Info widget as cursor crosses its boundaries.
3668         */
3669         x=event.xmotion.x;
3670         y=event.xmotion.y;
3671         if (windows->info.mapped != MagickFalse )
3672           {
3673             if ((x < (int) (windows->info.x+windows->info.width)) &&
3674                 (y < (int) (windows->info.y+windows->info.height)))
3675               (void) XWithdrawWindow(display,windows->info.id,
3676                 windows->info.screen);
3677           }
3678         else
3679           if ((x > (int) (windows->info.x+windows->info.width)) ||
3680               (y > (int) (windows->info.y+windows->info.height)))
3681             (void) XMapWindow(display,windows->info.id);
3682         break;
3683       }
3684       default:
3685         break;
3686     }
3687     if (event.xany.window == windows->magnify.id)
3688       {
3689         x=windows->magnify.x-windows->image.x;
3690         y=windows->magnify.y-windows->image.y;
3691       }
3692     x_offset=x;
3693     y_offset=y;
3694     if ((state & UpdateConfigurationState) != 0)
3695       {
3696         CacheView
3697           *image_view;
3698 
3699         int
3700           x,
3701           y;
3702 
3703         /*
3704           Pixel edit is relative to image configuration.
3705         */
3706         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3707           MagickTrue);
3708         color=windows->pixel_info->pen_colors[pen_id];
3709         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3710         width=(unsigned int) (*image)->columns;
3711         height=(unsigned int) (*image)->rows;
3712         x=0;
3713         y=0;
3714         if (windows->image.crop_geometry != (char *) NULL)
3715           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3716             &width,&height);
3717         x_offset=(int)
3718           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3719         y_offset=(int)
3720           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3721         if ((x_offset < 0) || (y_offset < 0))
3722           continue;
3723         if ((x_offset >= (int) (*image)->columns) ||
3724             (y_offset >= (int) (*image)->rows))
3725           continue;
3726         image_view=AcquireAuthenticCacheView(*image,exception);
3727         switch (method)
3728         {
3729           case PointMethod:
3730           default:
3731           {
3732             /*
3733               Update color information using point algorithm.
3734             */
3735             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3736               return(MagickFalse);
3737             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3738               (ssize_t) y_offset,1,1,exception);
3739             if (q == (Quantum *) NULL)
3740               break;
3741             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3742             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3743             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3744             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3745             break;
3746           }
3747           case ReplaceMethod:
3748           {
3749             PixelInfo
3750               pixel,
3751               target;
3752 
3753             /*
3754               Update color information using replace algorithm.
3755             */
3756             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3757               x_offset,(ssize_t) y_offset,&target,exception);
3758             if ((*image)->storage_class == DirectClass)
3759               {
3760                 for (y=0; y < (int) (*image)->rows; y++)
3761                 {
3762                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3763                     (*image)->columns,1,exception);
3764                   if (q == (Quantum *) NULL)
3765                     break;
3766                   for (x=0; x < (int) (*image)->columns; x++)
3767                   {
3768                     GetPixelInfoPixel(*image,q,&pixel);
3769                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3770                       {
3771                         SetPixelRed(*image,ScaleShortToQuantum(
3772                           color.red),q);
3773                         SetPixelGreen(*image,ScaleShortToQuantum(
3774                           color.green),q);
3775                         SetPixelBlue(*image,ScaleShortToQuantum(
3776                           color.blue),q);
3777                       }
3778                     q+=GetPixelChannels(*image);
3779                   }
3780                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3781                     break;
3782                 }
3783               }
3784             else
3785               {
3786                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3787                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3788                     {
3789                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3790                         color.red);
3791                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3792                         color.green);
3793                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3794                         color.blue);
3795                     }
3796                 (void) SyncImage(*image,exception);
3797               }
3798             break;
3799           }
3800           case FloodfillMethod:
3801           case FillToBorderMethod:
3802           {
3803             DrawInfo
3804               *draw_info;
3805 
3806             PixelInfo
3807               target;
3808 
3809             /*
3810               Update color information using floodfill algorithm.
3811             */
3812             (void) GetOneVirtualPixelInfo(*image,
3813               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3814               y_offset,&target,exception);
3815             if (method == FillToBorderMethod)
3816               {
3817                 target.red=(double)
3818                   ScaleShortToQuantum(border_color.red);
3819                 target.green=(double)
3820                   ScaleShortToQuantum(border_color.green);
3821                 target.blue=(double)
3822                   ScaleShortToQuantum(border_color.blue);
3823               }
3824             draw_info=CloneDrawInfo(resource_info->image_info,
3825               (DrawInfo *) NULL);
3826             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3827               AllCompliance,&draw_info->fill,exception);
3828             (void) FloodfillPaintImage(*image,draw_info,&target,
3829               (ssize_t)x_offset,(ssize_t)y_offset,
3830               method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3831             draw_info=DestroyDrawInfo(draw_info);
3832             break;
3833           }
3834           case ResetMethod:
3835           {
3836             /*
3837               Update color information using reset algorithm.
3838             */
3839             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3840               return(MagickFalse);
3841             for (y=0; y < (int) (*image)->rows; y++)
3842             {
3843               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3844                 (*image)->columns,1,exception);
3845               if (q == (Quantum *) NULL)
3846                 break;
3847               for (x=0; x < (int) (*image)->columns; x++)
3848               {
3849                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3850                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3851                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3852                 q+=GetPixelChannels(*image);
3853               }
3854               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3855                 break;
3856             }
3857             break;
3858           }
3859         }
3860         image_view=DestroyCacheView(image_view);
3861         state&=(~UpdateConfigurationState);
3862       }
3863   } while ((state & ExitState) == 0);
3864   (void) XSelectInput(display,windows->image.id,
3865     windows->image.attributes.event_mask);
3866   XSetCursorState(display,windows,MagickFalse);
3867   (void) XFreeCursor(display,cursor);
3868   return(MagickTrue);
3869 }
3870 
3871 /*
3872 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3873 %                                                                             %
3874 %                                                                             %
3875 %                                                                             %
3876 +   X C o m p o s i t e I m a g e                                             %
3877 %                                                                             %
3878 %                                                                             %
3879 %                                                                             %
3880 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3881 %
3882 %  XCompositeImage() requests an image name from the user, reads the image and
3883 %  composites it with the X window image at a location the user chooses with
3884 %  the pointer.
3885 %
3886 %  The format of the XCompositeImage method is:
3887 %
3888 %      MagickBooleanType XCompositeImage(Display *display,
3889 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3890 %        ExceptionInfo *exception)
3891 %
3892 %  A description of each parameter follows:
3893 %
3894 %    o display: Specifies a connection to an X server;  returned from
3895 %      XOpenDisplay.
3896 %
3897 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3898 %
3899 %    o windows: Specifies a pointer to a XWindows structure.
3900 %
3901 %    o image: the image; returned from ReadImage.
3902 %
3903 %    o exception: return any errors or warnings in this structure.
3904 %
3905 */
XCompositeImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)3906 static MagickBooleanType XCompositeImage(Display *display,
3907   XResourceInfo *resource_info,XWindows *windows,Image *image,
3908   ExceptionInfo *exception)
3909 {
3910   static char
3911     displacement_geometry[MagickPathExtent] = "30x30",
3912     filename[MagickPathExtent] = "\0";
3913 
3914   static const char
3915     *CompositeMenu[] =
3916     {
3917       "Operators",
3918       "Dissolve",
3919       "Displace",
3920       "Help",
3921       "Dismiss",
3922       (char *) NULL
3923     };
3924 
3925   static CompositeOperator
3926     compose = CopyCompositeOp;
3927 
3928   static const ModeType
3929     CompositeCommands[] =
3930     {
3931       CompositeOperatorsCommand,
3932       CompositeDissolveCommand,
3933       CompositeDisplaceCommand,
3934       CompositeHelpCommand,
3935       CompositeDismissCommand
3936     };
3937 
3938   char
3939     text[MagickPathExtent];
3940 
3941   Cursor
3942     cursor;
3943 
3944   Image
3945     *composite_image;
3946 
3947   int
3948     entry,
3949     id,
3950     x,
3951     y;
3952 
3953   double
3954     blend,
3955     scale_factor;
3956 
3957   RectangleInfo
3958     highlight_info,
3959     composite_info;
3960 
3961   unsigned int
3962     height,
3963     width;
3964 
3965   size_t
3966     state;
3967 
3968   XEvent
3969     event;
3970 
3971   /*
3972     Request image file name from user.
3973   */
3974   XFileBrowserWidget(display,windows,"Composite",filename);
3975   if (*filename == '\0')
3976     return(MagickTrue);
3977   /*
3978     Read image.
3979   */
3980   XSetCursorState(display,windows,MagickTrue);
3981   XCheckRefreshWindows(display,windows);
3982   (void) CopyMagickString(resource_info->image_info->filename,filename,
3983     MagickPathExtent);
3984   composite_image=ReadImage(resource_info->image_info,exception);
3985   CatchException(exception);
3986   XSetCursorState(display,windows,MagickFalse);
3987   if (composite_image == (Image *) NULL)
3988     return(MagickFalse);
3989   /*
3990     Map Command widget.
3991   */
3992   (void) CloneString(&windows->command.name,"Composite");
3993   windows->command.data=1;
3994   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3995   (void) XMapRaised(display,windows->command.id);
3996   XClientMessage(display,windows->image.id,windows->im_protocols,
3997     windows->im_update_widget,CurrentTime);
3998   /*
3999     Track pointer until button 1 is pressed.
4000   */
4001   XQueryPosition(display,windows->image.id,&x,&y);
4002   (void) XSelectInput(display,windows->image.id,
4003     windows->image.attributes.event_mask | PointerMotionMask);
4004   composite_info.x=(ssize_t) windows->image.x+x;
4005   composite_info.y=(ssize_t) windows->image.y+y;
4006   composite_info.width=0;
4007   composite_info.height=0;
4008   cursor=XCreateFontCursor(display,XC_ul_angle);
4009   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4010   blend=0.0;
4011   state=DefaultState;
4012   do
4013   {
4014     if (windows->info.mapped != MagickFalse )
4015       {
4016         /*
4017           Display pointer position.
4018         */
4019         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4020           (long) composite_info.x,(long) composite_info.y);
4021         XInfoWidget(display,windows,text);
4022       }
4023     highlight_info=composite_info;
4024     highlight_info.x=composite_info.x-windows->image.x;
4025     highlight_info.y=composite_info.y-windows->image.y;
4026     XHighlightRectangle(display,windows->image.id,
4027       windows->image.highlight_context,&highlight_info);
4028     /*
4029       Wait for next event.
4030     */
4031     XScreenEvent(display,windows,&event,exception);
4032     XHighlightRectangle(display,windows->image.id,
4033       windows->image.highlight_context,&highlight_info);
4034     if (event.xany.window == windows->command.id)
4035       {
4036         /*
4037           Select a command from the Command widget.
4038         */
4039         id=XCommandWidget(display,windows,CompositeMenu,&event);
4040         if (id < 0)
4041           continue;
4042         switch (CompositeCommands[id])
4043         {
4044           case CompositeOperatorsCommand:
4045           {
4046             char
4047               command[MagickPathExtent],
4048               **operators;
4049 
4050             /*
4051               Select a command from the pop-up menu.
4052             */
4053             operators=GetCommandOptions(MagickComposeOptions);
4054             if (operators == (char **) NULL)
4055               break;
4056             entry=XMenuWidget(display,windows,CompositeMenu[id],
4057               (const char **) operators,command);
4058             if (entry >= 0)
4059               compose=(CompositeOperator) ParseCommandOption(
4060                 MagickComposeOptions,MagickFalse,operators[entry]);
4061             operators=DestroyStringList(operators);
4062             break;
4063           }
4064           case CompositeDissolveCommand:
4065           {
4066             static char
4067               factor[MagickPathExtent] = "20.0";
4068 
4069             /*
4070               Dissolve the two images a given percent.
4071             */
4072             (void) XSetFunction(display,windows->image.highlight_context,
4073               GXcopy);
4074             (void) XDialogWidget(display,windows,"Dissolve",
4075               "Enter the blend factor (0.0 - 99.9%):",factor);
4076             (void) XSetFunction(display,windows->image.highlight_context,
4077               GXinvert);
4078             if (*factor == '\0')
4079               break;
4080             blend=StringToDouble(factor,(char **) NULL);
4081             compose=DissolveCompositeOp;
4082             break;
4083           }
4084           case CompositeDisplaceCommand:
4085           {
4086             /*
4087               Get horizontal and vertical scale displacement geometry.
4088             */
4089             (void) XSetFunction(display,windows->image.highlight_context,
4090               GXcopy);
4091             (void) XDialogWidget(display,windows,"Displace",
4092               "Enter the horizontal and vertical scale:",displacement_geometry);
4093             (void) XSetFunction(display,windows->image.highlight_context,
4094               GXinvert);
4095             if (*displacement_geometry == '\0')
4096               break;
4097             compose=DisplaceCompositeOp;
4098             break;
4099           }
4100           case CompositeHelpCommand:
4101           {
4102             (void) XSetFunction(display,windows->image.highlight_context,
4103               GXcopy);
4104             XTextViewWidget(display,resource_info,windows,MagickFalse,
4105               "Help Viewer - Image Composite",ImageCompositeHelp);
4106             (void) XSetFunction(display,windows->image.highlight_context,
4107               GXinvert);
4108             break;
4109           }
4110           case CompositeDismissCommand:
4111           {
4112             /*
4113               Prematurely exit.
4114             */
4115             state|=EscapeState;
4116             state|=ExitState;
4117             break;
4118           }
4119           default:
4120             break;
4121         }
4122         continue;
4123       }
4124     switch (event.type)
4125     {
4126       case ButtonPress:
4127       {
4128         if (image->debug != MagickFalse )
4129           (void) LogMagickEvent(X11Event,GetMagickModule(),
4130             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4131             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4132         if (event.xbutton.button != Button1)
4133           break;
4134         if (event.xbutton.window != windows->image.id)
4135           break;
4136         /*
4137           Change cursor.
4138         */
4139         composite_info.width=composite_image->columns;
4140         composite_info.height=composite_image->rows;
4141         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4142         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4143         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4144         break;
4145       }
4146       case ButtonRelease:
4147       {
4148         if (image->debug != MagickFalse )
4149           (void) LogMagickEvent(X11Event,GetMagickModule(),
4150             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4151             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4152         if (event.xbutton.button != Button1)
4153           break;
4154         if (event.xbutton.window != windows->image.id)
4155           break;
4156         if ((composite_info.width != 0) && (composite_info.height != 0))
4157           {
4158             /*
4159               User has selected the location of the composite image.
4160             */
4161             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4162             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4163             state|=ExitState;
4164           }
4165         break;
4166       }
4167       case Expose:
4168         break;
4169       case KeyPress:
4170       {
4171         char
4172           command[MagickPathExtent];
4173 
4174         KeySym
4175           key_symbol;
4176 
4177         int
4178           length;
4179 
4180         if (event.xkey.window != windows->image.id)
4181           break;
4182         /*
4183           Respond to a user key press.
4184         */
4185         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4186           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4187         *(command+length)='\0';
4188         if (image->debug != MagickFalse )
4189           (void) LogMagickEvent(X11Event,GetMagickModule(),
4190             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4191         switch ((int) key_symbol)
4192         {
4193           case XK_Escape:
4194           case XK_F20:
4195           {
4196             /*
4197               Prematurely exit.
4198             */
4199             composite_image=DestroyImage(composite_image);
4200             state|=EscapeState;
4201             state|=ExitState;
4202             break;
4203           }
4204           case XK_F1:
4205           case XK_Help:
4206           {
4207             (void) XSetFunction(display,windows->image.highlight_context,
4208               GXcopy);
4209             XTextViewWidget(display,resource_info,windows,MagickFalse,
4210               "Help Viewer - Image Composite",ImageCompositeHelp);
4211             (void) XSetFunction(display,windows->image.highlight_context,
4212               GXinvert);
4213             break;
4214           }
4215           default:
4216           {
4217             (void) XBell(display,0);
4218             break;
4219           }
4220         }
4221         break;
4222       }
4223       case MotionNotify:
4224       {
4225         /*
4226           Map and unmap Info widget as text cursor crosses its boundaries.
4227         */
4228         x=event.xmotion.x;
4229         y=event.xmotion.y;
4230         if (windows->info.mapped != MagickFalse )
4231           {
4232             if ((x < (int) (windows->info.x+windows->info.width)) &&
4233                 (y < (int) (windows->info.y+windows->info.height)))
4234               (void) XWithdrawWindow(display,windows->info.id,
4235                 windows->info.screen);
4236           }
4237         else
4238           if ((x > (int) (windows->info.x+windows->info.width)) ||
4239               (y > (int) (windows->info.y+windows->info.height)))
4240             (void) XMapWindow(display,windows->info.id);
4241         composite_info.x=(ssize_t) windows->image.x+x;
4242         composite_info.y=(ssize_t) windows->image.y+y;
4243         break;
4244       }
4245       default:
4246       {
4247         if (image->debug != MagickFalse )
4248           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4249             event.type);
4250         break;
4251       }
4252     }
4253   } while ((state & ExitState) == 0);
4254   (void) XSelectInput(display,windows->image.id,
4255     windows->image.attributes.event_mask);
4256   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4257   XSetCursorState(display,windows,MagickFalse);
4258   (void) XFreeCursor(display,cursor);
4259   if ((state & EscapeState) != 0)
4260     return(MagickTrue);
4261   /*
4262     Image compositing is relative to image configuration.
4263   */
4264   XSetCursorState(display,windows,MagickTrue);
4265   XCheckRefreshWindows(display,windows);
4266   width=(unsigned int) image->columns;
4267   height=(unsigned int) image->rows;
4268   x=0;
4269   y=0;
4270   if (windows->image.crop_geometry != (char *) NULL)
4271     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4272   scale_factor=(double) width/windows->image.ximage->width;
4273   composite_info.x+=x;
4274   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4275   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4276   scale_factor=(double) height/windows->image.ximage->height;
4277   composite_info.y+=y;
4278   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4279   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4280   if ((composite_info.width != composite_image->columns) ||
4281       (composite_info.height != composite_image->rows))
4282     {
4283       Image
4284         *resize_image;
4285 
4286       /*
4287         Scale composite image.
4288       */
4289       resize_image=ResizeImage(composite_image,composite_info.width,
4290         composite_info.height,composite_image->filter,exception);
4291       composite_image=DestroyImage(composite_image);
4292       if (resize_image == (Image *) NULL)
4293         {
4294           XSetCursorState(display,windows,MagickFalse);
4295           return(MagickFalse);
4296         }
4297       composite_image=resize_image;
4298     }
4299   if (compose == DisplaceCompositeOp)
4300     (void) SetImageArtifact(composite_image,"compose:args",
4301       displacement_geometry);
4302   if (blend != 0.0)
4303     {
4304       CacheView
4305         *image_view;
4306 
4307       int
4308         y;
4309 
4310       Quantum
4311         opacity;
4312 
4313       register int
4314         x;
4315 
4316       register Quantum
4317         *q;
4318 
4319       /*
4320         Create mattes for blending.
4321       */
4322       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4323       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4324         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4325       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4326         return(MagickFalse);
4327       image->alpha_trait=BlendPixelTrait;
4328       image_view=AcquireAuthenticCacheView(image,exception);
4329       for (y=0; y < (int) image->rows; y++)
4330       {
4331         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4332           exception);
4333         if (q == (Quantum *) NULL)
4334           break;
4335         for (x=0; x < (int) image->columns; x++)
4336         {
4337           SetPixelAlpha(image,opacity,q);
4338           q+=GetPixelChannels(image);
4339         }
4340         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4341           break;
4342       }
4343       image_view=DestroyCacheView(image_view);
4344     }
4345   /*
4346     Composite image with X Image window.
4347   */
4348   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4349     composite_info.x,composite_info.y,exception);
4350   composite_image=DestroyImage(composite_image);
4351   XSetCursorState(display,windows,MagickFalse);
4352   /*
4353     Update image configuration.
4354   */
4355   XConfigureImageColormap(display,resource_info,windows,image,exception);
4356   (void) XConfigureImage(display,resource_info,windows,image,exception);
4357   return(MagickTrue);
4358 }
4359 
4360 /*
4361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4362 %                                                                             %
4363 %                                                                             %
4364 %                                                                             %
4365 +   X C o n f i g u r e I m a g e                                             %
4366 %                                                                             %
4367 %                                                                             %
4368 %                                                                             %
4369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4370 %
4371 %  XConfigureImage() creates a new X image.  It also notifies the window
4372 %  manager of the new image size and configures the transient widows.
4373 %
4374 %  The format of the XConfigureImage method is:
4375 %
4376 %      MagickBooleanType XConfigureImage(Display *display,
4377 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4378 %        ExceptionInfo *exception)
4379 %
4380 %  A description of each parameter follows:
4381 %
4382 %    o display: Specifies a connection to an X server; returned from
4383 %      XOpenDisplay.
4384 %
4385 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4386 %
4387 %    o windows: Specifies a pointer to a XWindows structure.
4388 %
4389 %    o image: the image.
4390 %
4391 %    o exception: return any errors or warnings in this structure.
4392 %
4393 %    o exception: return any errors or warnings in this structure.
4394 %
4395 */
XConfigureImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)4396 static MagickBooleanType XConfigureImage(Display *display,
4397   XResourceInfo *resource_info,XWindows *windows,Image *image,
4398   ExceptionInfo *exception)
4399 {
4400   char
4401     geometry[MagickPathExtent];
4402 
4403   MagickStatusType
4404     status;
4405 
4406   size_t
4407     mask,
4408     height,
4409     width;
4410 
4411   ssize_t
4412     x,
4413     y;
4414 
4415   XSizeHints
4416     *size_hints;
4417 
4418   XWindowChanges
4419     window_changes;
4420 
4421   /*
4422     Dismiss if window dimensions are zero.
4423   */
4424   width=(unsigned int) windows->image.window_changes.width;
4425   height=(unsigned int) windows->image.window_changes.height;
4426   if (image->debug != MagickFalse )
4427     (void) LogMagickEvent(X11Event,GetMagickModule(),
4428       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4429       windows->image.ximage->height,(double) width,(double) height);
4430   if ((width*height) == 0)
4431     return(MagickTrue);
4432   x=0;
4433   y=0;
4434   /*
4435     Resize image to fit Image window dimensions.
4436   */
4437   XSetCursorState(display,windows,MagickTrue);
4438   (void) XFlush(display);
4439   if (((int) width != windows->image.ximage->width) ||
4440       ((int) height != windows->image.ximage->height))
4441     image->taint=MagickTrue;
4442   windows->magnify.x=(int)
4443     width*windows->magnify.x/windows->image.ximage->width;
4444   windows->magnify.y=(int)
4445     height*windows->magnify.y/windows->image.ximage->height;
4446   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4447   windows->image.y=(int)
4448     (height*windows->image.y/windows->image.ximage->height);
4449   status=XMakeImage(display,resource_info,&windows->image,image,
4450     (unsigned int) width,(unsigned int) height,exception);
4451   if (status == MagickFalse)
4452     XNoticeWidget(display,windows,"Unable to configure X image:",
4453       windows->image.name);
4454   /*
4455     Notify window manager of the new configuration.
4456   */
4457   if (resource_info->image_geometry != (char *) NULL)
4458     (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4459       resource_info->image_geometry);
4460   else
4461     (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4462       XDisplayWidth(display,windows->image.screen),
4463       XDisplayHeight(display,windows->image.screen));
4464   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4465   window_changes.width=(int) width;
4466   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4467     window_changes.width=XDisplayWidth(display,windows->image.screen);
4468   window_changes.height=(int) height;
4469   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4470     window_changes.height=XDisplayHeight(display,windows->image.screen);
4471   mask=(size_t) (CWWidth | CWHeight);
4472   if (resource_info->backdrop)
4473     {
4474       mask|=CWX | CWY;
4475       window_changes.x=(int)
4476         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4477       window_changes.y=(int)
4478         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4479     }
4480   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4481     (unsigned int) mask,&window_changes);
4482   (void) XClearWindow(display,windows->image.id);
4483   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4484   /*
4485     Update Magnify window configuration.
4486   */
4487   if (windows->magnify.mapped != MagickFalse )
4488     XMakeMagnifyImage(display,windows,exception);
4489   windows->pan.crop_geometry=windows->image.crop_geometry;
4490   XBestIconSize(display,&windows->pan,image);
4491   while (((windows->pan.width << 1) < MaxIconSize) &&
4492          ((windows->pan.height << 1) < MaxIconSize))
4493   {
4494     windows->pan.width<<=1;
4495     windows->pan.height<<=1;
4496   }
4497   if (windows->pan.geometry != (char *) NULL)
4498     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4499       &windows->pan.width,&windows->pan.height);
4500   window_changes.width=(int) windows->pan.width;
4501   window_changes.height=(int) windows->pan.height;
4502   size_hints=XAllocSizeHints();
4503   if (size_hints != (XSizeHints *) NULL)
4504     {
4505       /*
4506         Set new size hints.
4507       */
4508       size_hints->flags=PSize | PMinSize | PMaxSize;
4509       size_hints->width=window_changes.width;
4510       size_hints->height=window_changes.height;
4511       size_hints->min_width=size_hints->width;
4512       size_hints->min_height=size_hints->height;
4513       size_hints->max_width=size_hints->width;
4514       size_hints->max_height=size_hints->height;
4515       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4516       (void) XFree((void *) size_hints);
4517     }
4518   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4519     (unsigned int) (CWWidth | CWHeight),&window_changes);
4520   /*
4521     Update icon window configuration.
4522   */
4523   windows->icon.crop_geometry=windows->image.crop_geometry;
4524   XBestIconSize(display,&windows->icon,image);
4525   window_changes.width=(int) windows->icon.width;
4526   window_changes.height=(int) windows->icon.height;
4527   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4528     (unsigned int) (CWWidth | CWHeight),&window_changes);
4529   XSetCursorState(display,windows,MagickFalse);
4530   return(status != 0 ? MagickTrue : MagickFalse);
4531 }
4532 
4533 /*
4534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4535 %                                                                             %
4536 %                                                                             %
4537 %                                                                             %
4538 +   X C r o p I m a g e                                                       %
4539 %                                                                             %
4540 %                                                                             %
4541 %                                                                             %
4542 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4543 %
4544 %  XCropImage() allows the user to select a region of the image and crop, copy,
4545 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4546 %  the image with XPasteImage.
4547 %
4548 %  The format of the XCropImage method is:
4549 %
4550 %      MagickBooleanType XCropImage(Display *display,
4551 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4552 %        const ClipboardMode mode,ExceptionInfo *exception)
4553 %
4554 %  A description of each parameter follows:
4555 %
4556 %    o display: Specifies a connection to an X server; returned from
4557 %      XOpenDisplay.
4558 %
4559 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4560 %
4561 %    o windows: Specifies a pointer to a XWindows structure.
4562 %
4563 %    o image: the image; returned from ReadImage.
4564 %
4565 %    o mode: This unsigned value specified whether the image should be
4566 %      cropped, copied, or cut.
4567 %
4568 %    o exception: return any errors or warnings in this structure.
4569 %
4570 */
XCropImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,const ClipboardMode mode,ExceptionInfo * exception)4571 static MagickBooleanType XCropImage(Display *display,
4572   XResourceInfo *resource_info,XWindows *windows,Image *image,
4573   const ClipboardMode mode,ExceptionInfo *exception)
4574 {
4575   static const char
4576     *CropModeMenu[] =
4577     {
4578       "Help",
4579       "Dismiss",
4580       (char *) NULL
4581     },
4582     *RectifyModeMenu[] =
4583     {
4584       "Crop",
4585       "Help",
4586       "Dismiss",
4587       (char *) NULL
4588     };
4589 
4590   static const ModeType
4591     CropCommands[] =
4592     {
4593       CropHelpCommand,
4594       CropDismissCommand
4595     },
4596     RectifyCommands[] =
4597     {
4598       RectifyCopyCommand,
4599       RectifyHelpCommand,
4600       RectifyDismissCommand
4601     };
4602 
4603   CacheView
4604     *image_view;
4605 
4606   char
4607     command[MagickPathExtent],
4608     text[MagickPathExtent];
4609 
4610   Cursor
4611     cursor;
4612 
4613   int
4614     id,
4615     x,
4616     y;
4617 
4618   KeySym
4619     key_symbol;
4620 
4621   Image
4622     *crop_image;
4623 
4624   double
4625     scale_factor;
4626 
4627   RectangleInfo
4628     crop_info,
4629     highlight_info;
4630 
4631   register Quantum
4632     *q;
4633 
4634   unsigned int
4635     height,
4636     width;
4637 
4638   size_t
4639     state;
4640 
4641   XEvent
4642     event;
4643 
4644   /*
4645     Map Command widget.
4646   */
4647   switch (mode)
4648   {
4649     case CopyMode:
4650     {
4651       (void) CloneString(&windows->command.name,"Copy");
4652       break;
4653     }
4654     case CropMode:
4655     {
4656       (void) CloneString(&windows->command.name,"Crop");
4657       break;
4658     }
4659     case CutMode:
4660     {
4661       (void) CloneString(&windows->command.name,"Cut");
4662       break;
4663     }
4664   }
4665   RectifyModeMenu[0]=windows->command.name;
4666   windows->command.data=0;
4667   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4668   (void) XMapRaised(display,windows->command.id);
4669   XClientMessage(display,windows->image.id,windows->im_protocols,
4670     windows->im_update_widget,CurrentTime);
4671   /*
4672     Track pointer until button 1 is pressed.
4673   */
4674   XQueryPosition(display,windows->image.id,&x,&y);
4675   (void) XSelectInput(display,windows->image.id,
4676     windows->image.attributes.event_mask | PointerMotionMask);
4677   crop_info.x=(ssize_t) windows->image.x+x;
4678   crop_info.y=(ssize_t) windows->image.y+y;
4679   crop_info.width=0;
4680   crop_info.height=0;
4681   cursor=XCreateFontCursor(display,XC_fleur);
4682   state=DefaultState;
4683   do
4684   {
4685     if (windows->info.mapped != MagickFalse )
4686       {
4687         /*
4688           Display pointer position.
4689         */
4690         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4691           (long) crop_info.x,(long) crop_info.y);
4692         XInfoWidget(display,windows,text);
4693       }
4694     /*
4695       Wait for next event.
4696     */
4697     XScreenEvent(display,windows,&event,exception);
4698     if (event.xany.window == windows->command.id)
4699       {
4700         /*
4701           Select a command from the Command widget.
4702         */
4703         id=XCommandWidget(display,windows,CropModeMenu,&event);
4704         if (id < 0)
4705           continue;
4706         switch (CropCommands[id])
4707         {
4708           case CropHelpCommand:
4709           {
4710             switch (mode)
4711             {
4712               case CopyMode:
4713               {
4714                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4715                   "Help Viewer - Image Copy",ImageCopyHelp);
4716                 break;
4717               }
4718               case CropMode:
4719               {
4720                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4721                   "Help Viewer - Image Crop",ImageCropHelp);
4722                 break;
4723               }
4724               case CutMode:
4725               {
4726                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4727                   "Help Viewer - Image Cut",ImageCutHelp);
4728                 break;
4729               }
4730             }
4731             break;
4732           }
4733           case CropDismissCommand:
4734           {
4735             /*
4736               Prematurely exit.
4737             */
4738             state|=EscapeState;
4739             state|=ExitState;
4740             break;
4741           }
4742           default:
4743             break;
4744         }
4745         continue;
4746       }
4747     switch (event.type)
4748     {
4749       case ButtonPress:
4750       {
4751         if (event.xbutton.button != Button1)
4752           break;
4753         if (event.xbutton.window != windows->image.id)
4754           break;
4755         /*
4756           Note first corner of cropping rectangle-- exit loop.
4757         */
4758         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4759         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4760         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4761         state|=ExitState;
4762         break;
4763       }
4764       case ButtonRelease:
4765         break;
4766       case Expose:
4767         break;
4768       case KeyPress:
4769       {
4770         if (event.xkey.window != windows->image.id)
4771           break;
4772         /*
4773           Respond to a user key press.
4774         */
4775         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4776           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4777         switch ((int) key_symbol)
4778         {
4779           case XK_Escape:
4780           case XK_F20:
4781           {
4782             /*
4783               Prematurely exit.
4784             */
4785             state|=EscapeState;
4786             state|=ExitState;
4787             break;
4788           }
4789           case XK_F1:
4790           case XK_Help:
4791           {
4792             switch (mode)
4793             {
4794               case CopyMode:
4795               {
4796                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4797                   "Help Viewer - Image Copy",ImageCopyHelp);
4798                 break;
4799               }
4800               case CropMode:
4801               {
4802                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4803                   "Help Viewer - Image Crop",ImageCropHelp);
4804                 break;
4805               }
4806               case CutMode:
4807               {
4808                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4809                   "Help Viewer - Image Cut",ImageCutHelp);
4810                 break;
4811               }
4812             }
4813             break;
4814           }
4815           default:
4816           {
4817             (void) XBell(display,0);
4818             break;
4819           }
4820         }
4821         break;
4822       }
4823       case MotionNotify:
4824       {
4825         if (event.xmotion.window != windows->image.id)
4826           break;
4827         /*
4828           Map and unmap Info widget as text cursor crosses its boundaries.
4829         */
4830         x=event.xmotion.x;
4831         y=event.xmotion.y;
4832         if (windows->info.mapped != MagickFalse )
4833           {
4834             if ((x < (int) (windows->info.x+windows->info.width)) &&
4835                 (y < (int) (windows->info.y+windows->info.height)))
4836               (void) XWithdrawWindow(display,windows->info.id,
4837                 windows->info.screen);
4838           }
4839         else
4840           if ((x > (int) (windows->info.x+windows->info.width)) ||
4841               (y > (int) (windows->info.y+windows->info.height)))
4842             (void) XMapWindow(display,windows->info.id);
4843         crop_info.x=(ssize_t) windows->image.x+x;
4844         crop_info.y=(ssize_t) windows->image.y+y;
4845         break;
4846       }
4847       default:
4848         break;
4849     }
4850   } while ((state & ExitState) == 0);
4851   (void) XSelectInput(display,windows->image.id,
4852     windows->image.attributes.event_mask);
4853   if ((state & EscapeState) != 0)
4854     {
4855       /*
4856         User want to exit without cropping.
4857       */
4858       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4859       (void) XFreeCursor(display,cursor);
4860       return(MagickTrue);
4861     }
4862   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4863   do
4864   {
4865     /*
4866       Size rectangle as pointer moves until the mouse button is released.
4867     */
4868     x=(int) crop_info.x;
4869     y=(int) crop_info.y;
4870     crop_info.width=0;
4871     crop_info.height=0;
4872     state=DefaultState;
4873     do
4874     {
4875       highlight_info=crop_info;
4876       highlight_info.x=crop_info.x-windows->image.x;
4877       highlight_info.y=crop_info.y-windows->image.y;
4878       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4879         {
4880           /*
4881             Display info and draw cropping rectangle.
4882           */
4883           if (windows->info.mapped == MagickFalse)
4884             (void) XMapWindow(display,windows->info.id);
4885           (void) FormatLocaleString(text,MagickPathExtent,
4886             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4887             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4888           XInfoWidget(display,windows,text);
4889           XHighlightRectangle(display,windows->image.id,
4890             windows->image.highlight_context,&highlight_info);
4891         }
4892       else
4893         if (windows->info.mapped != MagickFalse )
4894           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4895       /*
4896         Wait for next event.
4897       */
4898       XScreenEvent(display,windows,&event,exception);
4899       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4900         XHighlightRectangle(display,windows->image.id,
4901           windows->image.highlight_context,&highlight_info);
4902       switch (event.type)
4903       {
4904         case ButtonPress:
4905         {
4906           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4907           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4908           break;
4909         }
4910         case ButtonRelease:
4911         {
4912           /*
4913             User has committed to cropping rectangle.
4914           */
4915           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4916           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4917           XSetCursorState(display,windows,MagickFalse);
4918           state|=ExitState;
4919           windows->command.data=0;
4920           (void) XCommandWidget(display,windows,RectifyModeMenu,
4921             (XEvent *) NULL);
4922           break;
4923         }
4924         case Expose:
4925           break;
4926         case MotionNotify:
4927         {
4928           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4929           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4930         }
4931         default:
4932           break;
4933       }
4934       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4935           ((state & ExitState) != 0))
4936         {
4937           /*
4938             Check boundary conditions.
4939           */
4940           if (crop_info.x < 0)
4941             crop_info.x=0;
4942           else
4943             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4944               crop_info.x=(ssize_t) windows->image.ximage->width;
4945           if ((int) crop_info.x < x)
4946             crop_info.width=(unsigned int) (x-crop_info.x);
4947           else
4948             {
4949               crop_info.width=(unsigned int) (crop_info.x-x);
4950               crop_info.x=(ssize_t) x;
4951             }
4952           if (crop_info.y < 0)
4953             crop_info.y=0;
4954           else
4955             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4956               crop_info.y=(ssize_t) windows->image.ximage->height;
4957           if ((int) crop_info.y < y)
4958             crop_info.height=(unsigned int) (y-crop_info.y);
4959           else
4960             {
4961               crop_info.height=(unsigned int) (crop_info.y-y);
4962               crop_info.y=(ssize_t) y;
4963             }
4964         }
4965     } while ((state & ExitState) == 0);
4966     /*
4967       Wait for user to grab a corner of the rectangle or press return.
4968     */
4969     state=DefaultState;
4970     (void) XMapWindow(display,windows->info.id);
4971     do
4972     {
4973       if (windows->info.mapped != MagickFalse )
4974         {
4975           /*
4976             Display pointer position.
4977           */
4978           (void) FormatLocaleString(text,MagickPathExtent,
4979             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4980             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4981           XInfoWidget(display,windows,text);
4982         }
4983       highlight_info=crop_info;
4984       highlight_info.x=crop_info.x-windows->image.x;
4985       highlight_info.y=crop_info.y-windows->image.y;
4986       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4987         {
4988           state|=EscapeState;
4989           state|=ExitState;
4990           break;
4991         }
4992       XHighlightRectangle(display,windows->image.id,
4993         windows->image.highlight_context,&highlight_info);
4994       XScreenEvent(display,windows,&event,exception);
4995       if (event.xany.window == windows->command.id)
4996         {
4997           /*
4998             Select a command from the Command widget.
4999           */
5000           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5001           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5002           (void) XSetFunction(display,windows->image.highlight_context,
5003             GXinvert);
5004           XHighlightRectangle(display,windows->image.id,
5005             windows->image.highlight_context,&highlight_info);
5006           if (id >= 0)
5007             switch (RectifyCommands[id])
5008             {
5009               case RectifyCopyCommand:
5010               {
5011                 state|=ExitState;
5012                 break;
5013               }
5014               case RectifyHelpCommand:
5015               {
5016                 (void) XSetFunction(display,windows->image.highlight_context,
5017                   GXcopy);
5018                 switch (mode)
5019                 {
5020                   case CopyMode:
5021                   {
5022                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5023                       "Help Viewer - Image Copy",ImageCopyHelp);
5024                     break;
5025                   }
5026                   case CropMode:
5027                   {
5028                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5029                       "Help Viewer - Image Crop",ImageCropHelp);
5030                     break;
5031                   }
5032                   case CutMode:
5033                   {
5034                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5035                       "Help Viewer - Image Cut",ImageCutHelp);
5036                     break;
5037                   }
5038                 }
5039                 (void) XSetFunction(display,windows->image.highlight_context,
5040                   GXinvert);
5041                 break;
5042               }
5043               case RectifyDismissCommand:
5044               {
5045                 /*
5046                   Prematurely exit.
5047                 */
5048                 state|=EscapeState;
5049                 state|=ExitState;
5050                 break;
5051               }
5052               default:
5053                 break;
5054             }
5055           continue;
5056         }
5057       XHighlightRectangle(display,windows->image.id,
5058         windows->image.highlight_context,&highlight_info);
5059       switch (event.type)
5060       {
5061         case ButtonPress:
5062         {
5063           if (event.xbutton.button != Button1)
5064             break;
5065           if (event.xbutton.window != windows->image.id)
5066             break;
5067           x=windows->image.x+event.xbutton.x;
5068           y=windows->image.y+event.xbutton.y;
5069           if ((x < (int) (crop_info.x+RoiDelta)) &&
5070               (x > (int) (crop_info.x-RoiDelta)) &&
5071               (y < (int) (crop_info.y+RoiDelta)) &&
5072               (y > (int) (crop_info.y-RoiDelta)))
5073             {
5074               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5075               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5076               state|=UpdateConfigurationState;
5077               break;
5078             }
5079           if ((x < (int) (crop_info.x+RoiDelta)) &&
5080               (x > (int) (crop_info.x-RoiDelta)) &&
5081               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5082               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5083             {
5084               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5085               state|=UpdateConfigurationState;
5086               break;
5087             }
5088           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5089               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5090               (y < (int) (crop_info.y+RoiDelta)) &&
5091               (y > (int) (crop_info.y-RoiDelta)))
5092             {
5093               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5094               state|=UpdateConfigurationState;
5095               break;
5096             }
5097           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5098               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5099               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5100               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5101             {
5102               state|=UpdateConfigurationState;
5103               break;
5104             }
5105         }
5106         case ButtonRelease:
5107         {
5108           if (event.xbutton.window == windows->pan.id)
5109             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5110                 (highlight_info.y != crop_info.y-windows->image.y))
5111               XHighlightRectangle(display,windows->image.id,
5112                 windows->image.highlight_context,&highlight_info);
5113           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5114             event.xbutton.time);
5115           break;
5116         }
5117         case Expose:
5118         {
5119           if (event.xexpose.window == windows->image.id)
5120             if (event.xexpose.count == 0)
5121               {
5122                 event.xexpose.x=(int) highlight_info.x;
5123                 event.xexpose.y=(int) highlight_info.y;
5124                 event.xexpose.width=(int) highlight_info.width;
5125                 event.xexpose.height=(int) highlight_info.height;
5126                 XRefreshWindow(display,&windows->image,&event);
5127               }
5128           if (event.xexpose.window == windows->info.id)
5129             if (event.xexpose.count == 0)
5130               XInfoWidget(display,windows,text);
5131           break;
5132         }
5133         case KeyPress:
5134         {
5135           if (event.xkey.window != windows->image.id)
5136             break;
5137           /*
5138             Respond to a user key press.
5139           */
5140           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5141             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5142           switch ((int) key_symbol)
5143           {
5144             case XK_Escape:
5145             case XK_F20:
5146               state|=EscapeState;
5147             case XK_Return:
5148             {
5149               state|=ExitState;
5150               break;
5151             }
5152             case XK_Home:
5153             case XK_KP_Home:
5154             {
5155               crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5156                 2L);
5157               crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5158                 2L);
5159               break;
5160             }
5161             case XK_Left:
5162             case XK_KP_Left:
5163             {
5164               crop_info.x--;
5165               break;
5166             }
5167             case XK_Up:
5168             case XK_KP_Up:
5169             case XK_Next:
5170             {
5171               crop_info.y--;
5172               break;
5173             }
5174             case XK_Right:
5175             case XK_KP_Right:
5176             {
5177               crop_info.x++;
5178               break;
5179             }
5180             case XK_Prior:
5181             case XK_Down:
5182             case XK_KP_Down:
5183             {
5184               crop_info.y++;
5185               break;
5186             }
5187             case XK_F1:
5188             case XK_Help:
5189             {
5190               (void) XSetFunction(display,windows->image.highlight_context,
5191                 GXcopy);
5192               switch (mode)
5193               {
5194                 case CopyMode:
5195                 {
5196                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5197                     "Help Viewer - Image Copy",ImageCopyHelp);
5198                   break;
5199                 }
5200                 case CropMode:
5201                 {
5202                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5203                     "Help Viewer - Image Cropg",ImageCropHelp);
5204                   break;
5205                 }
5206                 case CutMode:
5207                 {
5208                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5209                     "Help Viewer - Image Cutg",ImageCutHelp);
5210                   break;
5211                 }
5212               }
5213               (void) XSetFunction(display,windows->image.highlight_context,
5214                 GXinvert);
5215               break;
5216             }
5217             default:
5218             {
5219               (void) XBell(display,0);
5220               break;
5221             }
5222           }
5223           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5224             event.xkey.time);
5225           break;
5226         }
5227         case KeyRelease:
5228           break;
5229         case MotionNotify:
5230         {
5231           if (event.xmotion.window != windows->image.id)
5232             break;
5233           /*
5234             Map and unmap Info widget as text cursor crosses its boundaries.
5235           */
5236           x=event.xmotion.x;
5237           y=event.xmotion.y;
5238           if (windows->info.mapped != MagickFalse )
5239             {
5240               if ((x < (int) (windows->info.x+windows->info.width)) &&
5241                   (y < (int) (windows->info.y+windows->info.height)))
5242                 (void) XWithdrawWindow(display,windows->info.id,
5243                   windows->info.screen);
5244             }
5245           else
5246             if ((x > (int) (windows->info.x+windows->info.width)) ||
5247                 (y > (int) (windows->info.y+windows->info.height)))
5248               (void) XMapWindow(display,windows->info.id);
5249           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5250           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5251           break;
5252         }
5253         case SelectionRequest:
5254         {
5255           XSelectionEvent
5256             notify;
5257 
5258           XSelectionRequestEvent
5259             *request;
5260 
5261           /*
5262             Set primary selection.
5263           */
5264           (void) FormatLocaleString(text,MagickPathExtent,
5265             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5266             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5267           request=(&(event.xselectionrequest));
5268           (void) XChangeProperty(request->display,request->requestor,
5269             request->property,request->target,8,PropModeReplace,
5270             (unsigned char *) text,(int) strlen(text));
5271           notify.type=SelectionNotify;
5272           notify.display=request->display;
5273           notify.requestor=request->requestor;
5274           notify.selection=request->selection;
5275           notify.target=request->target;
5276           notify.time=request->time;
5277           if (request->property == None)
5278             notify.property=request->target;
5279           else
5280             notify.property=request->property;
5281           (void) XSendEvent(request->display,request->requestor,False,0,
5282             (XEvent *) &notify);
5283         }
5284         default:
5285           break;
5286       }
5287       if ((state & UpdateConfigurationState) != 0)
5288         {
5289           (void) XPutBackEvent(display,&event);
5290           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5291           break;
5292         }
5293     } while ((state & ExitState) == 0);
5294   } while ((state & ExitState) == 0);
5295   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5296   XSetCursorState(display,windows,MagickFalse);
5297   if ((state & EscapeState) != 0)
5298     return(MagickTrue);
5299   if (mode == CropMode)
5300     if (((int) crop_info.width != windows->image.ximage->width) ||
5301         ((int) crop_info.height != windows->image.ximage->height))
5302       {
5303         /*
5304           Reconfigure Image window as defined by cropping rectangle.
5305         */
5306         XSetCropGeometry(display,windows,&crop_info,image);
5307         windows->image.window_changes.width=(int) crop_info.width;
5308         windows->image.window_changes.height=(int) crop_info.height;
5309         (void) XConfigureImage(display,resource_info,windows,image,exception);
5310         return(MagickTrue);
5311       }
5312   /*
5313     Copy image before applying image transforms.
5314   */
5315   XSetCursorState(display,windows,MagickTrue);
5316   XCheckRefreshWindows(display,windows);
5317   width=(unsigned int) image->columns;
5318   height=(unsigned int) image->rows;
5319   x=0;
5320   y=0;
5321   if (windows->image.crop_geometry != (char *) NULL)
5322     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5323   scale_factor=(double) width/windows->image.ximage->width;
5324   crop_info.x+=x;
5325   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5326   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5327   scale_factor=(double) height/windows->image.ximage->height;
5328   crop_info.y+=y;
5329   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5330   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5331   crop_image=CropImage(image,&crop_info,exception);
5332   XSetCursorState(display,windows,MagickFalse);
5333   if (crop_image == (Image *) NULL)
5334     return(MagickFalse);
5335   if (resource_info->copy_image != (Image *) NULL)
5336     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5337   resource_info->copy_image=crop_image;
5338   if (mode == CopyMode)
5339     {
5340       (void) XConfigureImage(display,resource_info,windows,image,exception);
5341       return(MagickTrue);
5342     }
5343   /*
5344     Cut image.
5345   */
5346   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5347     return(MagickFalse);
5348   image->alpha_trait=BlendPixelTrait;
5349   image_view=AcquireAuthenticCacheView(image,exception);
5350   for (y=0; y < (int) crop_info.height; y++)
5351   {
5352     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5353       crop_info.width,1,exception);
5354     if (q == (Quantum *) NULL)
5355       break;
5356     for (x=0; x < (int) crop_info.width; x++)
5357     {
5358       SetPixelAlpha(image,TransparentAlpha,q);
5359       q+=GetPixelChannels(image);
5360     }
5361     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5362       break;
5363   }
5364   image_view=DestroyCacheView(image_view);
5365   /*
5366     Update image configuration.
5367   */
5368   XConfigureImageColormap(display,resource_info,windows,image,exception);
5369   (void) XConfigureImage(display,resource_info,windows,image,exception);
5370   return(MagickTrue);
5371 }
5372 
5373 /*
5374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5375 %                                                                             %
5376 %                                                                             %
5377 %                                                                             %
5378 +   X D r a w I m a g e                                                       %
5379 %                                                                             %
5380 %                                                                             %
5381 %                                                                             %
5382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5383 %
5384 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5385 %  the image.
5386 %
5387 %  The format of the XDrawEditImage method is:
5388 %
5389 %      MagickBooleanType XDrawEditImage(Display *display,
5390 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5391 %        ExceptionInfo *exception)
5392 %
5393 %  A description of each parameter follows:
5394 %
5395 %    o display: Specifies a connection to an X server; returned from
5396 %      XOpenDisplay.
5397 %
5398 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5399 %
5400 %    o windows: Specifies a pointer to a XWindows structure.
5401 %
5402 %    o image: the image.
5403 %
5404 %    o exception: return any errors or warnings in this structure.
5405 %
5406 */
XDrawEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)5407 static MagickBooleanType XDrawEditImage(Display *display,
5408   XResourceInfo *resource_info,XWindows *windows,Image **image,
5409   ExceptionInfo *exception)
5410 {
5411   static const char
5412     *DrawMenu[] =
5413     {
5414       "Element",
5415       "Color",
5416       "Stipple",
5417       "Width",
5418       "Undo",
5419       "Help",
5420       "Dismiss",
5421       (char *) NULL
5422     };
5423 
5424   static ElementType
5425     element = PointElement;
5426 
5427   static const ModeType
5428     DrawCommands[] =
5429     {
5430       DrawElementCommand,
5431       DrawColorCommand,
5432       DrawStippleCommand,
5433       DrawWidthCommand,
5434       DrawUndoCommand,
5435       DrawHelpCommand,
5436       DrawDismissCommand
5437     };
5438 
5439   static Pixmap
5440     stipple = (Pixmap) NULL;
5441 
5442   static unsigned int
5443     pen_id = 0,
5444     line_width = 1;
5445 
5446   char
5447     command[MagickPathExtent],
5448     text[MagickPathExtent];
5449 
5450   Cursor
5451     cursor;
5452 
5453   int
5454     entry,
5455     id,
5456     number_coordinates,
5457     x,
5458     y;
5459 
5460   double
5461     degrees;
5462 
5463   MagickStatusType
5464     status;
5465 
5466   RectangleInfo
5467     rectangle_info;
5468 
5469   register int
5470     i;
5471 
5472   unsigned int
5473     distance,
5474     height,
5475     max_coordinates,
5476     width;
5477 
5478   size_t
5479     state;
5480 
5481   Window
5482     root_window;
5483 
5484   XDrawInfo
5485     draw_info;
5486 
5487   XEvent
5488     event;
5489 
5490   XPoint
5491     *coordinate_info;
5492 
5493   XSegment
5494     line_info;
5495 
5496   /*
5497     Allocate polygon info.
5498   */
5499   max_coordinates=2048;
5500   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5501     sizeof(*coordinate_info));
5502   if (coordinate_info == (XPoint *) NULL)
5503     {
5504       (void) ThrowMagickException(exception,GetMagickModule(),
5505         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5506       return(MagickFalse);
5507     }
5508   /*
5509     Map Command widget.
5510   */
5511   (void) CloneString(&windows->command.name,"Draw");
5512   windows->command.data=4;
5513   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5514   (void) XMapRaised(display,windows->command.id);
5515   XClientMessage(display,windows->image.id,windows->im_protocols,
5516     windows->im_update_widget,CurrentTime);
5517   /*
5518     Wait for first button press.
5519   */
5520   root_window=XRootWindow(display,XDefaultScreen(display));
5521   draw_info.stencil=OpaqueStencil;
5522   status=MagickTrue;
5523   cursor=XCreateFontCursor(display,XC_tcross);
5524   for ( ; ; )
5525   {
5526     XQueryPosition(display,windows->image.id,&x,&y);
5527     (void) XSelectInput(display,windows->image.id,
5528       windows->image.attributes.event_mask | PointerMotionMask);
5529     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5530     state=DefaultState;
5531     do
5532     {
5533       if (windows->info.mapped != MagickFalse )
5534         {
5535           /*
5536             Display pointer position.
5537           */
5538           (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5539             x+windows->image.x,y+windows->image.y);
5540           XInfoWidget(display,windows,text);
5541         }
5542       /*
5543         Wait for next event.
5544       */
5545       XScreenEvent(display,windows,&event,exception);
5546       if (event.xany.window == windows->command.id)
5547         {
5548           /*
5549             Select a command from the Command widget.
5550           */
5551           id=XCommandWidget(display,windows,DrawMenu,&event);
5552           if (id < 0)
5553             continue;
5554           switch (DrawCommands[id])
5555           {
5556             case DrawElementCommand:
5557             {
5558               static const char
5559                 *Elements[] =
5560                 {
5561                   "point",
5562                   "line",
5563                   "rectangle",
5564                   "fill rectangle",
5565                   "circle",
5566                   "fill circle",
5567                   "ellipse",
5568                   "fill ellipse",
5569                   "polygon",
5570                   "fill polygon",
5571                   (char *) NULL,
5572                 };
5573 
5574               /*
5575                 Select a command from the pop-up menu.
5576               */
5577               element=(ElementType) (XMenuWidget(display,windows,
5578                 DrawMenu[id],Elements,command)+1);
5579               break;
5580             }
5581             case DrawColorCommand:
5582             {
5583               const char
5584                 *ColorMenu[MaxNumberPens+1];
5585 
5586               int
5587                 pen_number;
5588 
5589               MagickBooleanType
5590                 transparent;
5591 
5592               XColor
5593                 color;
5594 
5595               /*
5596                 Initialize menu selections.
5597               */
5598               for (i=0; i < (int) (MaxNumberPens-2); i++)
5599                 ColorMenu[i]=resource_info->pen_colors[i];
5600               ColorMenu[MaxNumberPens-2]="transparent";
5601               ColorMenu[MaxNumberPens-1]="Browser...";
5602               ColorMenu[MaxNumberPens]=(char *) NULL;
5603               /*
5604                 Select a pen color from the pop-up menu.
5605               */
5606               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5607                 (const char **) ColorMenu,command);
5608               if (pen_number < 0)
5609                 break;
5610               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5611                 MagickFalse;
5612               if (transparent != MagickFalse )
5613                 {
5614                   draw_info.stencil=TransparentStencil;
5615                   break;
5616                 }
5617               if (pen_number == (MaxNumberPens-1))
5618                 {
5619                   static char
5620                     color_name[MagickPathExtent] = "gray";
5621 
5622                   /*
5623                     Select a pen color from a dialog.
5624                   */
5625                   resource_info->pen_colors[pen_number]=color_name;
5626                   XColorBrowserWidget(display,windows,"Select",color_name);
5627                   if (*color_name == '\0')
5628                     break;
5629                 }
5630               /*
5631                 Set pen color.
5632               */
5633               (void) XParseColor(display,windows->map_info->colormap,
5634                 resource_info->pen_colors[pen_number],&color);
5635               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5636                 (unsigned int) MaxColors,&color);
5637               windows->pixel_info->pen_colors[pen_number]=color;
5638               pen_id=(unsigned int) pen_number;
5639               draw_info.stencil=OpaqueStencil;
5640               break;
5641             }
5642             case DrawStippleCommand:
5643             {
5644               Image
5645                 *stipple_image;
5646 
5647               ImageInfo
5648                 *image_info;
5649 
5650               int
5651                 status;
5652 
5653               static char
5654                 filename[MagickPathExtent] = "\0";
5655 
5656               static const char
5657                 *StipplesMenu[] =
5658                 {
5659                   "Brick",
5660                   "Diagonal",
5661                   "Scales",
5662                   "Vertical",
5663                   "Wavy",
5664                   "Translucent",
5665                   "Opaque",
5666                   (char *) NULL,
5667                   (char *) NULL,
5668                 };
5669 
5670               /*
5671                 Select a command from the pop-up menu.
5672               */
5673               StipplesMenu[7]="Open...";
5674               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5675                 command);
5676               if (entry < 0)
5677                 break;
5678               if (stipple != (Pixmap) NULL)
5679                 (void) XFreePixmap(display,stipple);
5680               stipple=(Pixmap) NULL;
5681               if (entry != 7)
5682                 {
5683                   switch (entry)
5684                   {
5685                     case 0:
5686                     {
5687                       stipple=XCreateBitmapFromData(display,root_window,
5688                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5689                       break;
5690                     }
5691                     case 1:
5692                     {
5693                       stipple=XCreateBitmapFromData(display,root_window,
5694                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5695                       break;
5696                     }
5697                     case 2:
5698                     {
5699                       stipple=XCreateBitmapFromData(display,root_window,
5700                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5701                       break;
5702                     }
5703                     case 3:
5704                     {
5705                       stipple=XCreateBitmapFromData(display,root_window,
5706                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5707                       break;
5708                     }
5709                     case 4:
5710                     {
5711                       stipple=XCreateBitmapFromData(display,root_window,
5712                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5713                       break;
5714                     }
5715                     case 5:
5716                     {
5717                       stipple=XCreateBitmapFromData(display,root_window,
5718                         (char *) HighlightBitmap,HighlightWidth,
5719                         HighlightHeight);
5720                       break;
5721                     }
5722                     case 6:
5723                     default:
5724                     {
5725                       stipple=XCreateBitmapFromData(display,root_window,
5726                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5727                       break;
5728                     }
5729                   }
5730                   break;
5731                 }
5732               XFileBrowserWidget(display,windows,"Stipple",filename);
5733               if (*filename == '\0')
5734                 break;
5735               /*
5736                 Read image.
5737               */
5738               XSetCursorState(display,windows,MagickTrue);
5739               XCheckRefreshWindows(display,windows);
5740               image_info=AcquireImageInfo();
5741               (void) CopyMagickString(image_info->filename,filename,
5742                 MagickPathExtent);
5743               stipple_image=ReadImage(image_info,exception);
5744               CatchException(exception);
5745               XSetCursorState(display,windows,MagickFalse);
5746               if (stipple_image == (Image *) NULL)
5747                 break;
5748               (void) AcquireUniqueFileResource(filename);
5749               (void) FormatLocaleString(stipple_image->filename,MagickPathExtent,
5750                 "xbm:%s",filename);
5751               (void) WriteImage(image_info,stipple_image,exception);
5752               stipple_image=DestroyImage(stipple_image);
5753               image_info=DestroyImageInfo(image_info);
5754               status=XReadBitmapFile(display,root_window,filename,&width,
5755                 &height,&stipple,&x,&y);
5756               (void) RelinquishUniqueFileResource(filename);
5757               if ((status != BitmapSuccess) != 0)
5758                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5759                   filename);
5760               break;
5761             }
5762             case DrawWidthCommand:
5763             {
5764               static char
5765                 width[MagickPathExtent] = "0";
5766 
5767               static const char
5768                 *WidthsMenu[] =
5769                 {
5770                   "1",
5771                   "2",
5772                   "4",
5773                   "8",
5774                   "16",
5775                   "Dialog...",
5776                   (char *) NULL,
5777                 };
5778 
5779               /*
5780                 Select a command from the pop-up menu.
5781               */
5782               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5783                 command);
5784               if (entry < 0)
5785                 break;
5786               if (entry != 5)
5787                 {
5788                   line_width=(unsigned int) StringToUnsignedLong(
5789                     WidthsMenu[entry]);
5790                   break;
5791                 }
5792               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5793                 width);
5794               if (*width == '\0')
5795                 break;
5796               line_width=(unsigned int) StringToUnsignedLong(width);
5797               break;
5798             }
5799             case DrawUndoCommand:
5800             {
5801               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5802                 image,exception);
5803               break;
5804             }
5805             case DrawHelpCommand:
5806             {
5807               XTextViewWidget(display,resource_info,windows,MagickFalse,
5808                 "Help Viewer - Image Rotation",ImageDrawHelp);
5809               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5810               break;
5811             }
5812             case DrawDismissCommand:
5813             {
5814               /*
5815                 Prematurely exit.
5816               */
5817               state|=EscapeState;
5818               state|=ExitState;
5819               break;
5820             }
5821             default:
5822               break;
5823           }
5824           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5825           continue;
5826         }
5827       switch (event.type)
5828       {
5829         case ButtonPress:
5830         {
5831           if (event.xbutton.button != Button1)
5832             break;
5833           if (event.xbutton.window != windows->image.id)
5834             break;
5835           /*
5836             exit loop.
5837           */
5838           x=event.xbutton.x;
5839           y=event.xbutton.y;
5840           state|=ExitState;
5841           break;
5842         }
5843         case ButtonRelease:
5844           break;
5845         case Expose:
5846           break;
5847         case KeyPress:
5848         {
5849           KeySym
5850             key_symbol;
5851 
5852           if (event.xkey.window != windows->image.id)
5853             break;
5854           /*
5855             Respond to a user key press.
5856           */
5857           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5858             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5859           switch ((int) key_symbol)
5860           {
5861             case XK_Escape:
5862             case XK_F20:
5863             {
5864               /*
5865                 Prematurely exit.
5866               */
5867               state|=EscapeState;
5868               state|=ExitState;
5869               break;
5870             }
5871             case XK_F1:
5872             case XK_Help:
5873             {
5874               XTextViewWidget(display,resource_info,windows,MagickFalse,
5875                 "Help Viewer - Image Rotation",ImageDrawHelp);
5876               break;
5877             }
5878             default:
5879             {
5880               (void) XBell(display,0);
5881               break;
5882             }
5883           }
5884           break;
5885         }
5886         case MotionNotify:
5887         {
5888           /*
5889             Map and unmap Info widget as text cursor crosses its boundaries.
5890           */
5891           x=event.xmotion.x;
5892           y=event.xmotion.y;
5893           if (windows->info.mapped != MagickFalse )
5894             {
5895               if ((x < (int) (windows->info.x+windows->info.width)) &&
5896                   (y < (int) (windows->info.y+windows->info.height)))
5897                 (void) XWithdrawWindow(display,windows->info.id,
5898                   windows->info.screen);
5899             }
5900           else
5901             if ((x > (int) (windows->info.x+windows->info.width)) ||
5902                 (y > (int) (windows->info.y+windows->info.height)))
5903               (void) XMapWindow(display,windows->info.id);
5904           break;
5905         }
5906       }
5907     } while ((state & ExitState) == 0);
5908     (void) XSelectInput(display,windows->image.id,
5909       windows->image.attributes.event_mask);
5910     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5911     if ((state & EscapeState) != 0)
5912       break;
5913     /*
5914       Draw element as pointer moves until the button is released.
5915     */
5916     distance=0;
5917     degrees=0.0;
5918     line_info.x1=x;
5919     line_info.y1=y;
5920     line_info.x2=x;
5921     line_info.y2=y;
5922     rectangle_info.x=(ssize_t) x;
5923     rectangle_info.y=(ssize_t) y;
5924     rectangle_info.width=0;
5925     rectangle_info.height=0;
5926     number_coordinates=1;
5927     coordinate_info->x=x;
5928     coordinate_info->y=y;
5929     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5930     state=DefaultState;
5931     do
5932     {
5933       switch (element)
5934       {
5935         case PointElement:
5936         default:
5937         {
5938           if (number_coordinates > 1)
5939             {
5940               (void) XDrawLines(display,windows->image.id,
5941                 windows->image.highlight_context,coordinate_info,
5942                 number_coordinates,CoordModeOrigin);
5943               (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5944                 coordinate_info[number_coordinates-1].x,
5945                 coordinate_info[number_coordinates-1].y);
5946               XInfoWidget(display,windows,text);
5947             }
5948           break;
5949         }
5950         case LineElement:
5951         {
5952           if (distance > 9)
5953             {
5954               /*
5955                 Display angle of the line.
5956               */
5957               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5958                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5959               (void) FormatLocaleString(text,MagickPathExtent," %g",
5960                 (double) degrees);
5961               XInfoWidget(display,windows,text);
5962               XHighlightLine(display,windows->image.id,
5963                 windows->image.highlight_context,&line_info);
5964             }
5965           else
5966             if (windows->info.mapped != MagickFalse )
5967               (void) XWithdrawWindow(display,windows->info.id,
5968                 windows->info.screen);
5969           break;
5970         }
5971         case RectangleElement:
5972         case FillRectangleElement:
5973         {
5974           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5975             {
5976               /*
5977                 Display info and draw drawing rectangle.
5978               */
5979               (void) FormatLocaleString(text,MagickPathExtent,
5980                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5981                 (double) rectangle_info.height,(double) rectangle_info.x,
5982                 (double) rectangle_info.y);
5983               XInfoWidget(display,windows,text);
5984               XHighlightRectangle(display,windows->image.id,
5985                 windows->image.highlight_context,&rectangle_info);
5986             }
5987           else
5988             if (windows->info.mapped != MagickFalse )
5989               (void) XWithdrawWindow(display,windows->info.id,
5990                 windows->info.screen);
5991           break;
5992         }
5993         case CircleElement:
5994         case FillCircleElement:
5995         case EllipseElement:
5996         case FillEllipseElement:
5997         {
5998           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5999             {
6000               /*
6001                 Display info and draw drawing rectangle.
6002               */
6003               (void) FormatLocaleString(text,MagickPathExtent,
6004                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6005                 (double) rectangle_info.height,(double) rectangle_info.x,
6006                 (double) rectangle_info.y);
6007               XInfoWidget(display,windows,text);
6008               XHighlightEllipse(display,windows->image.id,
6009                 windows->image.highlight_context,&rectangle_info);
6010             }
6011           else
6012             if (windows->info.mapped != MagickFalse )
6013               (void) XWithdrawWindow(display,windows->info.id,
6014                 windows->info.screen);
6015           break;
6016         }
6017         case PolygonElement:
6018         case FillPolygonElement:
6019         {
6020           if (number_coordinates > 1)
6021             (void) XDrawLines(display,windows->image.id,
6022               windows->image.highlight_context,coordinate_info,
6023               number_coordinates,CoordModeOrigin);
6024           if (distance > 9)
6025             {
6026               /*
6027                 Display angle of the line.
6028               */
6029               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6030                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6031               (void) FormatLocaleString(text,MagickPathExtent," %g",
6032                 (double) degrees);
6033               XInfoWidget(display,windows,text);
6034               XHighlightLine(display,windows->image.id,
6035                 windows->image.highlight_context,&line_info);
6036             }
6037           else
6038             if (windows->info.mapped != MagickFalse )
6039               (void) XWithdrawWindow(display,windows->info.id,
6040                 windows->info.screen);
6041           break;
6042         }
6043       }
6044       /*
6045         Wait for next event.
6046       */
6047       XScreenEvent(display,windows,&event,exception);
6048       switch (element)
6049       {
6050         case PointElement:
6051         default:
6052         {
6053           if (number_coordinates > 1)
6054             (void) XDrawLines(display,windows->image.id,
6055               windows->image.highlight_context,coordinate_info,
6056               number_coordinates,CoordModeOrigin);
6057           break;
6058         }
6059         case LineElement:
6060         {
6061           if (distance > 9)
6062             XHighlightLine(display,windows->image.id,
6063               windows->image.highlight_context,&line_info);
6064           break;
6065         }
6066         case RectangleElement:
6067         case FillRectangleElement:
6068         {
6069           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6070             XHighlightRectangle(display,windows->image.id,
6071               windows->image.highlight_context,&rectangle_info);
6072           break;
6073         }
6074         case CircleElement:
6075         case FillCircleElement:
6076         case EllipseElement:
6077         case FillEllipseElement:
6078         {
6079           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6080             XHighlightEllipse(display,windows->image.id,
6081               windows->image.highlight_context,&rectangle_info);
6082           break;
6083         }
6084         case PolygonElement:
6085         case FillPolygonElement:
6086         {
6087           if (number_coordinates > 1)
6088             (void) XDrawLines(display,windows->image.id,
6089               windows->image.highlight_context,coordinate_info,
6090               number_coordinates,CoordModeOrigin);
6091           if (distance > 9)
6092             XHighlightLine(display,windows->image.id,
6093               windows->image.highlight_context,&line_info);
6094           break;
6095         }
6096       }
6097       switch (event.type)
6098       {
6099         case ButtonPress:
6100           break;
6101         case ButtonRelease:
6102         {
6103           /*
6104             User has committed to element.
6105           */
6106           line_info.x2=event.xbutton.x;
6107           line_info.y2=event.xbutton.y;
6108           rectangle_info.x=(ssize_t) event.xbutton.x;
6109           rectangle_info.y=(ssize_t) event.xbutton.y;
6110           coordinate_info[number_coordinates].x=event.xbutton.x;
6111           coordinate_info[number_coordinates].y=event.xbutton.y;
6112           if (((element != PolygonElement) &&
6113                (element != FillPolygonElement)) || (distance <= 9))
6114             {
6115               state|=ExitState;
6116               break;
6117             }
6118           number_coordinates++;
6119           if (number_coordinates < (int) max_coordinates)
6120             {
6121               line_info.x1=event.xbutton.x;
6122               line_info.y1=event.xbutton.y;
6123               break;
6124             }
6125           max_coordinates<<=1;
6126           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6127             max_coordinates,sizeof(*coordinate_info));
6128           if (coordinate_info == (XPoint *) NULL)
6129             (void) ThrowMagickException(exception,GetMagickModule(),
6130               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6131           break;
6132         }
6133         case Expose:
6134           break;
6135         case MotionNotify:
6136         {
6137           if (event.xmotion.window != windows->image.id)
6138             break;
6139           if (element != PointElement)
6140             {
6141               line_info.x2=event.xmotion.x;
6142               line_info.y2=event.xmotion.y;
6143               rectangle_info.x=(ssize_t) event.xmotion.x;
6144               rectangle_info.y=(ssize_t) event.xmotion.y;
6145               break;
6146             }
6147           coordinate_info[number_coordinates].x=event.xbutton.x;
6148           coordinate_info[number_coordinates].y=event.xbutton.y;
6149           number_coordinates++;
6150           if (number_coordinates < (int) max_coordinates)
6151             break;
6152           max_coordinates<<=1;
6153           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6154             max_coordinates,sizeof(*coordinate_info));
6155           if (coordinate_info == (XPoint *) NULL)
6156             (void) ThrowMagickException(exception,GetMagickModule(),
6157               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6158           break;
6159         }
6160         default:
6161           break;
6162       }
6163       /*
6164         Check boundary conditions.
6165       */
6166       if (line_info.x2 < 0)
6167         line_info.x2=0;
6168       else
6169         if (line_info.x2 > (int) windows->image.width)
6170           line_info.x2=(short) windows->image.width;
6171       if (line_info.y2 < 0)
6172         line_info.y2=0;
6173       else
6174         if (line_info.y2 > (int) windows->image.height)
6175           line_info.y2=(short) windows->image.height;
6176       distance=(unsigned int)
6177         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6178          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6179       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6180           ((state & ExitState) != 0))
6181         {
6182           if (rectangle_info.x < 0)
6183             rectangle_info.x=0;
6184           else
6185             if (rectangle_info.x > (ssize_t) windows->image.width)
6186               rectangle_info.x=(ssize_t) windows->image.width;
6187           if ((int) rectangle_info.x < x)
6188             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6189           else
6190             {
6191               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6192               rectangle_info.x=(ssize_t) x;
6193             }
6194           if (rectangle_info.y < 0)
6195             rectangle_info.y=0;
6196           else
6197             if (rectangle_info.y > (ssize_t) windows->image.height)
6198               rectangle_info.y=(ssize_t) windows->image.height;
6199           if ((int) rectangle_info.y < y)
6200             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6201           else
6202             {
6203               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6204               rectangle_info.y=(ssize_t) y;
6205             }
6206         }
6207     } while ((state & ExitState) == 0);
6208     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6209     if ((element == PointElement) || (element == PolygonElement) ||
6210         (element == FillPolygonElement))
6211       {
6212         /*
6213           Determine polygon bounding box.
6214         */
6215         rectangle_info.x=(ssize_t) coordinate_info->x;
6216         rectangle_info.y=(ssize_t) coordinate_info->y;
6217         x=coordinate_info->x;
6218         y=coordinate_info->y;
6219         for (i=1; i < number_coordinates; i++)
6220         {
6221           if (coordinate_info[i].x > x)
6222             x=coordinate_info[i].x;
6223           if (coordinate_info[i].y > y)
6224             y=coordinate_info[i].y;
6225           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6226             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6227           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6228             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6229         }
6230         rectangle_info.width=(size_t) (x-rectangle_info.x);
6231         rectangle_info.height=(size_t) (y-rectangle_info.y);
6232         for (i=0; i < number_coordinates; i++)
6233         {
6234           coordinate_info[i].x-=rectangle_info.x;
6235           coordinate_info[i].y-=rectangle_info.y;
6236         }
6237       }
6238     else
6239       if (distance <= 9)
6240         continue;
6241       else
6242         if ((element == RectangleElement) ||
6243             (element == CircleElement) || (element == EllipseElement))
6244           {
6245             rectangle_info.width--;
6246             rectangle_info.height--;
6247           }
6248     /*
6249       Drawing is relative to image configuration.
6250     */
6251     draw_info.x=(int) rectangle_info.x;
6252     draw_info.y=(int) rectangle_info.y;
6253     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6254       image,exception);
6255     width=(unsigned int) (*image)->columns;
6256     height=(unsigned int) (*image)->rows;
6257     x=0;
6258     y=0;
6259     if (windows->image.crop_geometry != (char *) NULL)
6260       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6261     draw_info.x+=windows->image.x-(line_width/2);
6262     if (draw_info.x < 0)
6263       draw_info.x=0;
6264     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6265     draw_info.y+=windows->image.y-(line_width/2);
6266     if (draw_info.y < 0)
6267       draw_info.y=0;
6268     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6269     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6270     if (draw_info.width > (unsigned int) (*image)->columns)
6271       draw_info.width=(unsigned int) (*image)->columns;
6272     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6273     if (draw_info.height > (unsigned int) (*image)->rows)
6274       draw_info.height=(unsigned int) (*image)->rows;
6275     (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6276       width*draw_info.width/windows->image.ximage->width,
6277       height*draw_info.height/windows->image.ximage->height,
6278       draw_info.x+x,draw_info.y+y);
6279     /*
6280       Initialize drawing attributes.
6281     */
6282     draw_info.degrees=0.0;
6283     draw_info.element=element;
6284     draw_info.stipple=stipple;
6285     draw_info.line_width=line_width;
6286     draw_info.line_info=line_info;
6287     if (line_info.x1 > (int) (line_width/2))
6288       draw_info.line_info.x1=(short) line_width/2;
6289     if (line_info.y1 > (int) (line_width/2))
6290       draw_info.line_info.y1=(short) line_width/2;
6291     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6292     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6293     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6294       {
6295         draw_info.line_info.x2=(-draw_info.line_info.x2);
6296         draw_info.line_info.y2=(-draw_info.line_info.y2);
6297       }
6298     if (draw_info.line_info.x2 < 0)
6299       {
6300         draw_info.line_info.x2=(-draw_info.line_info.x2);
6301         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6302       }
6303     if (draw_info.line_info.y2 < 0)
6304       {
6305         draw_info.line_info.y2=(-draw_info.line_info.y2);
6306         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6307       }
6308     draw_info.rectangle_info=rectangle_info;
6309     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6310       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6311     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6312       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6313     draw_info.number_coordinates=(unsigned int) number_coordinates;
6314     draw_info.coordinate_info=coordinate_info;
6315     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6316     /*
6317       Draw element on image.
6318     */
6319     XSetCursorState(display,windows,MagickTrue);
6320     XCheckRefreshWindows(display,windows);
6321     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6322     XSetCursorState(display,windows,MagickFalse);
6323     /*
6324       Update image colormap and return to image drawing.
6325     */
6326     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6327     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6328   }
6329   XSetCursorState(display,windows,MagickFalse);
6330   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6331   return(status != 0 ? MagickTrue : MagickFalse);
6332 }
6333 
6334 /*
6335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6336 %                                                                             %
6337 %                                                                             %
6338 %                                                                             %
6339 +   X D r a w P a n R e c t a n g l e                                         %
6340 %                                                                             %
6341 %                                                                             %
6342 %                                                                             %
6343 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6344 %
6345 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6346 %  displays a zoom image and the rectangle shows which portion of the image is
6347 %  displayed in the Image window.
6348 %
6349 %  The format of the XDrawPanRectangle method is:
6350 %
6351 %      XDrawPanRectangle(Display *display,XWindows *windows)
6352 %
6353 %  A description of each parameter follows:
6354 %
6355 %    o display: Specifies a connection to an X server;  returned from
6356 %      XOpenDisplay.
6357 %
6358 %    o windows: Specifies a pointer to a XWindows structure.
6359 %
6360 */
XDrawPanRectangle(Display * display,XWindows * windows)6361 static void XDrawPanRectangle(Display *display,XWindows *windows)
6362 {
6363   double
6364     scale_factor;
6365 
6366   RectangleInfo
6367     highlight_info;
6368 
6369   /*
6370     Determine dimensions of the panning rectangle.
6371   */
6372   scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6373   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6374   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6375   scale_factor=(double)
6376     windows->pan.height/windows->image.ximage->height;
6377   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6378   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6379   /*
6380     Display the panning rectangle.
6381   */
6382   (void) XClearWindow(display,windows->pan.id);
6383   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6384     &highlight_info);
6385 }
6386 
6387 /*
6388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6389 %                                                                             %
6390 %                                                                             %
6391 %                                                                             %
6392 +   X I m a g e C a c h e                                                     %
6393 %                                                                             %
6394 %                                                                             %
6395 %                                                                             %
6396 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6397 %
6398 %  XImageCache() handles the creation, manipulation, and destruction of the
6399 %  image cache (undo and redo buffers).
6400 %
6401 %  The format of the XImageCache method is:
6402 %
6403 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6404 %        XWindows *windows,const CommandType command,Image **image,
6405 %        ExceptionInfo *exception)
6406 %
6407 %  A description of each parameter follows:
6408 %
6409 %    o display: Specifies a connection to an X server; returned from
6410 %      XOpenDisplay.
6411 %
6412 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6413 %
6414 %    o windows: Specifies a pointer to a XWindows structure.
6415 %
6416 %    o command: Specifies a command to perform.
6417 %
6418 %    o image: the image;  XImageCache may transform the image and return a new
6419 %      image pointer.
6420 %
6421 %    o exception: return any errors or warnings in this structure.
6422 %
6423 */
XImageCache(Display * display,XResourceInfo * resource_info,XWindows * windows,const CommandType command,Image ** image,ExceptionInfo * exception)6424 static void XImageCache(Display *display,XResourceInfo *resource_info,
6425   XWindows *windows,const CommandType command,Image **image,
6426   ExceptionInfo *exception)
6427 {
6428   Image
6429     *cache_image;
6430 
6431   static Image
6432     *redo_image = (Image *) NULL,
6433     *undo_image = (Image *) NULL;
6434 
6435   switch (command)
6436   {
6437     case FreeBuffersCommand:
6438     {
6439       /*
6440         Free memory from the undo and redo cache.
6441       */
6442       while (undo_image != (Image *) NULL)
6443       {
6444         cache_image=undo_image;
6445         undo_image=GetPreviousImageInList(undo_image);
6446         cache_image->list=DestroyImage(cache_image->list);
6447         cache_image=DestroyImage(cache_image);
6448       }
6449       undo_image=NewImageList();
6450       if (redo_image != (Image *) NULL)
6451         redo_image=DestroyImage(redo_image);
6452       redo_image=NewImageList();
6453       return;
6454     }
6455     case UndoCommand:
6456     {
6457       char
6458         image_geometry[MagickPathExtent];
6459 
6460       /*
6461         Undo the last image transformation.
6462       */
6463       if (undo_image == (Image *) NULL)
6464         {
6465           (void) XBell(display,0);
6466           return;
6467         }
6468       cache_image=undo_image;
6469       undo_image=GetPreviousImageInList(undo_image);
6470       windows->image.window_changes.width=(int) cache_image->columns;
6471       windows->image.window_changes.height=(int) cache_image->rows;
6472       (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6473         windows->image.ximage->width,windows->image.ximage->height);
6474       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6475         exception);
6476       if (windows->image.crop_geometry != (char *) NULL)
6477         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6478           windows->image.crop_geometry);
6479       windows->image.crop_geometry=cache_image->geometry;
6480       if (redo_image != (Image *) NULL)
6481         redo_image=DestroyImage(redo_image);
6482       redo_image=(*image);
6483       *image=cache_image->list;
6484       cache_image=DestroyImage(cache_image);
6485       if (windows->image.orphan != MagickFalse )
6486         return;
6487       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6488       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6489       return;
6490     }
6491     case CutCommand:
6492     case PasteCommand:
6493     case ApplyCommand:
6494     case HalfSizeCommand:
6495     case OriginalSizeCommand:
6496     case DoubleSizeCommand:
6497     case ResizeCommand:
6498     case TrimCommand:
6499     case CropCommand:
6500     case ChopCommand:
6501     case FlipCommand:
6502     case FlopCommand:
6503     case RotateRightCommand:
6504     case RotateLeftCommand:
6505     case RotateCommand:
6506     case ShearCommand:
6507     case RollCommand:
6508     case NegateCommand:
6509     case ContrastStretchCommand:
6510     case SigmoidalContrastCommand:
6511     case NormalizeCommand:
6512     case EqualizeCommand:
6513     case HueCommand:
6514     case SaturationCommand:
6515     case BrightnessCommand:
6516     case GammaCommand:
6517     case SpiffCommand:
6518     case DullCommand:
6519     case GrayscaleCommand:
6520     case MapCommand:
6521     case QuantizeCommand:
6522     case DespeckleCommand:
6523     case EmbossCommand:
6524     case ReduceNoiseCommand:
6525     case AddNoiseCommand:
6526     case SharpenCommand:
6527     case BlurCommand:
6528     case ThresholdCommand:
6529     case EdgeDetectCommand:
6530     case SpreadCommand:
6531     case ShadeCommand:
6532     case RaiseCommand:
6533     case SegmentCommand:
6534     case SolarizeCommand:
6535     case SepiaToneCommand:
6536     case SwirlCommand:
6537     case ImplodeCommand:
6538     case VignetteCommand:
6539     case WaveCommand:
6540     case OilPaintCommand:
6541     case CharcoalDrawCommand:
6542     case AnnotateCommand:
6543     case AddBorderCommand:
6544     case AddFrameCommand:
6545     case CompositeCommand:
6546     case CommentCommand:
6547     case LaunchCommand:
6548     case RegionofInterestCommand:
6549     case SaveToUndoBufferCommand:
6550     case RedoCommand:
6551     {
6552       Image
6553         *previous_image;
6554 
6555       ssize_t
6556         bytes;
6557 
6558       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6559       if (undo_image != (Image *) NULL)
6560         {
6561           /*
6562             Ensure the undo cache has enough memory available.
6563           */
6564           previous_image=undo_image;
6565           while (previous_image != (Image *) NULL)
6566           {
6567             bytes+=previous_image->list->columns*previous_image->list->rows*
6568               sizeof(PixelInfo);
6569             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6570               {
6571                 previous_image=GetPreviousImageInList(previous_image);
6572                 continue;
6573               }
6574             bytes-=previous_image->list->columns*previous_image->list->rows*
6575               sizeof(PixelInfo);
6576             if (previous_image == undo_image)
6577               undo_image=NewImageList();
6578             else
6579               previous_image->next->previous=NewImageList();
6580             break;
6581           }
6582           while (previous_image != (Image *) NULL)
6583           {
6584             /*
6585               Delete any excess memory from undo cache.
6586             */
6587             cache_image=previous_image;
6588             previous_image=GetPreviousImageInList(previous_image);
6589             cache_image->list=DestroyImage(cache_image->list);
6590             cache_image=DestroyImage(cache_image);
6591           }
6592         }
6593       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6594         break;
6595       /*
6596         Save image before transformations are applied.
6597       */
6598       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6599       if (cache_image == (Image *) NULL)
6600         break;
6601       XSetCursorState(display,windows,MagickTrue);
6602       XCheckRefreshWindows(display,windows);
6603       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6604       XSetCursorState(display,windows,MagickFalse);
6605       if (cache_image->list == (Image *) NULL)
6606         {
6607           cache_image=DestroyImage(cache_image);
6608           break;
6609         }
6610       cache_image->columns=(size_t) windows->image.ximage->width;
6611       cache_image->rows=(size_t) windows->image.ximage->height;
6612       cache_image->geometry=windows->image.crop_geometry;
6613       if (windows->image.crop_geometry != (char *) NULL)
6614         {
6615           cache_image->geometry=AcquireString((char *) NULL);
6616           (void) CopyMagickString(cache_image->geometry,
6617             windows->image.crop_geometry,MagickPathExtent);
6618         }
6619       if (undo_image == (Image *) NULL)
6620         {
6621           undo_image=cache_image;
6622           break;
6623         }
6624       undo_image->next=cache_image;
6625       undo_image->next->previous=undo_image;
6626       undo_image=undo_image->next;
6627       break;
6628     }
6629     default:
6630       break;
6631   }
6632   if (command == RedoCommand)
6633     {
6634       /*
6635         Redo the last image transformation.
6636       */
6637       if (redo_image == (Image *) NULL)
6638         {
6639           (void) XBell(display,0);
6640           return;
6641         }
6642       windows->image.window_changes.width=(int) redo_image->columns;
6643       windows->image.window_changes.height=(int) redo_image->rows;
6644       if (windows->image.crop_geometry != (char *) NULL)
6645         windows->image.crop_geometry=(char *)
6646           RelinquishMagickMemory(windows->image.crop_geometry);
6647       windows->image.crop_geometry=redo_image->geometry;
6648       *image=DestroyImage(*image);
6649       *image=redo_image;
6650       redo_image=NewImageList();
6651       if (windows->image.orphan != MagickFalse )
6652         return;
6653       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6654       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6655       return;
6656     }
6657   if (command != InfoCommand)
6658     return;
6659   /*
6660     Display image info.
6661   */
6662   XSetCursorState(display,windows,MagickTrue);
6663   XCheckRefreshWindows(display,windows);
6664   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6665   XSetCursorState(display,windows,MagickFalse);
6666 }
6667 
6668 /*
6669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6670 %                                                                             %
6671 %                                                                             %
6672 %                                                                             %
6673 +   X I m a g e W i n d o w C o m m a n d                                     %
6674 %                                                                             %
6675 %                                                                             %
6676 %                                                                             %
6677 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6678 %
6679 %  XImageWindowCommand() makes a transform to the image or Image window as
6680 %  specified by a user menu button or keyboard command.
6681 %
6682 %  The format of the XImageWindowCommand method is:
6683 %
6684 %      CommandType XImageWindowCommand(Display *display,
6685 %        XResourceInfo *resource_info,XWindows *windows,
6686 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6687 %        ExceptionInfo *exception)
6688 %
6689 %  A description of each parameter follows:
6690 %
6691 %    o nexus:  Method XImageWindowCommand returns an image when the
6692 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6693 %      image is returned.
6694 %
6695 %    o display: Specifies a connection to an X server; returned from
6696 %      XOpenDisplay.
6697 %
6698 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6699 %
6700 %    o windows: Specifies a pointer to a XWindows structure.
6701 %
6702 %    o state: key mask.
6703 %
6704 %    o key_symbol: Specifies a command to perform.
6705 %
6706 %    o image: the image;  XImageWIndowCommand may transform the image and
6707 %      return a new image pointer.
6708 %
6709 %    o exception: return any errors or warnings in this structure.
6710 %
6711 */
XImageWindowCommand(Display * display,XResourceInfo * resource_info,XWindows * windows,const MagickStatusType state,KeySym key_symbol,Image ** image,ExceptionInfo * exception)6712 static CommandType XImageWindowCommand(Display *display,
6713   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6714   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6715 {
6716   static char
6717     delta[MagickPathExtent] = "";
6718 
6719   static const char
6720     Digits[] = "01234567890";
6721 
6722   static KeySym
6723     last_symbol = XK_0;
6724 
6725   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6726     {
6727       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6728         {
6729           *delta='\0';
6730           resource_info->quantum=1;
6731         }
6732       last_symbol=key_symbol;
6733       delta[strlen(delta)+1]='\0';
6734       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6735       resource_info->quantum=StringToLong(delta);
6736       return(NullCommand);
6737     }
6738   last_symbol=key_symbol;
6739   if (resource_info->immutable)
6740     {
6741       /*
6742         Virtual image window has a restricted command set.
6743       */
6744       switch (key_symbol)
6745       {
6746         case XK_question:
6747           return(InfoCommand);
6748         case XK_p:
6749         case XK_Print:
6750           return(PrintCommand);
6751         case XK_space:
6752           return(NextCommand);
6753         case XK_q:
6754         case XK_Escape:
6755           return(QuitCommand);
6756         default:
6757           break;
6758       }
6759       return(NullCommand);
6760     }
6761   switch ((int) key_symbol)
6762   {
6763     case XK_o:
6764     {
6765       if ((state & ControlMask) == 0)
6766         break;
6767       return(OpenCommand);
6768     }
6769     case XK_space:
6770       return(NextCommand);
6771     case XK_BackSpace:
6772       return(FormerCommand);
6773     case XK_s:
6774     {
6775       if ((state & Mod1Mask) != 0)
6776         return(SwirlCommand);
6777       if ((state & ControlMask) == 0)
6778         return(ShearCommand);
6779       return(SaveCommand);
6780     }
6781     case XK_p:
6782     case XK_Print:
6783     {
6784       if ((state & Mod1Mask) != 0)
6785         return(OilPaintCommand);
6786       if ((state & Mod4Mask) != 0)
6787         return(ColorCommand);
6788       if ((state & ControlMask) == 0)
6789         return(NullCommand);
6790       return(PrintCommand);
6791     }
6792     case XK_d:
6793     {
6794       if ((state & Mod4Mask) != 0)
6795         return(DrawCommand);
6796       if ((state & ControlMask) == 0)
6797         return(NullCommand);
6798       return(DeleteCommand);
6799     }
6800     case XK_Select:
6801     {
6802       if ((state & ControlMask) == 0)
6803         return(NullCommand);
6804       return(SelectCommand);
6805     }
6806     case XK_n:
6807     {
6808       if ((state & ControlMask) == 0)
6809         return(NullCommand);
6810       return(NewCommand);
6811     }
6812     case XK_q:
6813     case XK_Escape:
6814       return(QuitCommand);
6815     case XK_z:
6816     case XK_Undo:
6817     {
6818       if ((state & ControlMask) == 0)
6819         return(NullCommand);
6820       return(UndoCommand);
6821     }
6822     case XK_r:
6823     case XK_Redo:
6824     {
6825       if ((state & ControlMask) == 0)
6826         return(RollCommand);
6827       return(RedoCommand);
6828     }
6829     case XK_x:
6830     {
6831       if ((state & ControlMask) == 0)
6832         return(NullCommand);
6833       return(CutCommand);
6834     }
6835     case XK_c:
6836     {
6837       if ((state & Mod1Mask) != 0)
6838         return(CharcoalDrawCommand);
6839       if ((state & ControlMask) == 0)
6840         return(CropCommand);
6841       return(CopyCommand);
6842     }
6843     case XK_v:
6844     case XK_Insert:
6845     {
6846       if ((state & Mod4Mask) != 0)
6847         return(CompositeCommand);
6848       if ((state & ControlMask) == 0)
6849         return(FlipCommand);
6850       return(PasteCommand);
6851     }
6852     case XK_less:
6853       return(HalfSizeCommand);
6854     case XK_minus:
6855       return(OriginalSizeCommand);
6856     case XK_greater:
6857       return(DoubleSizeCommand);
6858     case XK_percent:
6859       return(ResizeCommand);
6860     case XK_at:
6861       return(RefreshCommand);
6862     case XK_bracketleft:
6863       return(ChopCommand);
6864     case XK_h:
6865       return(FlopCommand);
6866     case XK_slash:
6867       return(RotateRightCommand);
6868     case XK_backslash:
6869       return(RotateLeftCommand);
6870     case XK_asterisk:
6871       return(RotateCommand);
6872     case XK_t:
6873       return(TrimCommand);
6874     case XK_H:
6875       return(HueCommand);
6876     case XK_S:
6877       return(SaturationCommand);
6878     case XK_L:
6879       return(BrightnessCommand);
6880     case XK_G:
6881       return(GammaCommand);
6882     case XK_C:
6883       return(SpiffCommand);
6884     case XK_Z:
6885       return(DullCommand);
6886     case XK_N:
6887       return(NormalizeCommand);
6888     case XK_equal:
6889       return(EqualizeCommand);
6890     case XK_asciitilde:
6891       return(NegateCommand);
6892     case XK_period:
6893       return(GrayscaleCommand);
6894     case XK_numbersign:
6895       return(QuantizeCommand);
6896     case XK_F2:
6897       return(DespeckleCommand);
6898     case XK_F3:
6899       return(EmbossCommand);
6900     case XK_F4:
6901       return(ReduceNoiseCommand);
6902     case XK_F5:
6903       return(AddNoiseCommand);
6904     case XK_F6:
6905       return(SharpenCommand);
6906     case XK_F7:
6907       return(BlurCommand);
6908     case XK_F8:
6909       return(ThresholdCommand);
6910     case XK_F9:
6911       return(EdgeDetectCommand);
6912     case XK_F10:
6913       return(SpreadCommand);
6914     case XK_F11:
6915       return(ShadeCommand);
6916     case XK_F12:
6917       return(RaiseCommand);
6918     case XK_F13:
6919       return(SegmentCommand);
6920     case XK_i:
6921     {
6922       if ((state & Mod1Mask) == 0)
6923         return(NullCommand);
6924       return(ImplodeCommand);
6925     }
6926     case XK_w:
6927     {
6928       if ((state & Mod1Mask) == 0)
6929         return(NullCommand);
6930       return(WaveCommand);
6931     }
6932     case XK_m:
6933     {
6934       if ((state & Mod4Mask) == 0)
6935         return(NullCommand);
6936       return(MatteCommand);
6937     }
6938     case XK_b:
6939     {
6940       if ((state & Mod4Mask) == 0)
6941         return(NullCommand);
6942       return(AddBorderCommand);
6943     }
6944     case XK_f:
6945     {
6946       if ((state & Mod4Mask) == 0)
6947         return(NullCommand);
6948       return(AddFrameCommand);
6949     }
6950     case XK_exclam:
6951     {
6952       if ((state & Mod4Mask) == 0)
6953         return(NullCommand);
6954       return(CommentCommand);
6955     }
6956     case XK_a:
6957     {
6958       if ((state & Mod1Mask) != 0)
6959         return(ApplyCommand);
6960       if ((state & Mod4Mask) != 0)
6961         return(AnnotateCommand);
6962       if ((state & ControlMask) == 0)
6963         return(NullCommand);
6964       return(RegionofInterestCommand);
6965     }
6966     case XK_question:
6967       return(InfoCommand);
6968     case XK_plus:
6969       return(ZoomCommand);
6970     case XK_P:
6971     {
6972       if ((state & ShiftMask) == 0)
6973         return(NullCommand);
6974       return(ShowPreviewCommand);
6975     }
6976     case XK_Execute:
6977       return(LaunchCommand);
6978     case XK_F1:
6979       return(HelpCommand);
6980     case XK_Find:
6981       return(BrowseDocumentationCommand);
6982     case XK_Menu:
6983     {
6984       (void) XMapRaised(display,windows->command.id);
6985       return(NullCommand);
6986     }
6987     case XK_Next:
6988     case XK_Prior:
6989     case XK_Home:
6990     case XK_KP_Home:
6991     {
6992       XTranslateImage(display,windows,*image,key_symbol);
6993       return(NullCommand);
6994     }
6995     case XK_Up:
6996     case XK_KP_Up:
6997     case XK_Down:
6998     case XK_KP_Down:
6999     case XK_Left:
7000     case XK_KP_Left:
7001     case XK_Right:
7002     case XK_KP_Right:
7003     {
7004       if ((state & Mod1Mask) != 0)
7005         {
7006           RectangleInfo
7007             crop_info;
7008 
7009           /*
7010             Trim one pixel from edge of image.
7011           */
7012           crop_info.x=0;
7013           crop_info.y=0;
7014           crop_info.width=(size_t) windows->image.ximage->width;
7015           crop_info.height=(size_t) windows->image.ximage->height;
7016           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7017             {
7018               if (resource_info->quantum >= (int) crop_info.height)
7019                 resource_info->quantum=(int) crop_info.height-1;
7020               crop_info.height-=resource_info->quantum;
7021             }
7022           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7023             {
7024               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7025                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7026               crop_info.y+=resource_info->quantum;
7027               crop_info.height-=resource_info->quantum;
7028             }
7029           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7030             {
7031               if (resource_info->quantum >= (int) crop_info.width)
7032                 resource_info->quantum=(int) crop_info.width-1;
7033               crop_info.width-=resource_info->quantum;
7034             }
7035           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7036             {
7037               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7038                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7039               crop_info.x+=resource_info->quantum;
7040               crop_info.width-=resource_info->quantum;
7041             }
7042           if ((int) (windows->image.x+windows->image.width) >
7043               (int) crop_info.width)
7044             windows->image.x=(int) (crop_info.width-windows->image.width);
7045           if ((int) (windows->image.y+windows->image.height) >
7046               (int) crop_info.height)
7047             windows->image.y=(int) (crop_info.height-windows->image.height);
7048           XSetCropGeometry(display,windows,&crop_info,*image);
7049           windows->image.window_changes.width=(int) crop_info.width;
7050           windows->image.window_changes.height=(int) crop_info.height;
7051           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7052           (void) XConfigureImage(display,resource_info,windows,*image,
7053             exception);
7054           return(NullCommand);
7055         }
7056       XTranslateImage(display,windows,*image,key_symbol);
7057       return(NullCommand);
7058     }
7059     default:
7060       return(NullCommand);
7061   }
7062   return(NullCommand);
7063 }
7064 
7065 /*
7066 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7067 %                                                                             %
7068 %                                                                             %
7069 %                                                                             %
7070 +   X M a g i c k C o m m a n d                                               %
7071 %                                                                             %
7072 %                                                                             %
7073 %                                                                             %
7074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7075 %
7076 %  XMagickCommand() makes a transform to the image or Image window as
7077 %  specified by a user menu button or keyboard command.
7078 %
7079 %  The format of the XMagickCommand method is:
7080 %
7081 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7082 %        XWindows *windows,const CommandType command,Image **image,
7083 %        ExceptionInfo *exception)
7084 %
7085 %  A description of each parameter follows:
7086 %
7087 %    o display: Specifies a connection to an X server; returned from
7088 %      XOpenDisplay.
7089 %
7090 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7091 %
7092 %    o windows: Specifies a pointer to a XWindows structure.
7093 %
7094 %    o command: Specifies a command to perform.
7095 %
7096 %    o image: the image;  XMagickCommand may transform the image and return a
7097 %      new image pointer.
7098 %
7099 %    o exception: return any errors or warnings in this structure.
7100 %
7101 */
XMagickCommand(Display * display,XResourceInfo * resource_info,XWindows * windows,const CommandType command,Image ** image,ExceptionInfo * exception)7102 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7103   XWindows *windows,const CommandType command,Image **image,
7104   ExceptionInfo *exception)
7105 {
7106   char
7107     filename[MagickPathExtent],
7108     geometry[MagickPathExtent],
7109     modulate_factors[MagickPathExtent];
7110 
7111   GeometryInfo
7112     geometry_info;
7113 
7114   Image
7115     *nexus;
7116 
7117   ImageInfo
7118     *image_info;
7119 
7120   int
7121     x,
7122     y;
7123 
7124   MagickStatusType
7125     flags,
7126     status;
7127 
7128   QuantizeInfo
7129     quantize_info;
7130 
7131   RectangleInfo
7132     page_geometry;
7133 
7134   register int
7135     i;
7136 
7137   static char
7138     color[MagickPathExtent] = "gray";
7139 
7140   unsigned int
7141     height,
7142     width;
7143 
7144   /*
7145     Process user command.
7146   */
7147   XCheckRefreshWindows(display,windows);
7148   XImageCache(display,resource_info,windows,command,image,exception);
7149   nexus=NewImageList();
7150   windows->image.window_changes.width=windows->image.ximage->width;
7151   windows->image.window_changes.height=windows->image.ximage->height;
7152   image_info=CloneImageInfo(resource_info->image_info);
7153   SetGeometryInfo(&geometry_info);
7154   GetQuantizeInfo(&quantize_info);
7155   switch (command)
7156   {
7157     case OpenCommand:
7158     {
7159       /*
7160         Load image.
7161       */
7162       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7163       break;
7164     }
7165     case NextCommand:
7166     {
7167       /*
7168         Display next image.
7169       */
7170       for (i=0; i < resource_info->quantum; i++)
7171         XClientMessage(display,windows->image.id,windows->im_protocols,
7172           windows->im_next_image,CurrentTime);
7173       break;
7174     }
7175     case FormerCommand:
7176     {
7177       /*
7178         Display former image.
7179       */
7180       for (i=0; i < resource_info->quantum; i++)
7181         XClientMessage(display,windows->image.id,windows->im_protocols,
7182           windows->im_former_image,CurrentTime);
7183       break;
7184     }
7185     case SelectCommand:
7186     {
7187       int
7188         status;
7189 
7190       /*
7191         Select image.
7192       */
7193       if (*resource_info->home_directory == '\0')
7194         (void) CopyMagickString(resource_info->home_directory,".",
7195           MagickPathExtent);
7196       status=chdir(resource_info->home_directory);
7197       if (status == -1)
7198         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7199           "UnableToOpenFile","%s",resource_info->home_directory);
7200       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7201       break;
7202     }
7203     case SaveCommand:
7204     {
7205       /*
7206         Save image.
7207       */
7208       status=XSaveImage(display,resource_info,windows,*image,exception);
7209       if (status == MagickFalse)
7210         {
7211           char
7212             message[MagickPathExtent];
7213 
7214           (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7215             exception->reason != (char *) NULL ? exception->reason : "",
7216             exception->description != (char *) NULL ? exception->description :
7217             "");
7218           XNoticeWidget(display,windows,"Unable to save file:",message);
7219           break;
7220         }
7221       break;
7222     }
7223     case PrintCommand:
7224     {
7225       /*
7226         Print image.
7227       */
7228       status=XPrintImage(display,resource_info,windows,*image,exception);
7229       if (status == MagickFalse)
7230         {
7231           char
7232             message[MagickPathExtent];
7233 
7234           (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7235             exception->reason != (char *) NULL ? exception->reason : "",
7236             exception->description != (char *) NULL ? exception->description :
7237             "");
7238           XNoticeWidget(display,windows,"Unable to print file:",message);
7239           break;
7240         }
7241       break;
7242     }
7243     case DeleteCommand:
7244     {
7245       static char
7246         filename[MagickPathExtent] = "\0";
7247 
7248       /*
7249         Delete image file.
7250       */
7251       XFileBrowserWidget(display,windows,"Delete",filename);
7252       if (*filename == '\0')
7253         break;
7254       status=ShredFile(filename);
7255       if (status != MagickFalse )
7256         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7257       break;
7258     }
7259     case NewCommand:
7260     {
7261       int
7262         status;
7263 
7264       static char
7265         color[MagickPathExtent] = "gray",
7266         geometry[MagickPathExtent] = "640x480";
7267 
7268       static const char
7269         *format = "gradient";
7270 
7271       /*
7272         Query user for canvas geometry.
7273       */
7274       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7275         geometry);
7276       if (*geometry == '\0')
7277         break;
7278       if (status == 0)
7279         format="xc";
7280       XColorBrowserWidget(display,windows,"Select",color);
7281       if (*color == '\0')
7282         break;
7283       /*
7284         Create canvas.
7285       */
7286       (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7287         "%s:%s",format,color);
7288       (void) CloneString(&image_info->size,geometry);
7289       nexus=ReadImage(image_info,exception);
7290       CatchException(exception);
7291       XClientMessage(display,windows->image.id,windows->im_protocols,
7292         windows->im_next_image,CurrentTime);
7293       break;
7294     }
7295     case VisualDirectoryCommand:
7296     {
7297       /*
7298         Visual Image directory.
7299       */
7300       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7301       break;
7302     }
7303     case QuitCommand:
7304     {
7305       /*
7306         exit program.
7307       */
7308       if (resource_info->confirm_exit == MagickFalse)
7309         XClientMessage(display,windows->image.id,windows->im_protocols,
7310           windows->im_exit,CurrentTime);
7311       else
7312         {
7313           int
7314             status;
7315 
7316           /*
7317             Confirm program exit.
7318           */
7319           status=XConfirmWidget(display,windows,"Do you really want to exit",
7320             resource_info->client_name);
7321           if (status > 0)
7322             XClientMessage(display,windows->image.id,windows->im_protocols,
7323               windows->im_exit,CurrentTime);
7324         }
7325       break;
7326     }
7327     case CutCommand:
7328     {
7329       /*
7330         Cut image.
7331       */
7332       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7333       break;
7334     }
7335     case CopyCommand:
7336     {
7337       /*
7338         Copy image.
7339       */
7340       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7341         exception);
7342       break;
7343     }
7344     case PasteCommand:
7345     {
7346       /*
7347         Paste image.
7348       */
7349       status=XPasteImage(display,resource_info,windows,*image,exception);
7350       if (status == MagickFalse)
7351         {
7352           XNoticeWidget(display,windows,"Unable to paste X image",
7353             (*image)->filename);
7354           break;
7355         }
7356       break;
7357     }
7358     case HalfSizeCommand:
7359     {
7360       /*
7361         Half image size.
7362       */
7363       windows->image.window_changes.width=windows->image.ximage->width/2;
7364       windows->image.window_changes.height=windows->image.ximage->height/2;
7365       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7366       break;
7367     }
7368     case OriginalSizeCommand:
7369     {
7370       /*
7371         Original image size.
7372       */
7373       windows->image.window_changes.width=(int) (*image)->columns;
7374       windows->image.window_changes.height=(int) (*image)->rows;
7375       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7376       break;
7377     }
7378     case DoubleSizeCommand:
7379     {
7380       /*
7381         Double the image size.
7382       */
7383       windows->image.window_changes.width=windows->image.ximage->width << 1;
7384       windows->image.window_changes.height=windows->image.ximage->height << 1;
7385       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7386       break;
7387     }
7388     case ResizeCommand:
7389     {
7390       int
7391         status;
7392 
7393       size_t
7394         height,
7395         width;
7396 
7397       ssize_t
7398         x,
7399         y;
7400 
7401       /*
7402         Resize image.
7403       */
7404       width=(size_t) windows->image.ximage->width;
7405       height=(size_t) windows->image.ximage->height;
7406       x=0;
7407       y=0;
7408       (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
7409         (double) width,(double) height);
7410       status=XDialogWidget(display,windows,"Resize",
7411         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7412       if (*geometry == '\0')
7413         break;
7414       if (status == 0)
7415         (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7416       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7417       windows->image.window_changes.width=(int) width;
7418       windows->image.window_changes.height=(int) height;
7419       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7420       break;
7421     }
7422     case ApplyCommand:
7423     {
7424       char
7425         image_geometry[MagickPathExtent];
7426 
7427       if ((windows->image.crop_geometry == (char *) NULL) &&
7428           ((int) (*image)->columns == windows->image.ximage->width) &&
7429           ((int) (*image)->rows == windows->image.ximage->height))
7430         break;
7431       /*
7432         Apply size transforms to image.
7433       */
7434       XSetCursorState(display,windows,MagickTrue);
7435       XCheckRefreshWindows(display,windows);
7436       /*
7437         Crop and/or scale displayed image.
7438       */
7439       (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7440         windows->image.ximage->width,windows->image.ximage->height);
7441       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7442         exception);
7443       if (windows->image.crop_geometry != (char *) NULL)
7444         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7445           windows->image.crop_geometry);
7446       windows->image.x=0;
7447       windows->image.y=0;
7448       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7449       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7450       break;
7451     }
7452     case RefreshCommand:
7453     {
7454       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7455       break;
7456     }
7457     case RestoreCommand:
7458     {
7459       /*
7460         Restore Image window to its original size.
7461       */
7462       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7463           (windows->image.height == (unsigned int) (*image)->rows) &&
7464           (windows->image.crop_geometry == (char *) NULL))
7465         {
7466           (void) XBell(display,0);
7467           break;
7468         }
7469       windows->image.window_changes.width=(int) (*image)->columns;
7470       windows->image.window_changes.height=(int) (*image)->rows;
7471       if (windows->image.crop_geometry != (char *) NULL)
7472         {
7473           windows->image.crop_geometry=(char *)
7474             RelinquishMagickMemory(windows->image.crop_geometry);
7475           windows->image.crop_geometry=(char *) NULL;
7476           windows->image.x=0;
7477           windows->image.y=0;
7478         }
7479       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7480       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7481       break;
7482     }
7483     case CropCommand:
7484     {
7485       /*
7486         Crop image.
7487       */
7488       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7489         exception);
7490       break;
7491     }
7492     case ChopCommand:
7493     {
7494       /*
7495         Chop image.
7496       */
7497       status=XChopImage(display,resource_info,windows,image,exception);
7498       if (status == MagickFalse)
7499         {
7500           XNoticeWidget(display,windows,"Unable to cut X image",
7501             (*image)->filename);
7502           break;
7503         }
7504       break;
7505     }
7506     case FlopCommand:
7507     {
7508       Image
7509         *flop_image;
7510 
7511       /*
7512         Flop image scanlines.
7513       */
7514       XSetCursorState(display,windows,MagickTrue);
7515       XCheckRefreshWindows(display,windows);
7516       flop_image=FlopImage(*image,exception);
7517       if (flop_image != (Image *) NULL)
7518         {
7519           *image=DestroyImage(*image);
7520           *image=flop_image;
7521         }
7522       CatchException(exception);
7523       XSetCursorState(display,windows,MagickFalse);
7524       if (windows->image.crop_geometry != (char *) NULL)
7525         {
7526           /*
7527             Flop crop geometry.
7528           */
7529           width=(unsigned int) (*image)->columns;
7530           height=(unsigned int) (*image)->rows;
7531           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7532             &width,&height);
7533           (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
7534             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7535         }
7536       if (windows->image.orphan != MagickFalse )
7537         break;
7538       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7539       break;
7540     }
7541     case FlipCommand:
7542     {
7543       Image
7544         *flip_image;
7545 
7546       /*
7547         Flip image scanlines.
7548       */
7549       XSetCursorState(display,windows,MagickTrue);
7550       XCheckRefreshWindows(display,windows);
7551       flip_image=FlipImage(*image,exception);
7552       if (flip_image != (Image *) NULL)
7553         {
7554           *image=DestroyImage(*image);
7555           *image=flip_image;
7556         }
7557       CatchException(exception);
7558       XSetCursorState(display,windows,MagickFalse);
7559       if (windows->image.crop_geometry != (char *) NULL)
7560         {
7561           /*
7562             Flip crop geometry.
7563           */
7564           width=(unsigned int) (*image)->columns;
7565           height=(unsigned int) (*image)->rows;
7566           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7567             &width,&height);
7568           (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
7569             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7570         }
7571       if (windows->image.orphan != MagickFalse )
7572         break;
7573       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7574       break;
7575     }
7576     case RotateRightCommand:
7577     {
7578       /*
7579         Rotate image 90 degrees clockwise.
7580       */
7581       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7582       if (status == MagickFalse)
7583         {
7584           XNoticeWidget(display,windows,"Unable to rotate X image",
7585             (*image)->filename);
7586           break;
7587         }
7588       break;
7589     }
7590     case RotateLeftCommand:
7591     {
7592       /*
7593         Rotate image 90 degrees counter-clockwise.
7594       */
7595       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7596       if (status == MagickFalse)
7597         {
7598           XNoticeWidget(display,windows,"Unable to rotate X image",
7599             (*image)->filename);
7600           break;
7601         }
7602       break;
7603     }
7604     case RotateCommand:
7605     {
7606       /*
7607         Rotate image.
7608       */
7609       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7610       if (status == MagickFalse)
7611         {
7612           XNoticeWidget(display,windows,"Unable to rotate X image",
7613             (*image)->filename);
7614           break;
7615         }
7616       break;
7617     }
7618     case ShearCommand:
7619     {
7620       Image
7621         *shear_image;
7622 
7623       static char
7624         geometry[MagickPathExtent] = "45.0x45.0";
7625 
7626       /*
7627         Query user for shear color and geometry.
7628       */
7629       XColorBrowserWidget(display,windows,"Select",color);
7630       if (*color == '\0')
7631         break;
7632       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7633         geometry);
7634       if (*geometry == '\0')
7635         break;
7636       /*
7637         Shear image.
7638       */
7639       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7640         exception);
7641       XSetCursorState(display,windows,MagickTrue);
7642       XCheckRefreshWindows(display,windows);
7643       (void) QueryColorCompliance(color,AllCompliance,
7644         &(*image)->background_color,exception);
7645       flags=ParseGeometry(geometry,&geometry_info);
7646       if ((flags & SigmaValue) == 0)
7647         geometry_info.sigma=geometry_info.rho;
7648       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7649         exception);
7650       if (shear_image != (Image *) NULL)
7651         {
7652           *image=DestroyImage(*image);
7653           *image=shear_image;
7654         }
7655       CatchException(exception);
7656       XSetCursorState(display,windows,MagickFalse);
7657       if (windows->image.orphan != MagickFalse )
7658         break;
7659       windows->image.window_changes.width=(int) (*image)->columns;
7660       windows->image.window_changes.height=(int) (*image)->rows;
7661       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7662       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7663       break;
7664     }
7665     case RollCommand:
7666     {
7667       Image
7668         *roll_image;
7669 
7670       static char
7671         geometry[MagickPathExtent] = "+2+2";
7672 
7673       /*
7674         Query user for the roll geometry.
7675       */
7676       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7677         geometry);
7678       if (*geometry == '\0')
7679         break;
7680       /*
7681         Roll image.
7682       */
7683       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7684         exception);
7685       XSetCursorState(display,windows,MagickTrue);
7686       XCheckRefreshWindows(display,windows);
7687       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7688         exception);
7689       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7690         exception);
7691       if (roll_image != (Image *) NULL)
7692         {
7693           *image=DestroyImage(*image);
7694           *image=roll_image;
7695         }
7696       CatchException(exception);
7697       XSetCursorState(display,windows,MagickFalse);
7698       if (windows->image.orphan != MagickFalse )
7699         break;
7700       windows->image.window_changes.width=(int) (*image)->columns;
7701       windows->image.window_changes.height=(int) (*image)->rows;
7702       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7703       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7704       break;
7705     }
7706     case TrimCommand:
7707     {
7708       static char
7709         fuzz[MagickPathExtent];
7710 
7711       /*
7712         Query user for the fuzz factor.
7713       */
7714       (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7715         (*image)->fuzz/(QuantumRange+1.0));
7716       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7717       if (*fuzz == '\0')
7718         break;
7719       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7720       /*
7721         Trim image.
7722       */
7723       status=XTrimImage(display,resource_info,windows,*image,exception);
7724       if (status == MagickFalse)
7725         {
7726           XNoticeWidget(display,windows,"Unable to trim X image",
7727             (*image)->filename);
7728           break;
7729         }
7730       break;
7731     }
7732     case HueCommand:
7733     {
7734       static char
7735         hue_percent[MagickPathExtent] = "110";
7736 
7737       /*
7738         Query user for percent hue change.
7739       */
7740       (void) XDialogWidget(display,windows,"Apply",
7741         "Enter percent change in image hue (0-200):",hue_percent);
7742       if (*hue_percent == '\0')
7743         break;
7744       /*
7745         Vary the image hue.
7746       */
7747       XSetCursorState(display,windows,MagickTrue);
7748       XCheckRefreshWindows(display,windows);
7749       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7750       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7751         MagickPathExtent);
7752       (void) ModulateImage(*image,modulate_factors,exception);
7753       XSetCursorState(display,windows,MagickFalse);
7754       if (windows->image.orphan != MagickFalse )
7755         break;
7756       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7757       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7758       break;
7759     }
7760     case SaturationCommand:
7761     {
7762       static char
7763         saturation_percent[MagickPathExtent] = "110";
7764 
7765       /*
7766         Query user for percent saturation change.
7767       */
7768       (void) XDialogWidget(display,windows,"Apply",
7769         "Enter percent change in color saturation (0-200):",saturation_percent);
7770       if (*saturation_percent == '\0')
7771         break;
7772       /*
7773         Vary color saturation.
7774       */
7775       XSetCursorState(display,windows,MagickTrue);
7776       XCheckRefreshWindows(display,windows);
7777       (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7778       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7779         MagickPathExtent);
7780       (void) ModulateImage(*image,modulate_factors,exception);
7781       XSetCursorState(display,windows,MagickFalse);
7782       if (windows->image.orphan != MagickFalse )
7783         break;
7784       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7785       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7786       break;
7787     }
7788     case BrightnessCommand:
7789     {
7790       static char
7791         brightness_percent[MagickPathExtent] = "110";
7792 
7793       /*
7794         Query user for percent brightness change.
7795       */
7796       (void) XDialogWidget(display,windows,"Apply",
7797         "Enter percent change in color brightness (0-200):",brightness_percent);
7798       if (*brightness_percent == '\0')
7799         break;
7800       /*
7801         Vary the color brightness.
7802       */
7803       XSetCursorState(display,windows,MagickTrue);
7804       XCheckRefreshWindows(display,windows);
7805       (void) CopyMagickString(modulate_factors,brightness_percent,
7806         MagickPathExtent);
7807       (void) ModulateImage(*image,modulate_factors,exception);
7808       XSetCursorState(display,windows,MagickFalse);
7809       if (windows->image.orphan != MagickFalse )
7810         break;
7811       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7812       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7813       break;
7814     }
7815     case GammaCommand:
7816     {
7817       static char
7818         factor[MagickPathExtent] = "1.6";
7819 
7820       /*
7821         Query user for gamma value.
7822       */
7823       (void) XDialogWidget(display,windows,"Gamma",
7824         "Enter gamma value (e.g. 1.2):",factor);
7825       if (*factor == '\0')
7826         break;
7827       /*
7828         Gamma correct image.
7829       */
7830       XSetCursorState(display,windows,MagickTrue);
7831       XCheckRefreshWindows(display,windows);
7832       (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7833       XSetCursorState(display,windows,MagickFalse);
7834       if (windows->image.orphan != MagickFalse )
7835         break;
7836       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7837       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7838       break;
7839     }
7840     case SpiffCommand:
7841     {
7842       /*
7843         Sharpen the image contrast.
7844       */
7845       XSetCursorState(display,windows,MagickTrue);
7846       XCheckRefreshWindows(display,windows);
7847       (void) ContrastImage(*image,MagickTrue,exception);
7848       XSetCursorState(display,windows,MagickFalse);
7849       if (windows->image.orphan != MagickFalse )
7850         break;
7851       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7852       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7853       break;
7854     }
7855     case DullCommand:
7856     {
7857       /*
7858         Dull the image contrast.
7859       */
7860       XSetCursorState(display,windows,MagickTrue);
7861       XCheckRefreshWindows(display,windows);
7862       (void) ContrastImage(*image,MagickFalse,exception);
7863       XSetCursorState(display,windows,MagickFalse);
7864       if (windows->image.orphan != MagickFalse )
7865         break;
7866       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7867       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7868       break;
7869     }
7870     case ContrastStretchCommand:
7871     {
7872       double
7873         black_point,
7874         white_point;
7875 
7876       static char
7877         levels[MagickPathExtent] = "1%";
7878 
7879       /*
7880         Query user for gamma value.
7881       */
7882       (void) XDialogWidget(display,windows,"Contrast Stretch",
7883         "Enter black and white points:",levels);
7884       if (*levels == '\0')
7885         break;
7886       /*
7887         Contrast stretch image.
7888       */
7889       XSetCursorState(display,windows,MagickTrue);
7890       XCheckRefreshWindows(display,windows);
7891       flags=ParseGeometry(levels,&geometry_info);
7892       black_point=geometry_info.rho;
7893       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7894       if ((flags & PercentValue) != 0)
7895         {
7896           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7897           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7898         }
7899       white_point=(double) (*image)->columns*(*image)->rows-white_point;
7900       (void) ContrastStretchImage(*image,black_point,white_point,
7901         exception);
7902       XSetCursorState(display,windows,MagickFalse);
7903       if (windows->image.orphan != MagickFalse )
7904         break;
7905       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7906       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7907       break;
7908     }
7909     case SigmoidalContrastCommand:
7910     {
7911       GeometryInfo
7912         geometry_info;
7913 
7914       MagickStatusType
7915         flags;
7916 
7917       static char
7918         levels[MagickPathExtent] = "3x50%";
7919 
7920       /*
7921         Query user for gamma value.
7922       */
7923       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7924         "Enter contrast and midpoint:",levels);
7925       if (*levels == '\0')
7926         break;
7927       /*
7928         Contrast stretch image.
7929       */
7930       XSetCursorState(display,windows,MagickTrue);
7931       XCheckRefreshWindows(display,windows);
7932       flags=ParseGeometry(levels,&geometry_info);
7933       if ((flags & SigmaValue) == 0)
7934         geometry_info.sigma=1.0*QuantumRange/2.0;
7935       if ((flags & PercentValue) != 0)
7936         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7937       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7938         geometry_info.sigma,exception);
7939       XSetCursorState(display,windows,MagickFalse);
7940       if (windows->image.orphan != MagickFalse )
7941         break;
7942       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7943       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7944       break;
7945     }
7946     case NormalizeCommand:
7947     {
7948       /*
7949         Perform histogram normalization on the image.
7950       */
7951       XSetCursorState(display,windows,MagickTrue);
7952       XCheckRefreshWindows(display,windows);
7953       (void) NormalizeImage(*image,exception);
7954       XSetCursorState(display,windows,MagickFalse);
7955       if (windows->image.orphan != MagickFalse )
7956         break;
7957       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7958       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7959       break;
7960     }
7961     case EqualizeCommand:
7962     {
7963       /*
7964         Perform histogram equalization on the image.
7965       */
7966       XSetCursorState(display,windows,MagickTrue);
7967       XCheckRefreshWindows(display,windows);
7968       (void) EqualizeImage(*image,exception);
7969       XSetCursorState(display,windows,MagickFalse);
7970       if (windows->image.orphan != MagickFalse )
7971         break;
7972       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7973       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7974       break;
7975     }
7976     case NegateCommand:
7977     {
7978       /*
7979         Negate colors in image.
7980       */
7981       XSetCursorState(display,windows,MagickTrue);
7982       XCheckRefreshWindows(display,windows);
7983       (void) NegateImage(*image,MagickFalse,exception);
7984       XSetCursorState(display,windows,MagickFalse);
7985       if (windows->image.orphan != MagickFalse )
7986         break;
7987       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7988       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7989       break;
7990     }
7991     case GrayscaleCommand:
7992     {
7993       /*
7994         Convert image to grayscale.
7995       */
7996       XSetCursorState(display,windows,MagickTrue);
7997       XCheckRefreshWindows(display,windows);
7998       (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7999         GrayscaleType : GrayscaleAlphaType,exception);
8000       XSetCursorState(display,windows,MagickFalse);
8001       if (windows->image.orphan != MagickFalse )
8002         break;
8003       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8004       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8005       break;
8006     }
8007     case MapCommand:
8008     {
8009       Image
8010         *affinity_image;
8011 
8012       static char
8013         filename[MagickPathExtent] = "\0";
8014 
8015       /*
8016         Request image file name from user.
8017       */
8018       XFileBrowserWidget(display,windows,"Map",filename);
8019       if (*filename == '\0')
8020         break;
8021       /*
8022         Map image.
8023       */
8024       XSetCursorState(display,windows,MagickTrue);
8025       XCheckRefreshWindows(display,windows);
8026       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
8027       affinity_image=ReadImage(image_info,exception);
8028       if (affinity_image != (Image *) NULL)
8029         {
8030           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8031           affinity_image=DestroyImage(affinity_image);
8032         }
8033       CatchException(exception);
8034       XSetCursorState(display,windows,MagickFalse);
8035       if (windows->image.orphan != MagickFalse )
8036         break;
8037       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8038       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8039       break;
8040     }
8041     case QuantizeCommand:
8042     {
8043       int
8044         status;
8045 
8046       static char
8047         colors[MagickPathExtent] = "256";
8048 
8049       /*
8050         Query user for maximum number of colors.
8051       */
8052       status=XDialogWidget(display,windows,"Quantize",
8053         "Maximum number of colors:",colors);
8054       if (*colors == '\0')
8055         break;
8056       /*
8057         Color reduce the image.
8058       */
8059       XSetCursorState(display,windows,MagickTrue);
8060       XCheckRefreshWindows(display,windows);
8061       quantize_info.number_colors=StringToUnsignedLong(colors);
8062       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8063         NoDitherMethod;
8064       (void) QuantizeImage(&quantize_info,*image,exception);
8065       XSetCursorState(display,windows,MagickFalse);
8066       if (windows->image.orphan != MagickFalse )
8067         break;
8068       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8069       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8070       break;
8071     }
8072     case DespeckleCommand:
8073     {
8074       Image
8075         *despeckle_image;
8076 
8077       /*
8078         Despeckle image.
8079       */
8080       XSetCursorState(display,windows,MagickTrue);
8081       XCheckRefreshWindows(display,windows);
8082       despeckle_image=DespeckleImage(*image,exception);
8083       if (despeckle_image != (Image *) NULL)
8084         {
8085           *image=DestroyImage(*image);
8086           *image=despeckle_image;
8087         }
8088       CatchException(exception);
8089       XSetCursorState(display,windows,MagickFalse);
8090       if (windows->image.orphan != MagickFalse )
8091         break;
8092       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8093       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8094       break;
8095     }
8096     case EmbossCommand:
8097     {
8098       Image
8099         *emboss_image;
8100 
8101       static char
8102         radius[MagickPathExtent] = "0.0x1.0";
8103 
8104       /*
8105         Query user for emboss radius.
8106       */
8107       (void) XDialogWidget(display,windows,"Emboss",
8108         "Enter the emboss radius and standard deviation:",radius);
8109       if (*radius == '\0')
8110         break;
8111       /*
8112         Reduce noise in the image.
8113       */
8114       XSetCursorState(display,windows,MagickTrue);
8115       XCheckRefreshWindows(display,windows);
8116       flags=ParseGeometry(radius,&geometry_info);
8117       if ((flags & SigmaValue) == 0)
8118         geometry_info.sigma=1.0;
8119       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8120         exception);
8121       if (emboss_image != (Image *) NULL)
8122         {
8123           *image=DestroyImage(*image);
8124           *image=emboss_image;
8125         }
8126       CatchException(exception);
8127       XSetCursorState(display,windows,MagickFalse);
8128       if (windows->image.orphan != MagickFalse )
8129         break;
8130       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8131       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8132       break;
8133     }
8134     case ReduceNoiseCommand:
8135     {
8136       Image
8137         *noise_image;
8138 
8139       static char
8140         radius[MagickPathExtent] = "0";
8141 
8142       /*
8143         Query user for noise radius.
8144       */
8145       (void) XDialogWidget(display,windows,"Reduce Noise",
8146         "Enter the noise radius:",radius);
8147       if (*radius == '\0')
8148         break;
8149       /*
8150         Reduce noise in the image.
8151       */
8152       XSetCursorState(display,windows,MagickTrue);
8153       XCheckRefreshWindows(display,windows);
8154       flags=ParseGeometry(radius,&geometry_info);
8155       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8156         geometry_info.rho,(size_t) geometry_info.rho,exception);
8157       if (noise_image != (Image *) NULL)
8158         {
8159           *image=DestroyImage(*image);
8160           *image=noise_image;
8161         }
8162       CatchException(exception);
8163       XSetCursorState(display,windows,MagickFalse);
8164       if (windows->image.orphan != MagickFalse )
8165         break;
8166       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8167       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8168       break;
8169     }
8170     case AddNoiseCommand:
8171     {
8172       char
8173         **noises;
8174 
8175       Image
8176         *noise_image;
8177 
8178       static char
8179         noise_type[MagickPathExtent] = "Gaussian";
8180 
8181       /*
8182         Add noise to the image.
8183       */
8184       noises=GetCommandOptions(MagickNoiseOptions);
8185       if (noises == (char **) NULL)
8186         break;
8187       XListBrowserWidget(display,windows,&windows->widget,
8188         (const char **) noises,"Add Noise",
8189         "Select a type of noise to add to your image:",noise_type);
8190       noises=DestroyStringList(noises);
8191       if (*noise_type == '\0')
8192         break;
8193       XSetCursorState(display,windows,MagickTrue);
8194       XCheckRefreshWindows(display,windows);
8195       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8196         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8197       if (noise_image != (Image *) NULL)
8198         {
8199           *image=DestroyImage(*image);
8200           *image=noise_image;
8201         }
8202       CatchException(exception);
8203       XSetCursorState(display,windows,MagickFalse);
8204       if (windows->image.orphan != MagickFalse )
8205         break;
8206       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8207       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8208       break;
8209     }
8210     case SharpenCommand:
8211     {
8212       Image
8213         *sharp_image;
8214 
8215       static char
8216         radius[MagickPathExtent] = "0.0x1.0";
8217 
8218       /*
8219         Query user for sharpen radius.
8220       */
8221       (void) XDialogWidget(display,windows,"Sharpen",
8222         "Enter the sharpen radius and standard deviation:",radius);
8223       if (*radius == '\0')
8224         break;
8225       /*
8226         Sharpen image scanlines.
8227       */
8228       XSetCursorState(display,windows,MagickTrue);
8229       XCheckRefreshWindows(display,windows);
8230       flags=ParseGeometry(radius,&geometry_info);
8231       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8232         exception);
8233       if (sharp_image != (Image *) NULL)
8234         {
8235           *image=DestroyImage(*image);
8236           *image=sharp_image;
8237         }
8238       CatchException(exception);
8239       XSetCursorState(display,windows,MagickFalse);
8240       if (windows->image.orphan != MagickFalse )
8241         break;
8242       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8243       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8244       break;
8245     }
8246     case BlurCommand:
8247     {
8248       Image
8249         *blur_image;
8250 
8251       static char
8252         radius[MagickPathExtent] = "0.0x1.0";
8253 
8254       /*
8255         Query user for blur radius.
8256       */
8257       (void) XDialogWidget(display,windows,"Blur",
8258         "Enter the blur radius and standard deviation:",radius);
8259       if (*radius == '\0')
8260         break;
8261       /*
8262         Blur an image.
8263       */
8264       XSetCursorState(display,windows,MagickTrue);
8265       XCheckRefreshWindows(display,windows);
8266       flags=ParseGeometry(radius,&geometry_info);
8267       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8268         exception);
8269       if (blur_image != (Image *) NULL)
8270         {
8271           *image=DestroyImage(*image);
8272           *image=blur_image;
8273         }
8274       CatchException(exception);
8275       XSetCursorState(display,windows,MagickFalse);
8276       if (windows->image.orphan != MagickFalse )
8277         break;
8278       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8279       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8280       break;
8281     }
8282     case ThresholdCommand:
8283     {
8284       double
8285         threshold;
8286 
8287       static char
8288         factor[MagickPathExtent] = "128";
8289 
8290       /*
8291         Query user for threshold value.
8292       */
8293       (void) XDialogWidget(display,windows,"Threshold",
8294         "Enter threshold value:",factor);
8295       if (*factor == '\0')
8296         break;
8297       /*
8298         Gamma correct image.
8299       */
8300       XSetCursorState(display,windows,MagickTrue);
8301       XCheckRefreshWindows(display,windows);
8302       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8303       (void) BilevelImage(*image,threshold,exception);
8304       XSetCursorState(display,windows,MagickFalse);
8305       if (windows->image.orphan != MagickFalse )
8306         break;
8307       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8308       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8309       break;
8310     }
8311     case EdgeDetectCommand:
8312     {
8313       Image
8314         *edge_image;
8315 
8316       static char
8317         radius[MagickPathExtent] = "0";
8318 
8319       /*
8320         Query user for edge factor.
8321       */
8322       (void) XDialogWidget(display,windows,"Detect Edges",
8323         "Enter the edge detect radius:",radius);
8324       if (*radius == '\0')
8325         break;
8326       /*
8327         Detect edge in image.
8328       */
8329       XSetCursorState(display,windows,MagickTrue);
8330       XCheckRefreshWindows(display,windows);
8331       flags=ParseGeometry(radius,&geometry_info);
8332       edge_image=EdgeImage(*image,geometry_info.rho,exception);
8333       if (edge_image != (Image *) NULL)
8334         {
8335           *image=DestroyImage(*image);
8336           *image=edge_image;
8337         }
8338       CatchException(exception);
8339       XSetCursorState(display,windows,MagickFalse);
8340       if (windows->image.orphan != MagickFalse )
8341         break;
8342       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8343       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8344       break;
8345     }
8346     case SpreadCommand:
8347     {
8348       Image
8349         *spread_image;
8350 
8351       static char
8352         amount[MagickPathExtent] = "2";
8353 
8354       /*
8355         Query user for spread amount.
8356       */
8357       (void) XDialogWidget(display,windows,"Spread",
8358         "Enter the displacement amount:",amount);
8359       if (*amount == '\0')
8360         break;
8361       /*
8362         Displace image pixels by a random amount.
8363       */
8364       XSetCursorState(display,windows,MagickTrue);
8365       XCheckRefreshWindows(display,windows);
8366       flags=ParseGeometry(amount,&geometry_info);
8367       spread_image=EdgeImage(*image,geometry_info.rho,exception);
8368       if (spread_image != (Image *) NULL)
8369         {
8370           *image=DestroyImage(*image);
8371           *image=spread_image;
8372         }
8373       CatchException(exception);
8374       XSetCursorState(display,windows,MagickFalse);
8375       if (windows->image.orphan != MagickFalse )
8376         break;
8377       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8378       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8379       break;
8380     }
8381     case ShadeCommand:
8382     {
8383       Image
8384         *shade_image;
8385 
8386       int
8387         status;
8388 
8389       static char
8390         geometry[MagickPathExtent] = "30x30";
8391 
8392       /*
8393         Query user for the shade geometry.
8394       */
8395       status=XDialogWidget(display,windows,"Shade",
8396         "Enter the azimuth and elevation of the light source:",geometry);
8397       if (*geometry == '\0')
8398         break;
8399       /*
8400         Shade image pixels.
8401       */
8402       XSetCursorState(display,windows,MagickTrue);
8403       XCheckRefreshWindows(display,windows);
8404       flags=ParseGeometry(geometry,&geometry_info);
8405       if ((flags & SigmaValue) == 0)
8406         geometry_info.sigma=1.0;
8407       shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
8408         geometry_info.rho,geometry_info.sigma,exception);
8409       if (shade_image != (Image *) NULL)
8410         {
8411           *image=DestroyImage(*image);
8412           *image=shade_image;
8413         }
8414       CatchException(exception);
8415       XSetCursorState(display,windows,MagickFalse);
8416       if (windows->image.orphan != MagickFalse )
8417         break;
8418       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8419       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8420       break;
8421     }
8422     case RaiseCommand:
8423     {
8424       static char
8425         bevel_width[MagickPathExtent] = "10";
8426 
8427       /*
8428         Query user for bevel width.
8429       */
8430       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8431       if (*bevel_width == '\0')
8432         break;
8433       /*
8434         Raise an image.
8435       */
8436       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8437         exception);
8438       XSetCursorState(display,windows,MagickTrue);
8439       XCheckRefreshWindows(display,windows);
8440       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8441         exception);
8442       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8443       XSetCursorState(display,windows,MagickFalse);
8444       if (windows->image.orphan != MagickFalse )
8445         break;
8446       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8447       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8448       break;
8449     }
8450     case SegmentCommand:
8451     {
8452       static char
8453         threshold[MagickPathExtent] = "1.0x1.5";
8454 
8455       /*
8456         Query user for smoothing threshold.
8457       */
8458       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8459         threshold);
8460       if (*threshold == '\0')
8461         break;
8462       /*
8463         Segment an image.
8464       */
8465       XSetCursorState(display,windows,MagickTrue);
8466       XCheckRefreshWindows(display,windows);
8467       flags=ParseGeometry(threshold,&geometry_info);
8468       if ((flags & SigmaValue) == 0)
8469         geometry_info.sigma=1.0;
8470       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8471         geometry_info.sigma,exception);
8472       XSetCursorState(display,windows,MagickFalse);
8473       if (windows->image.orphan != MagickFalse )
8474         break;
8475       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8476       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8477       break;
8478     }
8479     case SepiaToneCommand:
8480     {
8481       double
8482         threshold;
8483 
8484       Image
8485         *sepia_image;
8486 
8487       static char
8488         factor[MagickPathExtent] = "80%";
8489 
8490       /*
8491         Query user for sepia-tone factor.
8492       */
8493       (void) XDialogWidget(display,windows,"Sepia Tone",
8494         "Enter the sepia tone factor (0 - 99.9%):",factor);
8495       if (*factor == '\0')
8496         break;
8497       /*
8498         Sepia tone image pixels.
8499       */
8500       XSetCursorState(display,windows,MagickTrue);
8501       XCheckRefreshWindows(display,windows);
8502       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8503       sepia_image=SepiaToneImage(*image,threshold,exception);
8504       if (sepia_image != (Image *) NULL)
8505         {
8506           *image=DestroyImage(*image);
8507           *image=sepia_image;
8508         }
8509       CatchException(exception);
8510       XSetCursorState(display,windows,MagickFalse);
8511       if (windows->image.orphan != MagickFalse )
8512         break;
8513       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8514       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8515       break;
8516     }
8517     case SolarizeCommand:
8518     {
8519       double
8520         threshold;
8521 
8522       static char
8523         factor[MagickPathExtent] = "60%";
8524 
8525       /*
8526         Query user for solarize factor.
8527       */
8528       (void) XDialogWidget(display,windows,"Solarize",
8529         "Enter the solarize factor (0 - 99.9%):",factor);
8530       if (*factor == '\0')
8531         break;
8532       /*
8533         Solarize image pixels.
8534       */
8535       XSetCursorState(display,windows,MagickTrue);
8536       XCheckRefreshWindows(display,windows);
8537       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8538       (void) SolarizeImage(*image,threshold,exception);
8539       XSetCursorState(display,windows,MagickFalse);
8540       if (windows->image.orphan != MagickFalse )
8541         break;
8542       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8543       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8544       break;
8545     }
8546     case SwirlCommand:
8547     {
8548       Image
8549         *swirl_image;
8550 
8551       static char
8552         degrees[MagickPathExtent] = "60";
8553 
8554       /*
8555         Query user for swirl angle.
8556       */
8557       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8558         degrees);
8559       if (*degrees == '\0')
8560         break;
8561       /*
8562         Swirl image pixels about the center.
8563       */
8564       XSetCursorState(display,windows,MagickTrue);
8565       XCheckRefreshWindows(display,windows);
8566       flags=ParseGeometry(degrees,&geometry_info);
8567       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8568         exception);
8569       if (swirl_image != (Image *) NULL)
8570         {
8571           *image=DestroyImage(*image);
8572           *image=swirl_image;
8573         }
8574       CatchException(exception);
8575       XSetCursorState(display,windows,MagickFalse);
8576       if (windows->image.orphan != MagickFalse )
8577         break;
8578       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8579       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8580       break;
8581     }
8582     case ImplodeCommand:
8583     {
8584       Image
8585         *implode_image;
8586 
8587       static char
8588         factor[MagickPathExtent] = "0.3";
8589 
8590       /*
8591         Query user for implode factor.
8592       */
8593       (void) XDialogWidget(display,windows,"Implode",
8594         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8595       if (*factor == '\0')
8596         break;
8597       /*
8598         Implode image pixels about the center.
8599       */
8600       XSetCursorState(display,windows,MagickTrue);
8601       XCheckRefreshWindows(display,windows);
8602       flags=ParseGeometry(factor,&geometry_info);
8603       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8604         exception);
8605       if (implode_image != (Image *) NULL)
8606         {
8607           *image=DestroyImage(*image);
8608           *image=implode_image;
8609         }
8610       CatchException(exception);
8611       XSetCursorState(display,windows,MagickFalse);
8612       if (windows->image.orphan != MagickFalse )
8613         break;
8614       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8615       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8616       break;
8617     }
8618     case VignetteCommand:
8619     {
8620       Image
8621         *vignette_image;
8622 
8623       static char
8624         geometry[MagickPathExtent] = "0x20";
8625 
8626       /*
8627         Query user for the vignette geometry.
8628       */
8629       (void) XDialogWidget(display,windows,"Vignette",
8630         "Enter the radius, sigma, and x and y offsets:",geometry);
8631       if (*geometry == '\0')
8632         break;
8633       /*
8634         Soften the edges of the image in vignette style
8635       */
8636       XSetCursorState(display,windows,MagickTrue);
8637       XCheckRefreshWindows(display,windows);
8638       flags=ParseGeometry(geometry,&geometry_info);
8639       if ((flags & SigmaValue) == 0)
8640         geometry_info.sigma=1.0;
8641       if ((flags & XiValue) == 0)
8642         geometry_info.xi=0.1*(*image)->columns;
8643       if ((flags & PsiValue) == 0)
8644         geometry_info.psi=0.1*(*image)->rows;
8645       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8646         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8647         exception);
8648       if (vignette_image != (Image *) NULL)
8649         {
8650           *image=DestroyImage(*image);
8651           *image=vignette_image;
8652         }
8653       CatchException(exception);
8654       XSetCursorState(display,windows,MagickFalse);
8655       if (windows->image.orphan != MagickFalse )
8656         break;
8657       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8658       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8659       break;
8660     }
8661     case WaveCommand:
8662     {
8663       Image
8664         *wave_image;
8665 
8666       static char
8667         geometry[MagickPathExtent] = "25x150";
8668 
8669       /*
8670         Query user for the wave geometry.
8671       */
8672       (void) XDialogWidget(display,windows,"Wave",
8673         "Enter the amplitude and length of the wave:",geometry);
8674       if (*geometry == '\0')
8675         break;
8676       /*
8677         Alter an image along a sine wave.
8678       */
8679       XSetCursorState(display,windows,MagickTrue);
8680       XCheckRefreshWindows(display,windows);
8681       flags=ParseGeometry(geometry,&geometry_info);
8682       if ((flags & SigmaValue) == 0)
8683         geometry_info.sigma=1.0;
8684       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8685         (*image)->interpolate,exception);
8686       if (wave_image != (Image *) NULL)
8687         {
8688           *image=DestroyImage(*image);
8689           *image=wave_image;
8690         }
8691       CatchException(exception);
8692       XSetCursorState(display,windows,MagickFalse);
8693       if (windows->image.orphan != MagickFalse )
8694         break;
8695       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8696       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8697       break;
8698     }
8699     case OilPaintCommand:
8700     {
8701       Image
8702         *paint_image;
8703 
8704       static char
8705         radius[MagickPathExtent] = "0";
8706 
8707       /*
8708         Query user for circular neighborhood radius.
8709       */
8710       (void) XDialogWidget(display,windows,"Oil Paint",
8711         "Enter the mask radius:",radius);
8712       if (*radius == '\0')
8713         break;
8714       /*
8715         OilPaint image scanlines.
8716       */
8717       XSetCursorState(display,windows,MagickTrue);
8718       XCheckRefreshWindows(display,windows);
8719       flags=ParseGeometry(radius,&geometry_info);
8720       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8721         exception);
8722       if (paint_image != (Image *) NULL)
8723         {
8724           *image=DestroyImage(*image);
8725           *image=paint_image;
8726         }
8727       CatchException(exception);
8728       XSetCursorState(display,windows,MagickFalse);
8729       if (windows->image.orphan != MagickFalse )
8730         break;
8731       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8732       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8733       break;
8734     }
8735     case CharcoalDrawCommand:
8736     {
8737       Image
8738         *charcoal_image;
8739 
8740       static char
8741         radius[MagickPathExtent] = "0x1";
8742 
8743       /*
8744         Query user for charcoal radius.
8745       */
8746       (void) XDialogWidget(display,windows,"Charcoal Draw",
8747         "Enter the charcoal radius and sigma:",radius);
8748       if (*radius == '\0')
8749         break;
8750       /*
8751         Charcoal the image.
8752       */
8753       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8754         exception);
8755       XSetCursorState(display,windows,MagickTrue);
8756       XCheckRefreshWindows(display,windows);
8757       flags=ParseGeometry(radius,&geometry_info);
8758       if ((flags & SigmaValue) == 0)
8759         geometry_info.sigma=geometry_info.rho;
8760       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8761         exception);
8762       if (charcoal_image != (Image *) NULL)
8763         {
8764           *image=DestroyImage(*image);
8765           *image=charcoal_image;
8766         }
8767       CatchException(exception);
8768       XSetCursorState(display,windows,MagickFalse);
8769       if (windows->image.orphan != MagickFalse )
8770         break;
8771       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8772       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8773       break;
8774     }
8775     case AnnotateCommand:
8776     {
8777       /*
8778         Annotate the image with text.
8779       */
8780       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8781       if (status == MagickFalse)
8782         {
8783           XNoticeWidget(display,windows,"Unable to annotate X image",
8784             (*image)->filename);
8785           break;
8786         }
8787       break;
8788     }
8789     case DrawCommand:
8790     {
8791       /*
8792         Draw image.
8793       */
8794       status=XDrawEditImage(display,resource_info,windows,image,exception);
8795       if (status == MagickFalse)
8796         {
8797           XNoticeWidget(display,windows,"Unable to draw on the X image",
8798             (*image)->filename);
8799           break;
8800         }
8801       break;
8802     }
8803     case ColorCommand:
8804     {
8805       /*
8806         Color edit.
8807       */
8808       status=XColorEditImage(display,resource_info,windows,image,exception);
8809       if (status == MagickFalse)
8810         {
8811           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8812             (*image)->filename);
8813           break;
8814         }
8815       break;
8816     }
8817     case MatteCommand:
8818     {
8819       /*
8820         Matte edit.
8821       */
8822       status=XMatteEditImage(display,resource_info,windows,image,exception);
8823       if (status == MagickFalse)
8824         {
8825           XNoticeWidget(display,windows,"Unable to matte edit X image",
8826             (*image)->filename);
8827           break;
8828         }
8829       break;
8830     }
8831     case CompositeCommand:
8832     {
8833       /*
8834         Composite image.
8835       */
8836       status=XCompositeImage(display,resource_info,windows,*image,
8837         exception);
8838       if (status == MagickFalse)
8839         {
8840           XNoticeWidget(display,windows,"Unable to composite X image",
8841             (*image)->filename);
8842           break;
8843         }
8844       break;
8845     }
8846     case AddBorderCommand:
8847     {
8848       Image
8849         *border_image;
8850 
8851       static char
8852         geometry[MagickPathExtent] = "6x6";
8853 
8854       /*
8855         Query user for border color and geometry.
8856       */
8857       XColorBrowserWidget(display,windows,"Select",color);
8858       if (*color == '\0')
8859         break;
8860       (void) XDialogWidget(display,windows,"Add Border",
8861         "Enter border geometry:",geometry);
8862       if (*geometry == '\0')
8863         break;
8864       /*
8865         Add a border to the image.
8866       */
8867       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8868         exception);
8869       XSetCursorState(display,windows,MagickTrue);
8870       XCheckRefreshWindows(display,windows);
8871       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8872         exception);
8873       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8874         exception);
8875       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8876         exception);
8877       if (border_image != (Image *) NULL)
8878         {
8879           *image=DestroyImage(*image);
8880           *image=border_image;
8881         }
8882       CatchException(exception);
8883       XSetCursorState(display,windows,MagickFalse);
8884       if (windows->image.orphan != MagickFalse )
8885         break;
8886       windows->image.window_changes.width=(int) (*image)->columns;
8887       windows->image.window_changes.height=(int) (*image)->rows;
8888       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8889       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8890       break;
8891     }
8892     case AddFrameCommand:
8893     {
8894       FrameInfo
8895         frame_info;
8896 
8897       Image
8898         *frame_image;
8899 
8900       static char
8901         geometry[MagickPathExtent] = "6x6";
8902 
8903       /*
8904         Query user for frame color and geometry.
8905       */
8906       XColorBrowserWidget(display,windows,"Select",color);
8907       if (*color == '\0')
8908         break;
8909       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8910         geometry);
8911       if (*geometry == '\0')
8912         break;
8913       /*
8914         Surround image with an ornamental border.
8915       */
8916       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8917         exception);
8918       XSetCursorState(display,windows,MagickTrue);
8919       XCheckRefreshWindows(display,windows);
8920       (void) QueryColorCompliance(color,AllCompliance,&(*image)->alpha_color,
8921         exception);
8922       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8923         exception);
8924       frame_info.width=page_geometry.width;
8925       frame_info.height=page_geometry.height;
8926       frame_info.outer_bevel=page_geometry.x;
8927       frame_info.inner_bevel=page_geometry.y;
8928       frame_info.x=(ssize_t) frame_info.width;
8929       frame_info.y=(ssize_t) frame_info.height;
8930       frame_info.width=(*image)->columns+2*frame_info.width;
8931       frame_info.height=(*image)->rows+2*frame_info.height;
8932       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8933       if (frame_image != (Image *) NULL)
8934         {
8935           *image=DestroyImage(*image);
8936           *image=frame_image;
8937         }
8938       CatchException(exception);
8939       XSetCursorState(display,windows,MagickFalse);
8940       if (windows->image.orphan != MagickFalse )
8941         break;
8942       windows->image.window_changes.width=(int) (*image)->columns;
8943       windows->image.window_changes.height=(int) (*image)->rows;
8944       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8945       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8946       break;
8947     }
8948     case CommentCommand:
8949     {
8950       const char
8951         *value;
8952 
8953       FILE
8954         *file;
8955 
8956       int
8957         unique_file;
8958 
8959       /*
8960         Edit image comment.
8961       */
8962       unique_file=AcquireUniqueFileResource(image_info->filename);
8963       if (unique_file == -1)
8964         XNoticeWidget(display,windows,"Unable to edit image comment",
8965           image_info->filename);
8966       value=GetImageProperty(*image,"comment",exception);
8967       if (value == (char *) NULL)
8968         unique_file=close(unique_file)-1;
8969       else
8970         {
8971           register const char
8972             *p;
8973 
8974           file=fdopen(unique_file,"w");
8975           if (file == (FILE *) NULL)
8976             {
8977               XNoticeWidget(display,windows,"Unable to edit image comment",
8978                 image_info->filename);
8979               break;
8980             }
8981           for (p=value; *p != '\0'; p++)
8982             (void) fputc((int) *p,file);
8983           (void) fputc('\n',file);
8984           (void) fclose(file);
8985         }
8986       XSetCursorState(display,windows,MagickTrue);
8987       XCheckRefreshWindows(display,windows);
8988       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8989         exception);
8990       if (status == MagickFalse)
8991         XNoticeWidget(display,windows,"Unable to edit image comment",
8992           (char *) NULL);
8993       else
8994         {
8995           char
8996             *comment;
8997 
8998           comment=FileToString(image_info->filename,~0UL,exception);
8999           if (comment != (char *) NULL)
9000             {
9001               (void) SetImageProperty(*image,"comment",comment,exception);
9002               (*image)->taint=MagickTrue;
9003             }
9004         }
9005       (void) RelinquishUniqueFileResource(image_info->filename);
9006       XSetCursorState(display,windows,MagickFalse);
9007       break;
9008     }
9009     case LaunchCommand:
9010     {
9011       /*
9012         Launch program.
9013       */
9014       XSetCursorState(display,windows,MagickTrue);
9015       XCheckRefreshWindows(display,windows);
9016       (void) AcquireUniqueFilename(filename);
9017       (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
9018         filename);
9019       status=WriteImage(image_info,*image,exception);
9020       if (status == MagickFalse)
9021         XNoticeWidget(display,windows,"Unable to launch image editor",
9022           (char *) NULL);
9023       else
9024         {
9025           nexus=ReadImage(resource_info->image_info,exception);
9026           CatchException(exception);
9027           XClientMessage(display,windows->image.id,windows->im_protocols,
9028             windows->im_next_image,CurrentTime);
9029         }
9030       (void) RelinquishUniqueFileResource(filename);
9031       XSetCursorState(display,windows,MagickFalse);
9032       break;
9033     }
9034     case RegionofInterestCommand:
9035     {
9036       /*
9037         Apply an image processing technique to a region of interest.
9038       */
9039       (void) XROIImage(display,resource_info,windows,image,exception);
9040       break;
9041     }
9042     case InfoCommand:
9043       break;
9044     case ZoomCommand:
9045     {
9046       /*
9047         Zoom image.
9048       */
9049       if (windows->magnify.mapped != MagickFalse )
9050         (void) XRaiseWindow(display,windows->magnify.id);
9051       else
9052         {
9053           /*
9054             Make magnify image.
9055           */
9056           XSetCursorState(display,windows,MagickTrue);
9057           (void) XMapRaised(display,windows->magnify.id);
9058           XSetCursorState(display,windows,MagickFalse);
9059         }
9060       break;
9061     }
9062     case ShowPreviewCommand:
9063     {
9064       char
9065         **previews,
9066         value[MagickPathExtent];
9067 
9068       Image
9069         *preview_image;
9070 
9071       PreviewType
9072         preview;
9073 
9074       static char
9075         preview_type[MagickPathExtent] = "Gamma";
9076 
9077       /*
9078         Select preview type from menu.
9079       */
9080       previews=GetCommandOptions(MagickPreviewOptions);
9081       if (previews == (char **) NULL)
9082         break;
9083       XListBrowserWidget(display,windows,&windows->widget,
9084         (const char **) previews,"Preview",
9085         "Select an enhancement, effect, or F/X:",preview_type);
9086       previews=DestroyStringList(previews);
9087       if (*preview_type == '\0')
9088         break;
9089       /*
9090         Show image preview.
9091       */
9092       XSetCursorState(display,windows,MagickTrue);
9093       XCheckRefreshWindows(display,windows);
9094       preview=(PreviewType) ParseCommandOption(MagickPreviewOptions,
9095         MagickFalse,preview_type);
9096       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9097         windows->image.id);
9098       (void) SetImageProperty(*image,"group",value,exception);
9099       (void) DeleteImageProperty(*image,"label");
9100       (void) SetImageProperty(*image,"label","Preview",exception);
9101       preview_image=PreviewImage(*image,preview,exception);
9102       if (preview_image == (Image *) NULL)
9103         break;
9104       (void) AcquireUniqueFilename(filename);
9105       (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
9106         "show:%s",filename);
9107       status=WriteImage(image_info,preview_image,exception);
9108       (void) RelinquishUniqueFileResource(filename);
9109       preview_image=DestroyImage(preview_image);
9110       if (status == MagickFalse)
9111         XNoticeWidget(display,windows,"Unable to show image preview",
9112           (*image)->filename);
9113       XDelay(display,1500);
9114       XSetCursorState(display,windows,MagickFalse);
9115       break;
9116     }
9117     case ShowHistogramCommand:
9118     {
9119       char
9120         value[MagickPathExtent];
9121 
9122       Image
9123         *histogram_image;
9124 
9125       /*
9126         Show image histogram.
9127       */
9128       XSetCursorState(display,windows,MagickTrue);
9129       XCheckRefreshWindows(display,windows);
9130       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9131         windows->image.id);
9132       (void) SetImageProperty(*image,"group",value,exception);
9133       (void) DeleteImageProperty(*image,"label");
9134       (void) SetImageProperty(*image,"label","Histogram",exception);
9135       (void) AcquireUniqueFilename(filename);
9136       (void) FormatLocaleString((*image)->filename,MagickPathExtent,"histogram:%s",
9137         filename);
9138       status=WriteImage(image_info,*image,exception);
9139       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9140       histogram_image=ReadImage(image_info,exception);
9141       (void) RelinquishUniqueFileResource(filename);
9142       if (histogram_image == (Image *) NULL)
9143         break;
9144       (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9145         "show:%s",filename);
9146       status=WriteImage(image_info,histogram_image,exception);
9147       histogram_image=DestroyImage(histogram_image);
9148       if (status == MagickFalse)
9149         XNoticeWidget(display,windows,"Unable to show histogram",
9150           (*image)->filename);
9151       XDelay(display,1500);
9152       XSetCursorState(display,windows,MagickFalse);
9153       break;
9154     }
9155     case ShowMatteCommand:
9156     {
9157       char
9158         value[MagickPathExtent];
9159 
9160       Image
9161         *matte_image;
9162 
9163       if ((*image)->alpha_trait == UndefinedPixelTrait)
9164         {
9165           XNoticeWidget(display,windows,
9166             "Image does not have any matte information",(*image)->filename);
9167           break;
9168         }
9169       /*
9170         Show image matte.
9171       */
9172       XSetCursorState(display,windows,MagickTrue);
9173       XCheckRefreshWindows(display,windows);
9174       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9175         windows->image.id);
9176       (void) SetImageProperty(*image,"group",value,exception);
9177       (void) DeleteImageProperty(*image,"label");
9178       (void) SetImageProperty(*image,"label","Matte",exception);
9179       (void) AcquireUniqueFilename(filename);
9180       (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9181         filename);
9182       status=WriteImage(image_info,*image,exception);
9183       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9184       matte_image=ReadImage(image_info,exception);
9185       (void) RelinquishUniqueFileResource(filename);
9186       if (matte_image == (Image *) NULL)
9187         break;
9188       (void) FormatLocaleString(matte_image->filename,MagickPathExtent,"show:%s",
9189         filename);
9190       status=WriteImage(image_info,matte_image,exception);
9191       matte_image=DestroyImage(matte_image);
9192       if (status == MagickFalse)
9193         XNoticeWidget(display,windows,"Unable to show matte",
9194           (*image)->filename);
9195       XDelay(display,1500);
9196       XSetCursorState(display,windows,MagickFalse);
9197       break;
9198     }
9199     case BackgroundCommand:
9200     {
9201       /*
9202         Background image.
9203       */
9204       status=XBackgroundImage(display,resource_info,windows,image,exception);
9205       if (status == MagickFalse)
9206         break;
9207       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9208       if (nexus != (Image *) NULL)
9209         XClientMessage(display,windows->image.id,windows->im_protocols,
9210           windows->im_next_image,CurrentTime);
9211       break;
9212     }
9213     case SlideShowCommand:
9214     {
9215       static char
9216         delay[MagickPathExtent] = "5";
9217 
9218       /*
9219         Display next image after pausing.
9220       */
9221       (void) XDialogWidget(display,windows,"Slide Show",
9222         "Pause how many 1/100ths of a second between images:",delay);
9223       if (*delay == '\0')
9224         break;
9225       resource_info->delay=StringToUnsignedLong(delay);
9226       XClientMessage(display,windows->image.id,windows->im_protocols,
9227         windows->im_next_image,CurrentTime);
9228       break;
9229     }
9230     case PreferencesCommand:
9231     {
9232       /*
9233         Set user preferences.
9234       */
9235       status=XPreferencesWidget(display,resource_info,windows);
9236       if (status == MagickFalse)
9237         break;
9238       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9239       if (nexus != (Image *) NULL)
9240         XClientMessage(display,windows->image.id,windows->im_protocols,
9241           windows->im_next_image,CurrentTime);
9242       break;
9243     }
9244     case HelpCommand:
9245     {
9246       /*
9247         User requested help.
9248       */
9249       XTextViewWidget(display,resource_info,windows,MagickFalse,
9250         "Help Viewer - Display",DisplayHelp);
9251       break;
9252     }
9253     case BrowseDocumentationCommand:
9254     {
9255       Atom
9256         mozilla_atom;
9257 
9258       Window
9259         mozilla_window,
9260         root_window;
9261 
9262       /*
9263         Browse the ImageMagick documentation.
9264       */
9265       root_window=XRootWindow(display,XDefaultScreen(display));
9266       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9267       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9268       if (mozilla_window != (Window) NULL)
9269         {
9270           char
9271             command[MagickPathExtent],
9272             *url;
9273 
9274           /*
9275             Display documentation using Netscape remote control.
9276           */
9277           url=GetMagickHomeURL();
9278           (void) FormatLocaleString(command,MagickPathExtent,
9279             "openurl(%s,new-tab)",url);
9280           url=DestroyString(url);
9281           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9282           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9283             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9284           XSetCursorState(display,windows,MagickFalse);
9285           break;
9286         }
9287       XSetCursorState(display,windows,MagickTrue);
9288       XCheckRefreshWindows(display,windows);
9289       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9290         exception);
9291       if (status == MagickFalse)
9292         XNoticeWidget(display,windows,"Unable to browse documentation",
9293           (char *) NULL);
9294       XDelay(display,1500);
9295       XSetCursorState(display,windows,MagickFalse);
9296       break;
9297     }
9298     case VersionCommand:
9299     {
9300       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9301         GetMagickCopyright());
9302       break;
9303     }
9304     case SaveToUndoBufferCommand:
9305       break;
9306     default:
9307     {
9308       (void) XBell(display,0);
9309       break;
9310     }
9311   }
9312   image_info=DestroyImageInfo(image_info);
9313   return(nexus);
9314 }
9315 
9316 /*
9317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9318 %                                                                             %
9319 %                                                                             %
9320 %                                                                             %
9321 +   X M a g n i f y I m a g e                                                 %
9322 %                                                                             %
9323 %                                                                             %
9324 %                                                                             %
9325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9326 %
9327 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9328 %  The magnified portion is displayed in a separate window.
9329 %
9330 %  The format of the XMagnifyImage method is:
9331 %
9332 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9333 %        ExceptionInfo *exception)
9334 %
9335 %  A description of each parameter follows:
9336 %
9337 %    o display: Specifies a connection to an X server;  returned from
9338 %      XOpenDisplay.
9339 %
9340 %    o windows: Specifies a pointer to a XWindows structure.
9341 %
9342 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9343 %      the entire image is refreshed.
9344 %
9345 %    o exception: return any errors or warnings in this structure.
9346 %
9347 */
XMagnifyImage(Display * display,XWindows * windows,XEvent * event,ExceptionInfo * exception)9348 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9349   ExceptionInfo *exception)
9350 {
9351   char
9352     text[MagickPathExtent];
9353 
9354   register int
9355     x,
9356     y;
9357 
9358   size_t
9359     state;
9360 
9361   /*
9362     Update magnified image until the mouse button is released.
9363   */
9364   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9365   state=DefaultState;
9366   x=event->xbutton.x;
9367   y=event->xbutton.y;
9368   windows->magnify.x=(int) windows->image.x+x;
9369   windows->magnify.y=(int) windows->image.y+y;
9370   do
9371   {
9372     /*
9373       Map and unmap Info widget as text cursor crosses its boundaries.
9374     */
9375     if (windows->info.mapped != MagickFalse )
9376       {
9377         if ((x < (int) (windows->info.x+windows->info.width)) &&
9378             (y < (int) (windows->info.y+windows->info.height)))
9379           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9380       }
9381     else
9382       if ((x > (int) (windows->info.x+windows->info.width)) ||
9383           (y > (int) (windows->info.y+windows->info.height)))
9384         (void) XMapWindow(display,windows->info.id);
9385     if (windows->info.mapped != MagickFalse )
9386       {
9387         /*
9388           Display pointer position.
9389         */
9390         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9391           windows->magnify.x,windows->magnify.y);
9392         XInfoWidget(display,windows,text);
9393       }
9394     /*
9395       Wait for next event.
9396     */
9397     XScreenEvent(display,windows,event,exception);
9398     switch (event->type)
9399     {
9400       case ButtonPress:
9401         break;
9402       case ButtonRelease:
9403       {
9404         /*
9405           User has finished magnifying image.
9406         */
9407         x=event->xbutton.x;
9408         y=event->xbutton.y;
9409         state|=ExitState;
9410         break;
9411       }
9412       case Expose:
9413         break;
9414       case MotionNotify:
9415       {
9416         x=event->xmotion.x;
9417         y=event->xmotion.y;
9418         break;
9419       }
9420       default:
9421         break;
9422     }
9423     /*
9424       Check boundary conditions.
9425     */
9426     if (x < 0)
9427       x=0;
9428     else
9429       if (x >= (int) windows->image.width)
9430         x=(int) windows->image.width-1;
9431     if (y < 0)
9432       y=0;
9433     else
9434      if (y >= (int) windows->image.height)
9435        y=(int) windows->image.height-1;
9436   } while ((state & ExitState) == 0);
9437   /*
9438     Display magnified image.
9439   */
9440   XSetCursorState(display,windows,MagickFalse);
9441 }
9442 
9443 /*
9444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9445 %                                                                             %
9446 %                                                                             %
9447 %                                                                             %
9448 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9449 %                                                                             %
9450 %                                                                             %
9451 %                                                                             %
9452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9453 %
9454 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9455 %  pixel as specified by the key symbol.
9456 %
9457 %  The format of the XMagnifyWindowCommand method is:
9458 %
9459 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9460 %        const MagickStatusType state,const KeySym key_symbol,
9461 %        ExceptionInfo *exception)
9462 %
9463 %  A description of each parameter follows:
9464 %
9465 %    o display: Specifies a connection to an X server; returned from
9466 %      XOpenDisplay.
9467 %
9468 %    o windows: Specifies a pointer to a XWindows structure.
9469 %
9470 %    o state: key mask.
9471 %
9472 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9473 %      to trim.
9474 %
9475 %    o exception: return any errors or warnings in this structure.
9476 %
9477 */
XMagnifyWindowCommand(Display * display,XWindows * windows,const MagickStatusType state,const KeySym key_symbol,ExceptionInfo * exception)9478 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9479   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9480 {
9481   unsigned int
9482     quantum;
9483 
9484   /*
9485     User specified a magnify factor or position.
9486   */
9487   quantum=1;
9488   if ((state & Mod1Mask) != 0)
9489     quantum=10;
9490   switch ((int) key_symbol)
9491   {
9492     case QuitCommand:
9493     {
9494       (void) XWithdrawWindow(display,windows->magnify.id,
9495         windows->magnify.screen);
9496       break;
9497     }
9498     case XK_Home:
9499     case XK_KP_Home:
9500     {
9501       windows->magnify.x=(int) windows->image.width/2;
9502       windows->magnify.y=(int) windows->image.height/2;
9503       break;
9504     }
9505     case XK_Left:
9506     case XK_KP_Left:
9507     {
9508       if (windows->magnify.x > 0)
9509         windows->magnify.x-=quantum;
9510       break;
9511     }
9512     case XK_Up:
9513     case XK_KP_Up:
9514     {
9515       if (windows->magnify.y > 0)
9516         windows->magnify.y-=quantum;
9517       break;
9518     }
9519     case XK_Right:
9520     case XK_KP_Right:
9521     {
9522       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9523         windows->magnify.x+=quantum;
9524       break;
9525     }
9526     case XK_Down:
9527     case XK_KP_Down:
9528     {
9529       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9530         windows->magnify.y+=quantum;
9531       break;
9532     }
9533     case XK_0:
9534     case XK_1:
9535     case XK_2:
9536     case XK_3:
9537     case XK_4:
9538     case XK_5:
9539     case XK_6:
9540     case XK_7:
9541     case XK_8:
9542     case XK_9:
9543     {
9544       windows->magnify.data=(key_symbol-XK_0);
9545       break;
9546     }
9547     case XK_KP_0:
9548     case XK_KP_1:
9549     case XK_KP_2:
9550     case XK_KP_3:
9551     case XK_KP_4:
9552     case XK_KP_5:
9553     case XK_KP_6:
9554     case XK_KP_7:
9555     case XK_KP_8:
9556     case XK_KP_9:
9557     {
9558       windows->magnify.data=(key_symbol-XK_KP_0);
9559       break;
9560     }
9561     default:
9562       break;
9563   }
9564   XMakeMagnifyImage(display,windows,exception);
9565 }
9566 
9567 /*
9568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9569 %                                                                             %
9570 %                                                                             %
9571 %                                                                             %
9572 +   X M a k e P a n I m a g e                                                 %
9573 %                                                                             %
9574 %                                                                             %
9575 %                                                                             %
9576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9577 %
9578 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9579 %  icon window.
9580 %
9581 %  The format of the XMakePanImage method is:
9582 %
9583 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9584 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9585 %
9586 %  A description of each parameter follows:
9587 %
9588 %    o display: Specifies a connection to an X server;  returned from
9589 %      XOpenDisplay.
9590 %
9591 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9592 %
9593 %    o windows: Specifies a pointer to a XWindows structure.
9594 %
9595 %    o image: the image.
9596 %
9597 %    o exception: return any errors or warnings in this structure.
9598 %
9599 */
XMakePanImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)9600 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9601   XWindows *windows,Image *image,ExceptionInfo *exception)
9602 {
9603   MagickStatusType
9604     status;
9605 
9606   /*
9607     Create and display image for panning icon.
9608   */
9609   XSetCursorState(display,windows,MagickTrue);
9610   XCheckRefreshWindows(display,windows);
9611   windows->pan.x=(int) windows->image.x;
9612   windows->pan.y=(int) windows->image.y;
9613   status=XMakeImage(display,resource_info,&windows->pan,image,
9614     windows->pan.width,windows->pan.height,exception);
9615   if (status == MagickFalse)
9616     ThrowXWindowException(ResourceLimitError,
9617      "MemoryAllocationFailed",image->filename);
9618   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9619     windows->pan.pixmap);
9620   (void) XClearWindow(display,windows->pan.id);
9621   XDrawPanRectangle(display,windows);
9622   XSetCursorState(display,windows,MagickFalse);
9623 }
9624 
9625 /*
9626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9627 %                                                                             %
9628 %                                                                             %
9629 %                                                                             %
9630 +   X M a t t a E d i t I m a g e                                             %
9631 %                                                                             %
9632 %                                                                             %
9633 %                                                                             %
9634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9635 %
9636 %  XMatteEditImage() allows the user to interactively change the Matte channel
9637 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9638 %  before the matte information is stored.
9639 %
9640 %  The format of the XMatteEditImage method is:
9641 %
9642 %      MagickBooleanType XMatteEditImage(Display *display,
9643 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9644 %        ExceptionInfo *exception)
9645 %
9646 %  A description of each parameter follows:
9647 %
9648 %    o display: Specifies a connection to an X server;  returned from
9649 %      XOpenDisplay.
9650 %
9651 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9652 %
9653 %    o windows: Specifies a pointer to a XWindows structure.
9654 %
9655 %    o image: the image; returned from ReadImage.
9656 %
9657 %    o exception: return any errors or warnings in this structure.
9658 %
9659 */
XMatteEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)9660 static MagickBooleanType XMatteEditImage(Display *display,
9661   XResourceInfo *resource_info,XWindows *windows,Image **image,
9662   ExceptionInfo *exception)
9663 {
9664   static char
9665     matte[MagickPathExtent] = "0";
9666 
9667   static const char
9668     *MatteEditMenu[] =
9669     {
9670       "Method",
9671       "Border Color",
9672       "Fuzz",
9673       "Matte Value",
9674       "Undo",
9675       "Help",
9676       "Dismiss",
9677       (char *) NULL
9678     };
9679 
9680   static const ModeType
9681     MatteEditCommands[] =
9682     {
9683       MatteEditMethod,
9684       MatteEditBorderCommand,
9685       MatteEditFuzzCommand,
9686       MatteEditValueCommand,
9687       MatteEditUndoCommand,
9688       MatteEditHelpCommand,
9689       MatteEditDismissCommand
9690     };
9691 
9692   static PaintMethod
9693     method = PointMethod;
9694 
9695   static XColor
9696     border_color = { 0, 0, 0, 0, 0, 0 };
9697 
9698   char
9699     command[MagickPathExtent],
9700     text[MagickPathExtent];
9701 
9702   Cursor
9703     cursor;
9704 
9705   int
9706     entry,
9707     id,
9708     x,
9709     x_offset,
9710     y,
9711     y_offset;
9712 
9713   register int
9714     i;
9715 
9716   register Quantum
9717     *q;
9718 
9719   unsigned int
9720     height,
9721     width;
9722 
9723   size_t
9724     state;
9725 
9726   XEvent
9727     event;
9728 
9729   /*
9730     Map Command widget.
9731   */
9732   (void) CloneString(&windows->command.name,"Matte Edit");
9733   windows->command.data=4;
9734   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9735   (void) XMapRaised(display,windows->command.id);
9736   XClientMessage(display,windows->image.id,windows->im_protocols,
9737     windows->im_update_widget,CurrentTime);
9738   /*
9739     Make cursor.
9740   */
9741   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9742     resource_info->background_color,resource_info->foreground_color);
9743   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9744   /*
9745     Track pointer until button 1 is pressed.
9746   */
9747   XQueryPosition(display,windows->image.id,&x,&y);
9748   (void) XSelectInput(display,windows->image.id,
9749     windows->image.attributes.event_mask | PointerMotionMask);
9750   state=DefaultState;
9751   do
9752   {
9753     if (windows->info.mapped != MagickFalse )
9754       {
9755         /*
9756           Display pointer position.
9757         */
9758         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9759           x+windows->image.x,y+windows->image.y);
9760         XInfoWidget(display,windows,text);
9761       }
9762     /*
9763       Wait for next event.
9764     */
9765     XScreenEvent(display,windows,&event,exception);
9766     if (event.xany.window == windows->command.id)
9767       {
9768         /*
9769           Select a command from the Command widget.
9770         */
9771         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9772         if (id < 0)
9773           {
9774             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9775             continue;
9776           }
9777         switch (MatteEditCommands[id])
9778         {
9779           case MatteEditMethod:
9780           {
9781             char
9782               **methods;
9783 
9784             /*
9785               Select a method from the pop-up menu.
9786             */
9787             methods=GetCommandOptions(MagickMethodOptions);
9788             if (methods == (char **) NULL)
9789               break;
9790             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9791               (const char **) methods,command);
9792             if (entry >= 0)
9793               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9794                 MagickFalse,methods[entry]);
9795             methods=DestroyStringList(methods);
9796             break;
9797           }
9798           case MatteEditBorderCommand:
9799           {
9800             const char
9801               *ColorMenu[MaxNumberPens];
9802 
9803             int
9804               pen_number;
9805 
9806             /*
9807               Initialize menu selections.
9808             */
9809             for (i=0; i < (int) (MaxNumberPens-2); i++)
9810               ColorMenu[i]=resource_info->pen_colors[i];
9811             ColorMenu[MaxNumberPens-2]="Browser...";
9812             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9813             /*
9814               Select a pen color from the pop-up menu.
9815             */
9816             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9817               (const char **) ColorMenu,command);
9818             if (pen_number < 0)
9819               break;
9820             if (pen_number == (MaxNumberPens-2))
9821               {
9822                 static char
9823                   color_name[MagickPathExtent] = "gray";
9824 
9825                 /*
9826                   Select a pen color from a dialog.
9827                 */
9828                 resource_info->pen_colors[pen_number]=color_name;
9829                 XColorBrowserWidget(display,windows,"Select",color_name);
9830                 if (*color_name == '\0')
9831                   break;
9832               }
9833             /*
9834               Set border color.
9835             */
9836             (void) XParseColor(display,windows->map_info->colormap,
9837               resource_info->pen_colors[pen_number],&border_color);
9838             break;
9839           }
9840           case MatteEditFuzzCommand:
9841           {
9842             static char
9843               fuzz[MagickPathExtent];
9844 
9845             static const char
9846               *FuzzMenu[] =
9847               {
9848                 "0%",
9849                 "2%",
9850                 "5%",
9851                 "10%",
9852                 "15%",
9853                 "Dialog...",
9854                 (char *) NULL,
9855               };
9856 
9857             /*
9858               Select a command from the pop-up menu.
9859             */
9860             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9861               command);
9862             if (entry < 0)
9863               break;
9864             if (entry != 5)
9865               {
9866                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9867                   QuantumRange+1.0);
9868                 break;
9869               }
9870             (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9871             (void) XDialogWidget(display,windows,"Ok",
9872               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9873             if (*fuzz == '\0')
9874               break;
9875             (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9876             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9877               1.0);
9878             break;
9879           }
9880           case MatteEditValueCommand:
9881           {
9882             static char
9883               message[MagickPathExtent];
9884 
9885             static const char
9886               *MatteMenu[] =
9887               {
9888                 "Opaque",
9889                 "Transparent",
9890                 "Dialog...",
9891                 (char *) NULL,
9892               };
9893 
9894             /*
9895               Select a command from the pop-up menu.
9896             */
9897             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9898               command);
9899             if (entry < 0)
9900               break;
9901             if (entry != 2)
9902               {
9903                 (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
9904                   OpaqueAlpha);
9905                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9906                   (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
9907                     (Quantum) TransparentAlpha);
9908                 break;
9909               }
9910             (void) FormatLocaleString(message,MagickPathExtent,
9911               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9912               QuantumRange);
9913             (void) XDialogWidget(display,windows,"Matte",message,matte);
9914             if (*matte == '\0')
9915               break;
9916             break;
9917           }
9918           case MatteEditUndoCommand:
9919           {
9920             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9921               image,exception);
9922             break;
9923           }
9924           case MatteEditHelpCommand:
9925           {
9926             XTextViewWidget(display,resource_info,windows,MagickFalse,
9927               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9928             break;
9929           }
9930           case MatteEditDismissCommand:
9931           {
9932             /*
9933               Prematurely exit.
9934             */
9935             state|=EscapeState;
9936             state|=ExitState;
9937             break;
9938           }
9939           default:
9940             break;
9941         }
9942         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9943         continue;
9944       }
9945     switch (event.type)
9946     {
9947       case ButtonPress:
9948       {
9949         if (event.xbutton.button != Button1)
9950           break;
9951         if ((event.xbutton.window != windows->image.id) &&
9952             (event.xbutton.window != windows->magnify.id))
9953           break;
9954         /*
9955           Update matte data.
9956         */
9957         x=event.xbutton.x;
9958         y=event.xbutton.y;
9959         (void) XMagickCommand(display,resource_info,windows,
9960           SaveToUndoBufferCommand,image,exception);
9961         state|=UpdateConfigurationState;
9962         break;
9963       }
9964       case ButtonRelease:
9965       {
9966         if (event.xbutton.button != Button1)
9967           break;
9968         if ((event.xbutton.window != windows->image.id) &&
9969             (event.xbutton.window != windows->magnify.id))
9970           break;
9971         /*
9972           Update colormap information.
9973         */
9974         x=event.xbutton.x;
9975         y=event.xbutton.y;
9976         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9977         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9978         XInfoWidget(display,windows,text);
9979         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9980         state&=(~UpdateConfigurationState);
9981         break;
9982       }
9983       case Expose:
9984         break;
9985       case KeyPress:
9986       {
9987         char
9988           command[MagickPathExtent];
9989 
9990         KeySym
9991           key_symbol;
9992 
9993         if (event.xkey.window == windows->magnify.id)
9994           {
9995             Window
9996               window;
9997 
9998             window=windows->magnify.id;
9999             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10000           }
10001         if (event.xkey.window != windows->image.id)
10002           break;
10003         /*
10004           Respond to a user key press.
10005         */
10006         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10007           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10008         switch ((int) key_symbol)
10009         {
10010           case XK_Escape:
10011           case XK_F20:
10012           {
10013             /*
10014               Prematurely exit.
10015             */
10016             state|=ExitState;
10017             break;
10018           }
10019           case XK_F1:
10020           case XK_Help:
10021           {
10022             XTextViewWidget(display,resource_info,windows,MagickFalse,
10023               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10024             break;
10025           }
10026           default:
10027           {
10028             (void) XBell(display,0);
10029             break;
10030           }
10031         }
10032         break;
10033       }
10034       case MotionNotify:
10035       {
10036         /*
10037           Map and unmap Info widget as cursor crosses its boundaries.
10038         */
10039         x=event.xmotion.x;
10040         y=event.xmotion.y;
10041         if (windows->info.mapped != MagickFalse )
10042           {
10043             if ((x < (int) (windows->info.x+windows->info.width)) &&
10044                 (y < (int) (windows->info.y+windows->info.height)))
10045               (void) XWithdrawWindow(display,windows->info.id,
10046                 windows->info.screen);
10047           }
10048         else
10049           if ((x > (int) (windows->info.x+windows->info.width)) ||
10050               (y > (int) (windows->info.y+windows->info.height)))
10051             (void) XMapWindow(display,windows->info.id);
10052         break;
10053       }
10054       default:
10055         break;
10056     }
10057     if (event.xany.window == windows->magnify.id)
10058       {
10059         x=windows->magnify.x-windows->image.x;
10060         y=windows->magnify.y-windows->image.y;
10061       }
10062     x_offset=x;
10063     y_offset=y;
10064     if ((state & UpdateConfigurationState) != 0)
10065       {
10066         CacheView
10067           *image_view;
10068 
10069         int
10070           x,
10071           y;
10072 
10073         /*
10074           Matte edit is relative to image configuration.
10075         */
10076         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10077           MagickTrue);
10078         XPutPixel(windows->image.ximage,x_offset,y_offset,
10079           windows->pixel_info->background_color.pixel);
10080         width=(unsigned int) (*image)->columns;
10081         height=(unsigned int) (*image)->rows;
10082         x=0;
10083         y=0;
10084         if (windows->image.crop_geometry != (char *) NULL)
10085           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10086             &height);
10087         x_offset=(int) (width*(windows->image.x+x_offset)/
10088           windows->image.ximage->width+x);
10089         y_offset=(int) (height*(windows->image.y+y_offset)/
10090           windows->image.ximage->height+y);
10091         if ((x_offset < 0) || (y_offset < 0))
10092           continue;
10093         if ((x_offset >= (int) (*image)->columns) ||
10094             (y_offset >= (int) (*image)->rows))
10095           continue;
10096         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10097           return(MagickFalse);
10098         if ((*image)->alpha_trait == UndefinedPixelTrait)
10099           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10100         image_view=AcquireAuthenticCacheView(*image,exception);
10101         switch (method)
10102         {
10103           case PointMethod:
10104           default:
10105           {
10106             /*
10107               Update matte information using point algorithm.
10108             */
10109             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10110               (ssize_t) y_offset,1,1,exception);
10111             if (q == (Quantum *) NULL)
10112               break;
10113             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10114             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10115             break;
10116           }
10117           case ReplaceMethod:
10118           {
10119             PixelInfo
10120               pixel,
10121               target;
10122 
10123             /*
10124               Update matte information using replace algorithm.
10125             */
10126             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10127               x_offset,(ssize_t) y_offset,&target,exception);
10128             for (y=0; y < (int) (*image)->rows; y++)
10129             {
10130               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10131                 (*image)->columns,1,exception);
10132               if (q == (Quantum *) NULL)
10133                 break;
10134               for (x=0; x < (int) (*image)->columns; x++)
10135               {
10136                 GetPixelInfoPixel(*image,q,&pixel);
10137                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10138                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10139                 q+=GetPixelChannels(*image);
10140               }
10141               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10142                 break;
10143             }
10144             break;
10145           }
10146           case FloodfillMethod:
10147           case FillToBorderMethod:
10148           {
10149             ChannelType
10150               channel_mask;
10151 
10152             DrawInfo
10153               *draw_info;
10154 
10155             PixelInfo
10156               target;
10157 
10158             /*
10159               Update matte information using floodfill algorithm.
10160             */
10161             (void) GetOneVirtualPixelInfo(*image,
10162               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10163               y_offset,&target,exception);
10164             if (method == FillToBorderMethod)
10165               {
10166                 target.red=(double) ScaleShortToQuantum(
10167                   border_color.red);
10168                 target.green=(double) ScaleShortToQuantum(
10169                   border_color.green);
10170                 target.blue=(double) ScaleShortToQuantum(
10171                   border_color.blue);
10172               }
10173             draw_info=CloneDrawInfo(resource_info->image_info,
10174               (DrawInfo *) NULL);
10175             draw_info->fill.alpha=(double) ClampToQuantum(
10176               StringToDouble(matte,(char **) NULL));
10177             channel_mask=SetImageChannelMask(*image,AlphaChannel);
10178             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10179               x_offset,(ssize_t) y_offset,
10180               method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
10181             (void) SetPixelChannelMask(*image,channel_mask);
10182             draw_info=DestroyDrawInfo(draw_info);
10183             break;
10184           }
10185           case ResetMethod:
10186           {
10187             /*
10188               Update matte information using reset algorithm.
10189             */
10190             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10191               return(MagickFalse);
10192             for (y=0; y < (int) (*image)->rows; y++)
10193             {
10194               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10195                 (*image)->columns,1,exception);
10196               if (q == (Quantum *) NULL)
10197                 break;
10198               for (x=0; x < (int) (*image)->columns; x++)
10199               {
10200                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10201                 q+=GetPixelChannels(*image);
10202               }
10203               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10204                 break;
10205             }
10206             if (StringToLong(matte) == (long) OpaqueAlpha)
10207               (*image)->alpha_trait=UndefinedPixelTrait;
10208             break;
10209           }
10210         }
10211         image_view=DestroyCacheView(image_view);
10212         state&=(~UpdateConfigurationState);
10213       }
10214   } while ((state & ExitState) == 0);
10215   (void) XSelectInput(display,windows->image.id,
10216     windows->image.attributes.event_mask);
10217   XSetCursorState(display,windows,MagickFalse);
10218   (void) XFreeCursor(display,cursor);
10219   return(MagickTrue);
10220 }
10221 
10222 /*
10223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10224 %                                                                             %
10225 %                                                                             %
10226 %                                                                             %
10227 +   X O p e n I m a g e                                                       %
10228 %                                                                             %
10229 %                                                                             %
10230 %                                                                             %
10231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10232 %
10233 %  XOpenImage() loads an image from a file.
10234 %
10235 %  The format of the XOpenImage method is:
10236 %
10237 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10238 %       XWindows *windows,const unsigned int command)
10239 %
10240 %  A description of each parameter follows:
10241 %
10242 %    o display: Specifies a connection to an X server; returned from
10243 %      XOpenDisplay.
10244 %
10245 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10246 %
10247 %    o windows: Specifies a pointer to a XWindows structure.
10248 %
10249 %    o command: A value other than zero indicates that the file is selected
10250 %      from the command line argument list.
10251 %
10252 */
XOpenImage(Display * display,XResourceInfo * resource_info,XWindows * windows,const MagickBooleanType command)10253 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10254   XWindows *windows,const MagickBooleanType command)
10255 {
10256   const MagickInfo
10257     *magick_info;
10258 
10259   ExceptionInfo
10260     *exception;
10261 
10262   Image
10263     *nexus;
10264 
10265   ImageInfo
10266     *image_info;
10267 
10268   static char
10269     filename[MagickPathExtent] = "\0";
10270 
10271   /*
10272     Request file name from user.
10273   */
10274   if (command == MagickFalse)
10275     XFileBrowserWidget(display,windows,"Open",filename);
10276   else
10277     {
10278       char
10279         **filelist,
10280         **files;
10281 
10282       int
10283         count,
10284         status;
10285 
10286       register int
10287         i,
10288         j;
10289 
10290       /*
10291         Select next image from the command line.
10292       */
10293       status=XGetCommand(display,windows->image.id,&files,&count);
10294       if (status == 0)
10295         {
10296           ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10297           return((Image *) NULL);
10298         }
10299       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10300       if (filelist == (char **) NULL)
10301         {
10302           ThrowXWindowException(ResourceLimitError,
10303             "MemoryAllocationFailed","...");
10304           (void) XFreeStringList(files);
10305           return((Image *) NULL);
10306         }
10307       j=0;
10308       for (i=1; i < count; i++)
10309         if (*files[i] != '-')
10310           filelist[j++]=files[i];
10311       filelist[j]=(char *) NULL;
10312       XListBrowserWidget(display,windows,&windows->widget,
10313         (const char **) filelist,"Load","Select Image to Load:",filename);
10314       filelist=(char **) RelinquishMagickMemory(filelist);
10315       (void) XFreeStringList(files);
10316     }
10317   if (*filename == '\0')
10318     return((Image *) NULL);
10319   image_info=CloneImageInfo(resource_info->image_info);
10320   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10321     (void *) NULL);
10322   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10323   exception=AcquireExceptionInfo();
10324   (void) SetImageInfo(image_info,0,exception);
10325   if (LocaleCompare(image_info->magick,"X") == 0)
10326     {
10327       char
10328         seconds[MagickPathExtent];
10329 
10330       /*
10331         User may want to delay the X server screen grab.
10332       */
10333       (void) CopyMagickString(seconds,"0",MagickPathExtent);
10334       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10335         seconds);
10336       if (*seconds == '\0')
10337         return((Image *) NULL);
10338       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10339     }
10340   magick_info=GetMagickInfo(image_info->magick,exception);
10341   if ((magick_info != (const MagickInfo *) NULL) &&
10342       GetMagickRawSupport(magick_info) == MagickTrue)
10343     {
10344       char
10345         geometry[MagickPathExtent];
10346 
10347       /*
10348         Request image size from the user.
10349       */
10350       (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10351       if (image_info->size != (char *) NULL)
10352         (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10353       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10354         geometry);
10355       (void) CloneString(&image_info->size,geometry);
10356     }
10357   /*
10358     Load the image.
10359   */
10360   XSetCursorState(display,windows,MagickTrue);
10361   XCheckRefreshWindows(display,windows);
10362   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10363   nexus=ReadImage(image_info,exception);
10364   CatchException(exception);
10365   XSetCursorState(display,windows,MagickFalse);
10366   if (nexus != (Image *) NULL)
10367     XClientMessage(display,windows->image.id,windows->im_protocols,
10368       windows->im_next_image,CurrentTime);
10369   else
10370     {
10371       char
10372         *text,
10373         **textlist;
10374 
10375       /*
10376         Unknown image format.
10377       */
10378       text=FileToString(filename,~0UL,exception);
10379       if (text == (char *) NULL)
10380         return((Image *) NULL);
10381       textlist=StringToList(text);
10382       if (textlist != (char **) NULL)
10383         {
10384           char
10385             title[MagickPathExtent];
10386 
10387           register int
10388             i;
10389 
10390           (void) FormatLocaleString(title,MagickPathExtent,
10391             "Unknown format: %s",filename);
10392           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10393             (const char **) textlist);
10394           for (i=0; textlist[i] != (char *) NULL; i++)
10395             textlist[i]=DestroyString(textlist[i]);
10396           textlist=(char **) RelinquishMagickMemory(textlist);
10397         }
10398       text=DestroyString(text);
10399     }
10400   exception=DestroyExceptionInfo(exception);
10401   image_info=DestroyImageInfo(image_info);
10402   return(nexus);
10403 }
10404 
10405 /*
10406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10407 %                                                                             %
10408 %                                                                             %
10409 %                                                                             %
10410 +   X P a n I m a g e                                                         %
10411 %                                                                             %
10412 %                                                                             %
10413 %                                                                             %
10414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10415 %
10416 %  XPanImage() pans the image until the mouse button is released.
10417 %
10418 %  The format of the XPanImage method is:
10419 %
10420 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10421 %        ExceptionInfo *exception)
10422 %
10423 %  A description of each parameter follows:
10424 %
10425 %    o display: Specifies a connection to an X server;  returned from
10426 %      XOpenDisplay.
10427 %
10428 %    o windows: Specifies a pointer to a XWindows structure.
10429 %
10430 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10431 %      the entire image is refreshed.
10432 %
10433 %    o exception: return any errors or warnings in this structure.
10434 %
10435 */
XPanImage(Display * display,XWindows * windows,XEvent * event,ExceptionInfo * exception)10436 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10437   ExceptionInfo *exception)
10438 {
10439   char
10440     text[MagickPathExtent];
10441 
10442   Cursor
10443     cursor;
10444 
10445   double
10446     x_factor,
10447     y_factor;
10448 
10449   RectangleInfo
10450     pan_info;
10451 
10452   size_t
10453     state;
10454 
10455   /*
10456     Define cursor.
10457   */
10458   if ((windows->image.ximage->width > (int) windows->image.width) &&
10459       (windows->image.ximage->height > (int) windows->image.height))
10460     cursor=XCreateFontCursor(display,XC_fleur);
10461   else
10462     if (windows->image.ximage->width > (int) windows->image.width)
10463       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10464     else
10465       if (windows->image.ximage->height > (int) windows->image.height)
10466         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10467       else
10468         cursor=XCreateFontCursor(display,XC_arrow);
10469   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10470   /*
10471     Pan image as pointer moves until the mouse button is released.
10472   */
10473   x_factor=(double) windows->image.ximage->width/windows->pan.width;
10474   y_factor=(double) windows->image.ximage->height/windows->pan.height;
10475   pan_info.width=windows->pan.width*windows->image.width/
10476     windows->image.ximage->width;
10477   pan_info.height=windows->pan.height*windows->image.height/
10478     windows->image.ximage->height;
10479   pan_info.x=0;
10480   pan_info.y=0;
10481   state=UpdateConfigurationState;
10482   do
10483   {
10484     switch (event->type)
10485     {
10486       case ButtonPress:
10487       {
10488         /*
10489           User choose an initial pan location.
10490         */
10491         pan_info.x=(ssize_t) event->xbutton.x;
10492         pan_info.y=(ssize_t) event->xbutton.y;
10493         state|=UpdateConfigurationState;
10494         break;
10495       }
10496       case ButtonRelease:
10497       {
10498         /*
10499           User has finished panning the image.
10500         */
10501         pan_info.x=(ssize_t) event->xbutton.x;
10502         pan_info.y=(ssize_t) event->xbutton.y;
10503         state|=UpdateConfigurationState | ExitState;
10504         break;
10505       }
10506       case MotionNotify:
10507       {
10508         pan_info.x=(ssize_t) event->xmotion.x;
10509         pan_info.y=(ssize_t) event->xmotion.y;
10510         state|=UpdateConfigurationState;
10511       }
10512       default:
10513         break;
10514     }
10515     if ((state & UpdateConfigurationState) != 0)
10516       {
10517         /*
10518           Check boundary conditions.
10519         */
10520         if (pan_info.x < (ssize_t) (pan_info.width/2))
10521           pan_info.x=0;
10522         else
10523           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10524         if (pan_info.x < 0)
10525           pan_info.x=0;
10526         else
10527           if ((int) (pan_info.x+windows->image.width) >
10528               windows->image.ximage->width)
10529             pan_info.x=(ssize_t)
10530               (windows->image.ximage->width-windows->image.width);
10531         if (pan_info.y < (ssize_t) (pan_info.height/2))
10532           pan_info.y=0;
10533         else
10534           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10535         if (pan_info.y < 0)
10536           pan_info.y=0;
10537         else
10538           if ((int) (pan_info.y+windows->image.height) >
10539               windows->image.ximage->height)
10540             pan_info.y=(ssize_t)
10541               (windows->image.ximage->height-windows->image.height);
10542         if ((windows->image.x != (int) pan_info.x) ||
10543             (windows->image.y != (int) pan_info.y))
10544           {
10545             /*
10546               Display image pan offset.
10547             */
10548             windows->image.x=(int) pan_info.x;
10549             windows->image.y=(int) pan_info.y;
10550             (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
10551               windows->image.width,windows->image.height,windows->image.x,
10552               windows->image.y);
10553             XInfoWidget(display,windows,text);
10554             /*
10555               Refresh Image window.
10556             */
10557             XDrawPanRectangle(display,windows);
10558             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10559           }
10560         state&=(~UpdateConfigurationState);
10561       }
10562     /*
10563       Wait for next event.
10564     */
10565     if ((state & ExitState) == 0)
10566       XScreenEvent(display,windows,event,exception);
10567   } while ((state & ExitState) == 0);
10568   /*
10569     Restore cursor.
10570   */
10571   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10572   (void) XFreeCursor(display,cursor);
10573   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10574 }
10575 
10576 /*
10577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10578 %                                                                             %
10579 %                                                                             %
10580 %                                                                             %
10581 +   X P a s t e I m a g e                                                     %
10582 %                                                                             %
10583 %                                                                             %
10584 %                                                                             %
10585 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10586 %
10587 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10588 %  window image at a location the user chooses with the pointer.
10589 %
10590 %  The format of the XPasteImage method is:
10591 %
10592 %      MagickBooleanType XPasteImage(Display *display,
10593 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10594 %        ExceptionInfo *exception)
10595 %
10596 %  A description of each parameter follows:
10597 %
10598 %    o display: Specifies a connection to an X server;  returned from
10599 %      XOpenDisplay.
10600 %
10601 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10602 %
10603 %    o windows: Specifies a pointer to a XWindows structure.
10604 %
10605 %    o image: the image; returned from ReadImage.
10606 %
10607 %    o exception: return any errors or warnings in this structure.
10608 %
10609 */
XPasteImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)10610 static MagickBooleanType XPasteImage(Display *display,
10611   XResourceInfo *resource_info,XWindows *windows,Image *image,
10612   ExceptionInfo *exception)
10613 {
10614   static const char
10615     *PasteMenu[] =
10616     {
10617       "Operator",
10618       "Help",
10619       "Dismiss",
10620       (char *) NULL
10621     };
10622 
10623   static const ModeType
10624     PasteCommands[] =
10625     {
10626       PasteOperatorsCommand,
10627       PasteHelpCommand,
10628       PasteDismissCommand
10629     };
10630 
10631   static CompositeOperator
10632     compose = CopyCompositeOp;
10633 
10634   char
10635     text[MagickPathExtent];
10636 
10637   Cursor
10638     cursor;
10639 
10640   Image
10641     *paste_image;
10642 
10643   int
10644     entry,
10645     id,
10646     x,
10647     y;
10648 
10649   double
10650     scale_factor;
10651 
10652   RectangleInfo
10653     highlight_info,
10654     paste_info;
10655 
10656   unsigned int
10657     height,
10658     width;
10659 
10660   size_t
10661     state;
10662 
10663   XEvent
10664     event;
10665 
10666   /*
10667     Copy image.
10668   */
10669   if (resource_info->copy_image == (Image *) NULL)
10670     return(MagickFalse);
10671   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10672   /*
10673     Map Command widget.
10674   */
10675   (void) CloneString(&windows->command.name,"Paste");
10676   windows->command.data=1;
10677   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10678   (void) XMapRaised(display,windows->command.id);
10679   XClientMessage(display,windows->image.id,windows->im_protocols,
10680     windows->im_update_widget,CurrentTime);
10681   /*
10682     Track pointer until button 1 is pressed.
10683   */
10684   XSetCursorState(display,windows,MagickFalse);
10685   XQueryPosition(display,windows->image.id,&x,&y);
10686   (void) XSelectInput(display,windows->image.id,
10687     windows->image.attributes.event_mask | PointerMotionMask);
10688   paste_info.x=(ssize_t) windows->image.x+x;
10689   paste_info.y=(ssize_t) windows->image.y+y;
10690   paste_info.width=0;
10691   paste_info.height=0;
10692   cursor=XCreateFontCursor(display,XC_ul_angle);
10693   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10694   state=DefaultState;
10695   do
10696   {
10697     if (windows->info.mapped != MagickFalse )
10698       {
10699         /*
10700           Display pointer position.
10701         */
10702         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
10703           (long) paste_info.x,(long) paste_info.y);
10704         XInfoWidget(display,windows,text);
10705       }
10706     highlight_info=paste_info;
10707     highlight_info.x=paste_info.x-windows->image.x;
10708     highlight_info.y=paste_info.y-windows->image.y;
10709     XHighlightRectangle(display,windows->image.id,
10710       windows->image.highlight_context,&highlight_info);
10711     /*
10712       Wait for next event.
10713     */
10714     XScreenEvent(display,windows,&event,exception);
10715     XHighlightRectangle(display,windows->image.id,
10716       windows->image.highlight_context,&highlight_info);
10717     if (event.xany.window == windows->command.id)
10718       {
10719         /*
10720           Select a command from the Command widget.
10721         */
10722         id=XCommandWidget(display,windows,PasteMenu,&event);
10723         if (id < 0)
10724           continue;
10725         switch (PasteCommands[id])
10726         {
10727           case PasteOperatorsCommand:
10728           {
10729             char
10730               command[MagickPathExtent],
10731               **operators;
10732 
10733             /*
10734               Select a command from the pop-up menu.
10735             */
10736             operators=GetCommandOptions(MagickComposeOptions);
10737             if (operators == (char **) NULL)
10738               break;
10739             entry=XMenuWidget(display,windows,PasteMenu[id],
10740               (const char **) operators,command);
10741             if (entry >= 0)
10742               compose=(CompositeOperator) ParseCommandOption(
10743                 MagickComposeOptions,MagickFalse,operators[entry]);
10744             operators=DestroyStringList(operators);
10745             break;
10746           }
10747           case PasteHelpCommand:
10748           {
10749             XTextViewWidget(display,resource_info,windows,MagickFalse,
10750               "Help Viewer - Image Composite",ImagePasteHelp);
10751             break;
10752           }
10753           case PasteDismissCommand:
10754           {
10755             /*
10756               Prematurely exit.
10757             */
10758             state|=EscapeState;
10759             state|=ExitState;
10760             break;
10761           }
10762           default:
10763             break;
10764         }
10765         continue;
10766       }
10767     switch (event.type)
10768     {
10769       case ButtonPress:
10770       {
10771         if (image->debug != MagickFalse )
10772           (void) LogMagickEvent(X11Event,GetMagickModule(),
10773             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10774             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10775         if (event.xbutton.button != Button1)
10776           break;
10777         if (event.xbutton.window != windows->image.id)
10778           break;
10779         /*
10780           Paste rectangle is relative to image configuration.
10781         */
10782         width=(unsigned int) image->columns;
10783         height=(unsigned int) image->rows;
10784         x=0;
10785         y=0;
10786         if (windows->image.crop_geometry != (char *) NULL)
10787           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10788             &width,&height);
10789         scale_factor=(double) windows->image.ximage->width/width;
10790         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10791         scale_factor=(double) windows->image.ximage->height/height;
10792         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10793         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10794         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10795         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10796         break;
10797       }
10798       case ButtonRelease:
10799       {
10800         if (image->debug != MagickFalse )
10801           (void) LogMagickEvent(X11Event,GetMagickModule(),
10802             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10803             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10804         if (event.xbutton.button != Button1)
10805           break;
10806         if (event.xbutton.window != windows->image.id)
10807           break;
10808         if ((paste_info.width != 0) && (paste_info.height != 0))
10809           {
10810             /*
10811               User has selected the location of the paste image.
10812             */
10813             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10814             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10815             state|=ExitState;
10816           }
10817         break;
10818       }
10819       case Expose:
10820         break;
10821       case KeyPress:
10822       {
10823         char
10824           command[MagickPathExtent];
10825 
10826         KeySym
10827           key_symbol;
10828 
10829         int
10830           length;
10831 
10832         if (event.xkey.window != windows->image.id)
10833           break;
10834         /*
10835           Respond to a user key press.
10836         */
10837         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10838           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10839         *(command+length)='\0';
10840         if (image->debug != MagickFalse )
10841           (void) LogMagickEvent(X11Event,GetMagickModule(),
10842             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10843         switch ((int) key_symbol)
10844         {
10845           case XK_Escape:
10846           case XK_F20:
10847           {
10848             /*
10849               Prematurely exit.
10850             */
10851             paste_image=DestroyImage(paste_image);
10852             state|=EscapeState;
10853             state|=ExitState;
10854             break;
10855           }
10856           case XK_F1:
10857           case XK_Help:
10858           {
10859             (void) XSetFunction(display,windows->image.highlight_context,
10860               GXcopy);
10861             XTextViewWidget(display,resource_info,windows,MagickFalse,
10862               "Help Viewer - Image Composite",ImagePasteHelp);
10863             (void) XSetFunction(display,windows->image.highlight_context,
10864               GXinvert);
10865             break;
10866           }
10867           default:
10868           {
10869             (void) XBell(display,0);
10870             break;
10871           }
10872         }
10873         break;
10874       }
10875       case MotionNotify:
10876       {
10877         /*
10878           Map and unmap Info widget as text cursor crosses its boundaries.
10879         */
10880         x=event.xmotion.x;
10881         y=event.xmotion.y;
10882         if (windows->info.mapped != MagickFalse )
10883           {
10884             if ((x < (int) (windows->info.x+windows->info.width)) &&
10885                 (y < (int) (windows->info.y+windows->info.height)))
10886               (void) XWithdrawWindow(display,windows->info.id,
10887                 windows->info.screen);
10888           }
10889         else
10890           if ((x > (int) (windows->info.x+windows->info.width)) ||
10891               (y > (int) (windows->info.y+windows->info.height)))
10892             (void) XMapWindow(display,windows->info.id);
10893         paste_info.x=(ssize_t) windows->image.x+x;
10894         paste_info.y=(ssize_t) windows->image.y+y;
10895         break;
10896       }
10897       default:
10898       {
10899         if (image->debug != MagickFalse )
10900           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10901             event.type);
10902         break;
10903       }
10904     }
10905   } while ((state & ExitState) == 0);
10906   (void) XSelectInput(display,windows->image.id,
10907     windows->image.attributes.event_mask);
10908   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10909   XSetCursorState(display,windows,MagickFalse);
10910   (void) XFreeCursor(display,cursor);
10911   if ((state & EscapeState) != 0)
10912     return(MagickTrue);
10913   /*
10914     Image pasting is relative to image configuration.
10915   */
10916   XSetCursorState(display,windows,MagickTrue);
10917   XCheckRefreshWindows(display,windows);
10918   width=(unsigned int) image->columns;
10919   height=(unsigned int) image->rows;
10920   x=0;
10921   y=0;
10922   if (windows->image.crop_geometry != (char *) NULL)
10923     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10924   scale_factor=(double) width/windows->image.ximage->width;
10925   paste_info.x+=x;
10926   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10927   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10928   scale_factor=(double) height/windows->image.ximage->height;
10929   paste_info.y+=y;
10930   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10931   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10932   /*
10933     Paste image with X Image window.
10934   */
10935   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10936     paste_info.y,exception);
10937   paste_image=DestroyImage(paste_image);
10938   XSetCursorState(display,windows,MagickFalse);
10939   /*
10940     Update image colormap.
10941   */
10942   XConfigureImageColormap(display,resource_info,windows,image,exception);
10943   (void) XConfigureImage(display,resource_info,windows,image,exception);
10944   return(MagickTrue);
10945 }
10946 
10947 /*
10948 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10949 %                                                                             %
10950 %                                                                             %
10951 %                                                                             %
10952 +   X P r i n t I m a g e                                                     %
10953 %                                                                             %
10954 %                                                                             %
10955 %                                                                             %
10956 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10957 %
10958 %  XPrintImage() prints an image to a Postscript printer.
10959 %
10960 %  The format of the XPrintImage method is:
10961 %
10962 %      MagickBooleanType XPrintImage(Display *display,
10963 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10964 %        ExceptionInfo *exception)
10965 %
10966 %  A description of each parameter follows:
10967 %
10968 %    o display: Specifies a connection to an X server; returned from
10969 %      XOpenDisplay.
10970 %
10971 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10972 %
10973 %    o windows: Specifies a pointer to a XWindows structure.
10974 %
10975 %    o image: the image.
10976 %
10977 %    o exception: return any errors or warnings in this structure.
10978 %
10979 */
XPrintImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)10980 static MagickBooleanType XPrintImage(Display *display,
10981   XResourceInfo *resource_info,XWindows *windows,Image *image,
10982   ExceptionInfo *exception)
10983 {
10984   char
10985     filename[MagickPathExtent],
10986     geometry[MagickPathExtent];
10987 
10988   Image
10989     *print_image;
10990 
10991   ImageInfo
10992     *image_info;
10993 
10994   MagickStatusType
10995     status;
10996 
10997   /*
10998     Request Postscript page geometry from user.
10999   */
11000   image_info=CloneImageInfo(resource_info->image_info);
11001   (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
11002   if (image_info->page != (char *) NULL)
11003     (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
11004   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11005     "Select Postscript Page Geometry:",geometry);
11006   if (*geometry == '\0')
11007     return(MagickTrue);
11008   image_info->page=GetPageGeometry(geometry);
11009   /*
11010     Apply image transforms.
11011   */
11012   XSetCursorState(display,windows,MagickTrue);
11013   XCheckRefreshWindows(display,windows);
11014   print_image=CloneImage(image,0,0,MagickTrue,exception);
11015   if (print_image == (Image *) NULL)
11016     return(MagickFalse);
11017   (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
11018     windows->image.ximage->width,windows->image.ximage->height);
11019   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11020     exception);
11021   /*
11022     Print image.
11023   */
11024   (void) AcquireUniqueFilename(filename);
11025   (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
11026     filename);
11027   status=WriteImage(image_info,print_image,exception);
11028   (void) RelinquishUniqueFileResource(filename);
11029   print_image=DestroyImage(print_image);
11030   image_info=DestroyImageInfo(image_info);
11031   XSetCursorState(display,windows,MagickFalse);
11032   return(status != 0 ? MagickTrue : MagickFalse);
11033 }
11034 
11035 /*
11036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11037 %                                                                             %
11038 %                                                                             %
11039 %                                                                             %
11040 +   X R O I I m a g e                                                         %
11041 %                                                                             %
11042 %                                                                             %
11043 %                                                                             %
11044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11045 %
11046 %  XROIImage() applies an image processing technique to a region of interest.
11047 %
11048 %  The format of the XROIImage method is:
11049 %
11050 %      MagickBooleanType XROIImage(Display *display,
11051 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11052 %        ExceptionInfo *exception)
11053 %
11054 %  A description of each parameter follows:
11055 %
11056 %    o display: Specifies a connection to an X server; returned from
11057 %      XOpenDisplay.
11058 %
11059 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11060 %
11061 %    o windows: Specifies a pointer to a XWindows structure.
11062 %
11063 %    o image: the image; returned from ReadImage.
11064 %
11065 %    o exception: return any errors or warnings in this structure.
11066 %
11067 */
XROIImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)11068 static MagickBooleanType XROIImage(Display *display,
11069   XResourceInfo *resource_info,XWindows *windows,Image **image,
11070   ExceptionInfo *exception)
11071 {
11072 #define ApplyMenus  7
11073 
11074   static const char
11075     *ROIMenu[] =
11076     {
11077       "Help",
11078       "Dismiss",
11079       (char *) NULL
11080     },
11081     *ApplyMenu[] =
11082     {
11083       "File",
11084       "Edit",
11085       "Transform",
11086       "Enhance",
11087       "Effects",
11088       "F/X",
11089       "Miscellany",
11090       "Help",
11091       "Dismiss",
11092       (char *) NULL
11093     },
11094     *FileMenu[] =
11095     {
11096       "Save...",
11097       "Print...",
11098       (char *) NULL
11099     },
11100     *EditMenu[] =
11101     {
11102       "Undo",
11103       "Redo",
11104       (char *) NULL
11105     },
11106     *TransformMenu[] =
11107     {
11108       "Flop",
11109       "Flip",
11110       "Rotate Right",
11111       "Rotate Left",
11112       (char *) NULL
11113     },
11114     *EnhanceMenu[] =
11115     {
11116       "Hue...",
11117       "Saturation...",
11118       "Brightness...",
11119       "Gamma...",
11120       "Spiff",
11121       "Dull",
11122       "Contrast Stretch...",
11123       "Sigmoidal Contrast...",
11124       "Normalize",
11125       "Equalize",
11126       "Negate",
11127       "Grayscale",
11128       "Map...",
11129       "Quantize...",
11130       (char *) NULL
11131     },
11132     *EffectsMenu[] =
11133     {
11134       "Despeckle",
11135       "Emboss",
11136       "Reduce Noise",
11137       "Add Noise",
11138       "Sharpen...",
11139       "Blur...",
11140       "Threshold...",
11141       "Edge Detect...",
11142       "Spread...",
11143       "Shade...",
11144       "Raise...",
11145       "Segment...",
11146       (char *) NULL
11147     },
11148     *FXMenu[] =
11149     {
11150       "Solarize...",
11151       "Sepia Tone...",
11152       "Swirl...",
11153       "Implode...",
11154       "Vignette...",
11155       "Wave...",
11156       "Oil Paint...",
11157       "Charcoal Draw...",
11158       (char *) NULL
11159     },
11160     *MiscellanyMenu[] =
11161     {
11162       "Image Info",
11163       "Zoom Image",
11164       "Show Preview...",
11165       "Show Histogram",
11166       "Show Matte",
11167       (char *) NULL
11168     };
11169 
11170   static const char
11171     **Menus[ApplyMenus] =
11172     {
11173       FileMenu,
11174       EditMenu,
11175       TransformMenu,
11176       EnhanceMenu,
11177       EffectsMenu,
11178       FXMenu,
11179       MiscellanyMenu
11180     };
11181 
11182   static const CommandType
11183     ApplyCommands[] =
11184     {
11185       NullCommand,
11186       NullCommand,
11187       NullCommand,
11188       NullCommand,
11189       NullCommand,
11190       NullCommand,
11191       NullCommand,
11192       HelpCommand,
11193       QuitCommand
11194     },
11195     FileCommands[] =
11196     {
11197       SaveCommand,
11198       PrintCommand
11199     },
11200     EditCommands[] =
11201     {
11202       UndoCommand,
11203       RedoCommand
11204     },
11205     TransformCommands[] =
11206     {
11207       FlopCommand,
11208       FlipCommand,
11209       RotateRightCommand,
11210       RotateLeftCommand
11211     },
11212     EnhanceCommands[] =
11213     {
11214       HueCommand,
11215       SaturationCommand,
11216       BrightnessCommand,
11217       GammaCommand,
11218       SpiffCommand,
11219       DullCommand,
11220       ContrastStretchCommand,
11221       SigmoidalContrastCommand,
11222       NormalizeCommand,
11223       EqualizeCommand,
11224       NegateCommand,
11225       GrayscaleCommand,
11226       MapCommand,
11227       QuantizeCommand
11228     },
11229     EffectsCommands[] =
11230     {
11231       DespeckleCommand,
11232       EmbossCommand,
11233       ReduceNoiseCommand,
11234       AddNoiseCommand,
11235       SharpenCommand,
11236       BlurCommand,
11237       EdgeDetectCommand,
11238       SpreadCommand,
11239       ShadeCommand,
11240       RaiseCommand,
11241       SegmentCommand
11242     },
11243     FXCommands[] =
11244     {
11245       SolarizeCommand,
11246       SepiaToneCommand,
11247       SwirlCommand,
11248       ImplodeCommand,
11249       VignetteCommand,
11250       WaveCommand,
11251       OilPaintCommand,
11252       CharcoalDrawCommand
11253     },
11254     MiscellanyCommands[] =
11255     {
11256       InfoCommand,
11257       ZoomCommand,
11258       ShowPreviewCommand,
11259       ShowHistogramCommand,
11260       ShowMatteCommand
11261     },
11262     ROICommands[] =
11263     {
11264       ROIHelpCommand,
11265       ROIDismissCommand
11266     };
11267 
11268   static const CommandType
11269     *Commands[ApplyMenus] =
11270     {
11271       FileCommands,
11272       EditCommands,
11273       TransformCommands,
11274       EnhanceCommands,
11275       EffectsCommands,
11276       FXCommands,
11277       MiscellanyCommands
11278     };
11279 
11280   char
11281     command[MagickPathExtent],
11282     text[MagickPathExtent];
11283 
11284   CommandType
11285     command_type;
11286 
11287   Cursor
11288     cursor;
11289 
11290   Image
11291     *roi_image;
11292 
11293   int
11294     entry,
11295     id,
11296     x,
11297     y;
11298 
11299   double
11300     scale_factor;
11301 
11302   MagickProgressMonitor
11303     progress_monitor;
11304 
11305   RectangleInfo
11306     crop_info,
11307     highlight_info,
11308     roi_info;
11309 
11310   unsigned int
11311     height,
11312     width;
11313 
11314   size_t
11315     state;
11316 
11317   XEvent
11318     event;
11319 
11320   /*
11321     Map Command widget.
11322   */
11323   (void) CloneString(&windows->command.name,"ROI");
11324   windows->command.data=0;
11325   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11326   (void) XMapRaised(display,windows->command.id);
11327   XClientMessage(display,windows->image.id,windows->im_protocols,
11328     windows->im_update_widget,CurrentTime);
11329   /*
11330     Track pointer until button 1 is pressed.
11331   */
11332   XQueryPosition(display,windows->image.id,&x,&y);
11333   (void) XSelectInput(display,windows->image.id,
11334     windows->image.attributes.event_mask | PointerMotionMask);
11335   roi_info.x=(ssize_t) windows->image.x+x;
11336   roi_info.y=(ssize_t) windows->image.y+y;
11337   roi_info.width=0;
11338   roi_info.height=0;
11339   cursor=XCreateFontCursor(display,XC_fleur);
11340   state=DefaultState;
11341   do
11342   {
11343     if (windows->info.mapped != MagickFalse )
11344       {
11345         /*
11346           Display pointer position.
11347         */
11348         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11349           (long) roi_info.x,(long) roi_info.y);
11350         XInfoWidget(display,windows,text);
11351       }
11352     /*
11353       Wait for next event.
11354     */
11355     XScreenEvent(display,windows,&event,exception);
11356     if (event.xany.window == windows->command.id)
11357       {
11358         /*
11359           Select a command from the Command widget.
11360         */
11361         id=XCommandWidget(display,windows,ROIMenu,&event);
11362         if (id < 0)
11363           continue;
11364         switch (ROICommands[id])
11365         {
11366           case ROIHelpCommand:
11367           {
11368             XTextViewWidget(display,resource_info,windows,MagickFalse,
11369               "Help Viewer - Region of Interest",ImageROIHelp);
11370             break;
11371           }
11372           case ROIDismissCommand:
11373           {
11374             /*
11375               Prematurely exit.
11376             */
11377             state|=EscapeState;
11378             state|=ExitState;
11379             break;
11380           }
11381           default:
11382             break;
11383         }
11384         continue;
11385       }
11386     switch (event.type)
11387     {
11388       case ButtonPress:
11389       {
11390         if (event.xbutton.button != Button1)
11391           break;
11392         if (event.xbutton.window != windows->image.id)
11393           break;
11394         /*
11395           Note first corner of region of interest rectangle-- exit loop.
11396         */
11397         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11398         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11399         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11400         state|=ExitState;
11401         break;
11402       }
11403       case ButtonRelease:
11404         break;
11405       case Expose:
11406         break;
11407       case KeyPress:
11408       {
11409         KeySym
11410           key_symbol;
11411 
11412         if (event.xkey.window != windows->image.id)
11413           break;
11414         /*
11415           Respond to a user key press.
11416         */
11417         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11418           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11419         switch ((int) key_symbol)
11420         {
11421           case XK_Escape:
11422           case XK_F20:
11423           {
11424             /*
11425               Prematurely exit.
11426             */
11427             state|=EscapeState;
11428             state|=ExitState;
11429             break;
11430           }
11431           case XK_F1:
11432           case XK_Help:
11433           {
11434             XTextViewWidget(display,resource_info,windows,MagickFalse,
11435               "Help Viewer - Region of Interest",ImageROIHelp);
11436             break;
11437           }
11438           default:
11439           {
11440             (void) XBell(display,0);
11441             break;
11442           }
11443         }
11444         break;
11445       }
11446       case MotionNotify:
11447       {
11448         /*
11449           Map and unmap Info widget as text cursor crosses its boundaries.
11450         */
11451         x=event.xmotion.x;
11452         y=event.xmotion.y;
11453         if (windows->info.mapped != MagickFalse )
11454           {
11455             if ((x < (int) (windows->info.x+windows->info.width)) &&
11456                 (y < (int) (windows->info.y+windows->info.height)))
11457               (void) XWithdrawWindow(display,windows->info.id,
11458                 windows->info.screen);
11459           }
11460         else
11461           if ((x > (int) (windows->info.x+windows->info.width)) ||
11462               (y > (int) (windows->info.y+windows->info.height)))
11463             (void) XMapWindow(display,windows->info.id);
11464         roi_info.x=(ssize_t) windows->image.x+x;
11465         roi_info.y=(ssize_t) windows->image.y+y;
11466         break;
11467       }
11468       default:
11469         break;
11470     }
11471   } while ((state & ExitState) == 0);
11472   (void) XSelectInput(display,windows->image.id,
11473     windows->image.attributes.event_mask);
11474   if ((state & EscapeState) != 0)
11475     {
11476       /*
11477         User want to exit without region of interest.
11478       */
11479       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11480       (void) XFreeCursor(display,cursor);
11481       return(MagickTrue);
11482     }
11483   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11484   do
11485   {
11486     /*
11487       Size rectangle as pointer moves until the mouse button is released.
11488     */
11489     x=(int) roi_info.x;
11490     y=(int) roi_info.y;
11491     roi_info.width=0;
11492     roi_info.height=0;
11493     state=DefaultState;
11494     do
11495     {
11496       highlight_info=roi_info;
11497       highlight_info.x=roi_info.x-windows->image.x;
11498       highlight_info.y=roi_info.y-windows->image.y;
11499       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11500         {
11501           /*
11502             Display info and draw region of interest rectangle.
11503           */
11504           if (windows->info.mapped == MagickFalse)
11505             (void) XMapWindow(display,windows->info.id);
11506           (void) FormatLocaleString(text,MagickPathExtent,
11507             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11508             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11509           XInfoWidget(display,windows,text);
11510           XHighlightRectangle(display,windows->image.id,
11511             windows->image.highlight_context,&highlight_info);
11512         }
11513       else
11514         if (windows->info.mapped != MagickFalse )
11515           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11516       /*
11517         Wait for next event.
11518       */
11519       XScreenEvent(display,windows,&event,exception);
11520       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11521         XHighlightRectangle(display,windows->image.id,
11522           windows->image.highlight_context,&highlight_info);
11523       switch (event.type)
11524       {
11525         case ButtonPress:
11526         {
11527           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11528           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11529           break;
11530         }
11531         case ButtonRelease:
11532         {
11533           /*
11534             User has committed to region of interest rectangle.
11535           */
11536           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11537           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11538           XSetCursorState(display,windows,MagickFalse);
11539           state|=ExitState;
11540           if (LocaleCompare(windows->command.name,"Apply") == 0)
11541             break;
11542           (void) CloneString(&windows->command.name,"Apply");
11543           windows->command.data=ApplyMenus;
11544           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11545           break;
11546         }
11547         case Expose:
11548           break;
11549         case MotionNotify:
11550         {
11551           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11552           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11553         }
11554         default:
11555           break;
11556       }
11557       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11558           ((state & ExitState) != 0))
11559         {
11560           /*
11561             Check boundary conditions.
11562           */
11563           if (roi_info.x < 0)
11564             roi_info.x=0;
11565           else
11566             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11567               roi_info.x=(ssize_t) windows->image.ximage->width;
11568           if ((int) roi_info.x < x)
11569             roi_info.width=(unsigned int) (x-roi_info.x);
11570           else
11571             {
11572               roi_info.width=(unsigned int) (roi_info.x-x);
11573               roi_info.x=(ssize_t) x;
11574             }
11575           if (roi_info.y < 0)
11576             roi_info.y=0;
11577           else
11578             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11579               roi_info.y=(ssize_t) windows->image.ximage->height;
11580           if ((int) roi_info.y < y)
11581             roi_info.height=(unsigned int) (y-roi_info.y);
11582           else
11583             {
11584               roi_info.height=(unsigned int) (roi_info.y-y);
11585               roi_info.y=(ssize_t) y;
11586             }
11587         }
11588     } while ((state & ExitState) == 0);
11589     /*
11590       Wait for user to grab a corner of the rectangle or press return.
11591     */
11592     state=DefaultState;
11593     command_type=NullCommand;
11594     crop_info.x=0;
11595     crop_info.y=0;
11596     (void) XMapWindow(display,windows->info.id);
11597     do
11598     {
11599       if (windows->info.mapped != MagickFalse )
11600         {
11601           /*
11602             Display pointer position.
11603           */
11604           (void) FormatLocaleString(text,MagickPathExtent,
11605             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11606             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11607           XInfoWidget(display,windows,text);
11608         }
11609       highlight_info=roi_info;
11610       highlight_info.x=roi_info.x-windows->image.x;
11611       highlight_info.y=roi_info.y-windows->image.y;
11612       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11613         {
11614           state|=EscapeState;
11615           state|=ExitState;
11616           break;
11617         }
11618       if ((state & UpdateRegionState) != 0)
11619         {
11620           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11621           switch (command_type)
11622           {
11623             case UndoCommand:
11624             case RedoCommand:
11625             {
11626               (void) XMagickCommand(display,resource_info,windows,command_type,
11627                 image,exception);
11628               break;
11629             }
11630             default:
11631             {
11632               /*
11633                 Region of interest is relative to image configuration.
11634               */
11635               progress_monitor=SetImageProgressMonitor(*image,
11636                 (MagickProgressMonitor) NULL,(*image)->client_data);
11637               crop_info=roi_info;
11638               width=(unsigned int) (*image)->columns;
11639               height=(unsigned int) (*image)->rows;
11640               x=0;
11641               y=0;
11642               if (windows->image.crop_geometry != (char *) NULL)
11643                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11644                   &width,&height);
11645               scale_factor=(double) width/windows->image.ximage->width;
11646               crop_info.x+=x;
11647               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11648               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11649               scale_factor=(double)
11650                 height/windows->image.ximage->height;
11651               crop_info.y+=y;
11652               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11653               crop_info.height=(unsigned int)
11654                 (scale_factor*crop_info.height+0.5);
11655               roi_image=CropImage(*image,&crop_info,exception);
11656               (void) SetImageProgressMonitor(*image,progress_monitor,
11657                 (*image)->client_data);
11658               if (roi_image == (Image *) NULL)
11659                 continue;
11660               /*
11661                 Apply image processing technique to the region of interest.
11662               */
11663               windows->image.orphan=MagickTrue;
11664               (void) XMagickCommand(display,resource_info,windows,command_type,
11665                 &roi_image,exception);
11666               progress_monitor=SetImageProgressMonitor(*image,
11667                 (MagickProgressMonitor) NULL,(*image)->client_data);
11668               (void) XMagickCommand(display,resource_info,windows,
11669                 SaveToUndoBufferCommand,image,exception);
11670               windows->image.orphan=MagickFalse;
11671               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11672                 MagickTrue,crop_info.x,crop_info.y,exception);
11673               roi_image=DestroyImage(roi_image);
11674               (void) SetImageProgressMonitor(*image,progress_monitor,
11675                 (*image)->client_data);
11676               break;
11677             }
11678           }
11679           if (command_type != InfoCommand)
11680             {
11681               XConfigureImageColormap(display,resource_info,windows,*image,
11682                 exception);
11683               (void) XConfigureImage(display,resource_info,windows,*image,
11684                 exception);
11685             }
11686           XCheckRefreshWindows(display,windows);
11687           XInfoWidget(display,windows,text);
11688           (void) XSetFunction(display,windows->image.highlight_context,
11689             GXinvert);
11690           state&=(~UpdateRegionState);
11691         }
11692       XHighlightRectangle(display,windows->image.id,
11693         windows->image.highlight_context,&highlight_info);
11694       XScreenEvent(display,windows,&event,exception);
11695       if (event.xany.window == windows->command.id)
11696         {
11697           /*
11698             Select a command from the Command widget.
11699           */
11700           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11701           command_type=NullCommand;
11702           id=XCommandWidget(display,windows,ApplyMenu,&event);
11703           if (id >= 0)
11704             {
11705               (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11706               command_type=ApplyCommands[id];
11707               if (id < ApplyMenus)
11708                 {
11709                   /*
11710                     Select a command from a pop-up menu.
11711                   */
11712                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11713                     (const char **) Menus[id],command);
11714                   if (entry >= 0)
11715                     {
11716                       (void) CopyMagickString(command,Menus[id][entry],
11717                         MagickPathExtent);
11718                       command_type=Commands[id][entry];
11719                     }
11720                 }
11721             }
11722           (void) XSetFunction(display,windows->image.highlight_context,
11723             GXinvert);
11724           XHighlightRectangle(display,windows->image.id,
11725             windows->image.highlight_context,&highlight_info);
11726           if (command_type == HelpCommand)
11727             {
11728               (void) XSetFunction(display,windows->image.highlight_context,
11729                 GXcopy);
11730               XTextViewWidget(display,resource_info,windows,MagickFalse,
11731                 "Help Viewer - Region of Interest",ImageROIHelp);
11732               (void) XSetFunction(display,windows->image.highlight_context,
11733                 GXinvert);
11734               continue;
11735             }
11736           if (command_type == QuitCommand)
11737             {
11738               /*
11739                 exit.
11740               */
11741               state|=EscapeState;
11742               state|=ExitState;
11743               continue;
11744             }
11745           if (command_type != NullCommand)
11746             state|=UpdateRegionState;
11747           continue;
11748         }
11749       XHighlightRectangle(display,windows->image.id,
11750         windows->image.highlight_context,&highlight_info);
11751       switch (event.type)
11752       {
11753         case ButtonPress:
11754         {
11755           x=windows->image.x;
11756           y=windows->image.y;
11757           if (event.xbutton.button != Button1)
11758             break;
11759           if (event.xbutton.window != windows->image.id)
11760             break;
11761           x=windows->image.x+event.xbutton.x;
11762           y=windows->image.y+event.xbutton.y;
11763           if ((x < (int) (roi_info.x+RoiDelta)) &&
11764               (x > (int) (roi_info.x-RoiDelta)) &&
11765               (y < (int) (roi_info.y+RoiDelta)) &&
11766               (y > (int) (roi_info.y-RoiDelta)))
11767             {
11768               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11769               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11770               state|=UpdateConfigurationState;
11771               break;
11772             }
11773           if ((x < (int) (roi_info.x+RoiDelta)) &&
11774               (x > (int) (roi_info.x-RoiDelta)) &&
11775               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11776               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11777             {
11778               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11779               state|=UpdateConfigurationState;
11780               break;
11781             }
11782           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11783               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11784               (y < (int) (roi_info.y+RoiDelta)) &&
11785               (y > (int) (roi_info.y-RoiDelta)))
11786             {
11787               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11788               state|=UpdateConfigurationState;
11789               break;
11790             }
11791           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11792               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11793               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11794               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11795             {
11796               state|=UpdateConfigurationState;
11797               break;
11798             }
11799         }
11800         case ButtonRelease:
11801         {
11802           if (event.xbutton.window == windows->pan.id)
11803             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11804                 (highlight_info.y != crop_info.y-windows->image.y))
11805               XHighlightRectangle(display,windows->image.id,
11806                 windows->image.highlight_context,&highlight_info);
11807           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11808             event.xbutton.time);
11809           break;
11810         }
11811         case Expose:
11812         {
11813           if (event.xexpose.window == windows->image.id)
11814             if (event.xexpose.count == 0)
11815               {
11816                 event.xexpose.x=(int) highlight_info.x;
11817                 event.xexpose.y=(int) highlight_info.y;
11818                 event.xexpose.width=(int) highlight_info.width;
11819                 event.xexpose.height=(int) highlight_info.height;
11820                 XRefreshWindow(display,&windows->image,&event);
11821               }
11822           if (event.xexpose.window == windows->info.id)
11823             if (event.xexpose.count == 0)
11824               XInfoWidget(display,windows,text);
11825           break;
11826         }
11827         case KeyPress:
11828         {
11829           KeySym
11830             key_symbol;
11831 
11832           if (event.xkey.window != windows->image.id)
11833             break;
11834           /*
11835             Respond to a user key press.
11836           */
11837           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11838             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11839           switch ((int) key_symbol)
11840           {
11841             case XK_Shift_L:
11842             case XK_Shift_R:
11843               break;
11844             case XK_Escape:
11845             case XK_F20:
11846               state|=EscapeState;
11847             case XK_Return:
11848             {
11849               state|=ExitState;
11850               break;
11851             }
11852             case XK_Home:
11853             case XK_KP_Home:
11854             {
11855               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11856               roi_info.y=(ssize_t) (windows->image.height/2L-
11857                 roi_info.height/2L);
11858               break;
11859             }
11860             case XK_Left:
11861             case XK_KP_Left:
11862             {
11863               roi_info.x--;
11864               break;
11865             }
11866             case XK_Up:
11867             case XK_KP_Up:
11868             case XK_Next:
11869             {
11870               roi_info.y--;
11871               break;
11872             }
11873             case XK_Right:
11874             case XK_KP_Right:
11875             {
11876               roi_info.x++;
11877               break;
11878             }
11879             case XK_Prior:
11880             case XK_Down:
11881             case XK_KP_Down:
11882             {
11883               roi_info.y++;
11884               break;
11885             }
11886             case XK_F1:
11887             case XK_Help:
11888             {
11889               (void) XSetFunction(display,windows->image.highlight_context,
11890                 GXcopy);
11891               XTextViewWidget(display,resource_info,windows,MagickFalse,
11892                 "Help Viewer - Region of Interest",ImageROIHelp);
11893               (void) XSetFunction(display,windows->image.highlight_context,
11894                 GXinvert);
11895               break;
11896             }
11897             default:
11898             {
11899               command_type=XImageWindowCommand(display,resource_info,windows,
11900                 event.xkey.state,key_symbol,image,exception);
11901               if (command_type != NullCommand)
11902                 state|=UpdateRegionState;
11903               break;
11904             }
11905           }
11906           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11907             event.xkey.time);
11908           break;
11909         }
11910         case KeyRelease:
11911           break;
11912         case MotionNotify:
11913         {
11914           if (event.xbutton.window != windows->image.id)
11915             break;
11916           /*
11917             Map and unmap Info widget as text cursor crosses its boundaries.
11918           */
11919           x=event.xmotion.x;
11920           y=event.xmotion.y;
11921           if (windows->info.mapped != MagickFalse )
11922             {
11923               if ((x < (int) (windows->info.x+windows->info.width)) &&
11924                   (y < (int) (windows->info.y+windows->info.height)))
11925                 (void) XWithdrawWindow(display,windows->info.id,
11926                   windows->info.screen);
11927             }
11928           else
11929             if ((x > (int) (windows->info.x+windows->info.width)) ||
11930                 (y > (int) (windows->info.y+windows->info.height)))
11931               (void) XMapWindow(display,windows->info.id);
11932           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11933           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11934           break;
11935         }
11936         case SelectionRequest:
11937         {
11938           XSelectionEvent
11939             notify;
11940 
11941           XSelectionRequestEvent
11942             *request;
11943 
11944           /*
11945             Set primary selection.
11946           */
11947           (void) FormatLocaleString(text,MagickPathExtent,
11948             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11949             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11950           request=(&(event.xselectionrequest));
11951           (void) XChangeProperty(request->display,request->requestor,
11952             request->property,request->target,8,PropModeReplace,
11953             (unsigned char *) text,(int) strlen(text));
11954           notify.type=SelectionNotify;
11955           notify.display=request->display;
11956           notify.requestor=request->requestor;
11957           notify.selection=request->selection;
11958           notify.target=request->target;
11959           notify.time=request->time;
11960           if (request->property == None)
11961             notify.property=request->target;
11962           else
11963             notify.property=request->property;
11964           (void) XSendEvent(request->display,request->requestor,False,0,
11965             (XEvent *) &notify);
11966         }
11967         default:
11968           break;
11969       }
11970       if ((state & UpdateConfigurationState) != 0)
11971         {
11972           (void) XPutBackEvent(display,&event);
11973           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11974           break;
11975         }
11976     } while ((state & ExitState) == 0);
11977   } while ((state & ExitState) == 0);
11978   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11979   XSetCursorState(display,windows,MagickFalse);
11980   if ((state & EscapeState) != 0)
11981     return(MagickTrue);
11982   return(MagickTrue);
11983 }
11984 
11985 /*
11986 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11987 %                                                                             %
11988 %                                                                             %
11989 %                                                                             %
11990 +   X R o t a t e I m a g e                                                   %
11991 %                                                                             %
11992 %                                                                             %
11993 %                                                                             %
11994 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11995 %
11996 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11997 %  rotation angle is computed from the slope of a line drawn by the user.
11998 %
11999 %  The format of the XRotateImage method is:
12000 %
12001 %      MagickBooleanType XRotateImage(Display *display,
12002 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
12003 %        Image **image,ExceptionInfo *exception)
12004 %
12005 %  A description of each parameter follows:
12006 %
12007 %    o display: Specifies a connection to an X server; returned from
12008 %      XOpenDisplay.
12009 %
12010 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12011 %
12012 %    o windows: Specifies a pointer to a XWindows structure.
12013 %
12014 %    o degrees: Specifies the number of degrees to rotate the image.
12015 %
12016 %    o image: the image.
12017 %
12018 %    o exception: return any errors or warnings in this structure.
12019 %
12020 */
XRotateImage(Display * display,XResourceInfo * resource_info,XWindows * windows,double degrees,Image ** image,ExceptionInfo * exception)12021 static MagickBooleanType XRotateImage(Display *display,
12022   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12023   ExceptionInfo *exception)
12024 {
12025   static const char
12026     *RotateMenu[] =
12027     {
12028       "Pixel Color",
12029       "Direction",
12030       "Help",
12031       "Dismiss",
12032       (char *) NULL
12033     };
12034 
12035   static ModeType
12036     direction = HorizontalRotateCommand;
12037 
12038   static const ModeType
12039     DirectionCommands[] =
12040     {
12041       HorizontalRotateCommand,
12042       VerticalRotateCommand
12043     },
12044     RotateCommands[] =
12045     {
12046       RotateColorCommand,
12047       RotateDirectionCommand,
12048       RotateHelpCommand,
12049       RotateDismissCommand
12050     };
12051 
12052   static unsigned int
12053     pen_id = 0;
12054 
12055   char
12056     command[MagickPathExtent],
12057     text[MagickPathExtent];
12058 
12059   Image
12060     *rotate_image;
12061 
12062   int
12063     id,
12064     x,
12065     y;
12066 
12067   double
12068     normalized_degrees;
12069 
12070   register int
12071     i;
12072 
12073   unsigned int
12074     height,
12075     rotations,
12076     width;
12077 
12078   if (degrees == 0.0)
12079     {
12080       unsigned int
12081         distance;
12082 
12083       size_t
12084         state;
12085 
12086       XEvent
12087         event;
12088 
12089       XSegment
12090         rotate_info;
12091 
12092       /*
12093         Map Command widget.
12094       */
12095       (void) CloneString(&windows->command.name,"Rotate");
12096       windows->command.data=2;
12097       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12098       (void) XMapRaised(display,windows->command.id);
12099       XClientMessage(display,windows->image.id,windows->im_protocols,
12100         windows->im_update_widget,CurrentTime);
12101       /*
12102         Wait for first button press.
12103       */
12104       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12105       XQueryPosition(display,windows->image.id,&x,&y);
12106       rotate_info.x1=x;
12107       rotate_info.y1=y;
12108       rotate_info.x2=x;
12109       rotate_info.y2=y;
12110       state=DefaultState;
12111       do
12112       {
12113         XHighlightLine(display,windows->image.id,
12114           windows->image.highlight_context,&rotate_info);
12115         /*
12116           Wait for next event.
12117         */
12118         XScreenEvent(display,windows,&event,exception);
12119         XHighlightLine(display,windows->image.id,
12120           windows->image.highlight_context,&rotate_info);
12121         if (event.xany.window == windows->command.id)
12122           {
12123             /*
12124               Select a command from the Command widget.
12125             */
12126             id=XCommandWidget(display,windows,RotateMenu,&event);
12127             if (id < 0)
12128               continue;
12129             (void) XSetFunction(display,windows->image.highlight_context,
12130               GXcopy);
12131             switch (RotateCommands[id])
12132             {
12133               case RotateColorCommand:
12134               {
12135                 const char
12136                   *ColorMenu[MaxNumberPens];
12137 
12138                 int
12139                   pen_number;
12140 
12141                 XColor
12142                   color;
12143 
12144                 /*
12145                   Initialize menu selections.
12146                 */
12147                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12148                   ColorMenu[i]=resource_info->pen_colors[i];
12149                 ColorMenu[MaxNumberPens-2]="Browser...";
12150                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12151                 /*
12152                   Select a pen color from the pop-up menu.
12153                 */
12154                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12155                   (const char **) ColorMenu,command);
12156                 if (pen_number < 0)
12157                   break;
12158                 if (pen_number == (MaxNumberPens-2))
12159                   {
12160                     static char
12161                       color_name[MagickPathExtent] = "gray";
12162 
12163                     /*
12164                       Select a pen color from a dialog.
12165                     */
12166                     resource_info->pen_colors[pen_number]=color_name;
12167                     XColorBrowserWidget(display,windows,"Select",color_name);
12168                     if (*color_name == '\0')
12169                       break;
12170                   }
12171                 /*
12172                   Set pen color.
12173                 */
12174                 (void) XParseColor(display,windows->map_info->colormap,
12175                   resource_info->pen_colors[pen_number],&color);
12176                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12177                   (unsigned int) MaxColors,&color);
12178                 windows->pixel_info->pen_colors[pen_number]=color;
12179                 pen_id=(unsigned int) pen_number;
12180                 break;
12181               }
12182               case RotateDirectionCommand:
12183               {
12184                 static const char
12185                   *Directions[] =
12186                   {
12187                     "horizontal",
12188                     "vertical",
12189                     (char *) NULL,
12190                   };
12191 
12192                 /*
12193                   Select a command from the pop-up menu.
12194                 */
12195                 id=XMenuWidget(display,windows,RotateMenu[id],
12196                   Directions,command);
12197                 if (id >= 0)
12198                   direction=DirectionCommands[id];
12199                 break;
12200               }
12201               case RotateHelpCommand:
12202               {
12203                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12204                   "Help Viewer - Image Rotation",ImageRotateHelp);
12205                 break;
12206               }
12207               case RotateDismissCommand:
12208               {
12209                 /*
12210                   Prematurely exit.
12211                 */
12212                 state|=EscapeState;
12213                 state|=ExitState;
12214                 break;
12215               }
12216               default:
12217                 break;
12218             }
12219             (void) XSetFunction(display,windows->image.highlight_context,
12220               GXinvert);
12221             continue;
12222           }
12223         switch (event.type)
12224         {
12225           case ButtonPress:
12226           {
12227             if (event.xbutton.button != Button1)
12228               break;
12229             if (event.xbutton.window != windows->image.id)
12230               break;
12231             /*
12232               exit loop.
12233             */
12234             (void) XSetFunction(display,windows->image.highlight_context,
12235               GXcopy);
12236             rotate_info.x1=event.xbutton.x;
12237             rotate_info.y1=event.xbutton.y;
12238             state|=ExitState;
12239             break;
12240           }
12241           case ButtonRelease:
12242             break;
12243           case Expose:
12244             break;
12245           case KeyPress:
12246           {
12247             char
12248               command[MagickPathExtent];
12249 
12250             KeySym
12251               key_symbol;
12252 
12253             if (event.xkey.window != windows->image.id)
12254               break;
12255             /*
12256               Respond to a user key press.
12257             */
12258             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12259               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12260             switch ((int) key_symbol)
12261             {
12262               case XK_Escape:
12263               case XK_F20:
12264               {
12265                 /*
12266                   Prematurely exit.
12267                 */
12268                 state|=EscapeState;
12269                 state|=ExitState;
12270                 break;
12271               }
12272               case XK_F1:
12273               case XK_Help:
12274               {
12275                 (void) XSetFunction(display,windows->image.highlight_context,
12276                   GXcopy);
12277                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12278                   "Help Viewer - Image Rotation",ImageRotateHelp);
12279                 (void) XSetFunction(display,windows->image.highlight_context,
12280                   GXinvert);
12281                 break;
12282               }
12283               default:
12284               {
12285                 (void) XBell(display,0);
12286                 break;
12287               }
12288             }
12289             break;
12290           }
12291           case MotionNotify:
12292           {
12293             rotate_info.x1=event.xmotion.x;
12294             rotate_info.y1=event.xmotion.y;
12295           }
12296         }
12297         rotate_info.x2=rotate_info.x1;
12298         rotate_info.y2=rotate_info.y1;
12299         if (direction == HorizontalRotateCommand)
12300           rotate_info.x2+=32;
12301         else
12302           rotate_info.y2-=32;
12303       } while ((state & ExitState) == 0);
12304       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12305       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12306       if ((state & EscapeState) != 0)
12307         return(MagickTrue);
12308       /*
12309         Draw line as pointer moves until the mouse button is released.
12310       */
12311       distance=0;
12312       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12313       state=DefaultState;
12314       do
12315       {
12316         if (distance > 9)
12317           {
12318             /*
12319               Display info and draw rotation line.
12320             */
12321             if (windows->info.mapped == MagickFalse)
12322               (void) XMapWindow(display,windows->info.id);
12323             (void) FormatLocaleString(text,MagickPathExtent," %g",
12324               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12325             XInfoWidget(display,windows,text);
12326             XHighlightLine(display,windows->image.id,
12327               windows->image.highlight_context,&rotate_info);
12328           }
12329         else
12330           if (windows->info.mapped != MagickFalse )
12331             (void) XWithdrawWindow(display,windows->info.id,
12332               windows->info.screen);
12333         /*
12334           Wait for next event.
12335         */
12336         XScreenEvent(display,windows,&event,exception);
12337         if (distance > 9)
12338           XHighlightLine(display,windows->image.id,
12339             windows->image.highlight_context,&rotate_info);
12340         switch (event.type)
12341         {
12342           case ButtonPress:
12343             break;
12344           case ButtonRelease:
12345           {
12346             /*
12347               User has committed to rotation line.
12348             */
12349             rotate_info.x2=event.xbutton.x;
12350             rotate_info.y2=event.xbutton.y;
12351             state|=ExitState;
12352             break;
12353           }
12354           case Expose:
12355             break;
12356           case MotionNotify:
12357           {
12358             rotate_info.x2=event.xmotion.x;
12359             rotate_info.y2=event.xmotion.y;
12360           }
12361           default:
12362             break;
12363         }
12364         /*
12365           Check boundary conditions.
12366         */
12367         if (rotate_info.x2 < 0)
12368           rotate_info.x2=0;
12369         else
12370           if (rotate_info.x2 > (int) windows->image.width)
12371             rotate_info.x2=(short) windows->image.width;
12372         if (rotate_info.y2 < 0)
12373           rotate_info.y2=0;
12374         else
12375           if (rotate_info.y2 > (int) windows->image.height)
12376             rotate_info.y2=(short) windows->image.height;
12377         /*
12378           Compute rotation angle from the slope of the line.
12379         */
12380         degrees=0.0;
12381         distance=(unsigned int)
12382           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12383           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12384         if (distance > 9)
12385           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12386             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12387       } while ((state & ExitState) == 0);
12388       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12389       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12390       if (distance <= 9)
12391         return(MagickTrue);
12392     }
12393   if (direction == VerticalRotateCommand)
12394     degrees-=90.0;
12395   if (degrees == 0.0)
12396     return(MagickTrue);
12397   /*
12398     Rotate image.
12399   */
12400   normalized_degrees=degrees;
12401   while (normalized_degrees < -45.0)
12402     normalized_degrees+=360.0;
12403   for (rotations=0; normalized_degrees > 45.0; rotations++)
12404     normalized_degrees-=90.0;
12405   if (normalized_degrees != 0.0)
12406     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12407       exception);
12408   XSetCursorState(display,windows,MagickTrue);
12409   XCheckRefreshWindows(display,windows);
12410   (*image)->background_color.red=(double) ScaleShortToQuantum(
12411     windows->pixel_info->pen_colors[pen_id].red);
12412   (*image)->background_color.green=(double) ScaleShortToQuantum(
12413     windows->pixel_info->pen_colors[pen_id].green);
12414   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12415     windows->pixel_info->pen_colors[pen_id].blue);
12416   rotate_image=RotateImage(*image,degrees,exception);
12417   XSetCursorState(display,windows,MagickFalse);
12418   if (rotate_image == (Image *) NULL)
12419     return(MagickFalse);
12420   *image=DestroyImage(*image);
12421   *image=rotate_image;
12422   if (windows->image.crop_geometry != (char *) NULL)
12423     {
12424       /*
12425         Rotate crop geometry.
12426       */
12427       width=(unsigned int) (*image)->columns;
12428       height=(unsigned int) (*image)->rows;
12429       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12430       switch (rotations % 4)
12431       {
12432         default:
12433         case 0:
12434           break;
12435         case 1:
12436         {
12437           /*
12438             Rotate 90 degrees.
12439           */
12440           (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12441             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12442             (int) height-y,x);
12443           break;
12444         }
12445         case 2:
12446         {
12447           /*
12448             Rotate 180 degrees.
12449           */
12450           (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12451             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12452           break;
12453         }
12454         case 3:
12455         {
12456           /*
12457             Rotate 270 degrees.
12458           */
12459           (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
12460             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12461           break;
12462         }
12463       }
12464     }
12465   if (windows->image.orphan != MagickFalse )
12466     return(MagickTrue);
12467   if (normalized_degrees != 0.0)
12468     {
12469       /*
12470         Update image colormap.
12471       */
12472       windows->image.window_changes.width=(int) (*image)->columns;
12473       windows->image.window_changes.height=(int) (*image)->rows;
12474       if (windows->image.crop_geometry != (char *) NULL)
12475         {
12476           /*
12477             Obtain dimensions of image from crop geometry.
12478           */
12479           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12480             &width,&height);
12481           windows->image.window_changes.width=(int) width;
12482           windows->image.window_changes.height=(int) height;
12483         }
12484       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12485     }
12486   else
12487     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12488       {
12489         windows->image.window_changes.width=windows->image.ximage->height;
12490         windows->image.window_changes.height=windows->image.ximage->width;
12491       }
12492   /*
12493     Update image configuration.
12494   */
12495   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12496   return(MagickTrue);
12497 }
12498 
12499 /*
12500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12501 %                                                                             %
12502 %                                                                             %
12503 %                                                                             %
12504 +   X S a v e I m a g e                                                       %
12505 %                                                                             %
12506 %                                                                             %
12507 %                                                                             %
12508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12509 %
12510 %  XSaveImage() saves an image to a file.
12511 %
12512 %  The format of the XSaveImage method is:
12513 %
12514 %      MagickBooleanType XSaveImage(Display *display,
12515 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12516 %        ExceptionInfo *exception)
12517 %
12518 %  A description of each parameter follows:
12519 %
12520 %    o display: Specifies a connection to an X server; returned from
12521 %      XOpenDisplay.
12522 %
12523 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12524 %
12525 %    o windows: Specifies a pointer to a XWindows structure.
12526 %
12527 %    o image: the image.
12528 %
12529 %    o exception: return any errors or warnings in this structure.
12530 %
12531 */
XSaveImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)12532 static MagickBooleanType XSaveImage(Display *display,
12533   XResourceInfo *resource_info,XWindows *windows,Image *image,
12534   ExceptionInfo *exception)
12535 {
12536   char
12537     filename[MagickPathExtent],
12538     geometry[MagickPathExtent];
12539 
12540   Image
12541     *save_image;
12542 
12543   ImageInfo
12544     *image_info;
12545 
12546   MagickStatusType
12547     status;
12548 
12549   /*
12550     Request file name from user.
12551   */
12552   if (resource_info->write_filename != (char *) NULL)
12553     (void) CopyMagickString(filename,resource_info->write_filename,
12554       MagickPathExtent);
12555   else
12556     {
12557       char
12558         path[MagickPathExtent];
12559 
12560       int
12561         status;
12562 
12563       GetPathComponent(image->filename,HeadPath,path);
12564       GetPathComponent(image->filename,TailPath,filename);
12565       if (*path != '\0')
12566         {
12567           status=chdir(path);
12568           if (status == -1)
12569             (void) ThrowMagickException(exception,GetMagickModule(),
12570               FileOpenError,"UnableToOpenFile","%s",path);
12571         }
12572     }
12573   XFileBrowserWidget(display,windows,"Save",filename);
12574   if (*filename == '\0')
12575     return(MagickTrue);
12576   if (IsPathAccessible(filename) != MagickFalse )
12577     {
12578       int
12579         status;
12580 
12581       /*
12582         File exists-- seek user's permission before overwriting.
12583       */
12584       status=XConfirmWidget(display,windows,"Overwrite",filename);
12585       if (status <= 0)
12586         return(MagickTrue);
12587     }
12588   image_info=CloneImageInfo(resource_info->image_info);
12589   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12590   (void) SetImageInfo(image_info,1,exception);
12591   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12592       (LocaleCompare(image_info->magick,"JPG") == 0))
12593     {
12594       char
12595         quality[MagickPathExtent];
12596 
12597       int
12598         status;
12599 
12600       /*
12601         Request JPEG quality from user.
12602       */
12603       (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12604         image->quality);
12605       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12606         quality);
12607       if (*quality == '\0')
12608         return(MagickTrue);
12609       image->quality=StringToUnsignedLong(quality);
12610       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12611     }
12612   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12613       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12614       (LocaleCompare(image_info->magick,"PS") == 0) ||
12615       (LocaleCompare(image_info->magick,"PS2") == 0))
12616     {
12617       char
12618         geometry[MagickPathExtent];
12619 
12620       /*
12621         Request page geometry from user.
12622       */
12623       (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12624       if (LocaleCompare(image_info->magick,"PDF") == 0)
12625         (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12626       if (image_info->page != (char *) NULL)
12627         (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12628       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12629         "Select page geometry:",geometry);
12630       if (*geometry != '\0')
12631         image_info->page=GetPageGeometry(geometry);
12632     }
12633   /*
12634     Apply image transforms.
12635   */
12636   XSetCursorState(display,windows,MagickTrue);
12637   XCheckRefreshWindows(display,windows);
12638   save_image=CloneImage(image,0,0,MagickTrue,exception);
12639   if (save_image == (Image *) NULL)
12640     return(MagickFalse);
12641   (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12642     windows->image.ximage->width,windows->image.ximage->height);
12643   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12644     exception);
12645   /*
12646     Write image.
12647   */
12648   (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12649   status=WriteImage(image_info,save_image,exception);
12650   if (status != MagickFalse )
12651     image->taint=MagickFalse;
12652   save_image=DestroyImage(save_image);
12653   image_info=DestroyImageInfo(image_info);
12654   XSetCursorState(display,windows,MagickFalse);
12655   return(status != 0 ? MagickTrue : MagickFalse);
12656 }
12657 
12658 /*
12659 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12660 %                                                                             %
12661 %                                                                             %
12662 %                                                                             %
12663 +   X S c r e e n E v e n t                                                   %
12664 %                                                                             %
12665 %                                                                             %
12666 %                                                                             %
12667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12668 %
12669 %  XScreenEvent() handles global events associated with the Pan and Magnify
12670 %  windows.
12671 %
12672 %  The format of the XScreenEvent function is:
12673 %
12674 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12675 %        ExceptionInfo *exception)
12676 %
12677 %  A description of each parameter follows:
12678 %
12679 %    o display: Specifies a pointer to the Display structure;  returned from
12680 %      XOpenDisplay.
12681 %
12682 %    o windows: Specifies a pointer to a XWindows structure.
12683 %
12684 %    o event: Specifies a pointer to a X11 XEvent structure.
12685 %
12686 %    o exception: return any errors or warnings in this structure.
12687 %
12688 */
12689 
12690 #if defined(__cplusplus) || defined(c_plusplus)
12691 extern "C" {
12692 #endif
12693 
XPredicate(Display * magick_unused (display),XEvent * event,char * data)12694 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12695 {
12696   register XWindows
12697     *windows;
12698 
12699   windows=(XWindows *) data;
12700   if ((event->type == ClientMessage) &&
12701       (event->xclient.window == windows->image.id))
12702     return(MagickFalse);
12703   return(MagickTrue);
12704 }
12705 
12706 #if defined(__cplusplus) || defined(c_plusplus)
12707 }
12708 #endif
12709 
XScreenEvent(Display * display,XWindows * windows,XEvent * event,ExceptionInfo * exception)12710 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12711   ExceptionInfo *exception)
12712 {
12713   register int
12714     x,
12715     y;
12716 
12717   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12718   if (event->xany.window == windows->command.id)
12719     return;
12720   switch (event->type)
12721   {
12722     case ButtonPress:
12723     case ButtonRelease:
12724     {
12725       if ((event->xbutton.button == Button3) &&
12726           (event->xbutton.state & Mod1Mask))
12727         {
12728           /*
12729             Convert Alt-Button3 to Button2.
12730           */
12731           event->xbutton.button=Button2;
12732           event->xbutton.state&=(~Mod1Mask);
12733         }
12734       if (event->xbutton.window == windows->backdrop.id)
12735         {
12736           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12737             event->xbutton.time);
12738           break;
12739         }
12740       if (event->xbutton.window == windows->pan.id)
12741         {
12742           XPanImage(display,windows,event,exception);
12743           break;
12744         }
12745       if (event->xbutton.window == windows->image.id)
12746         if (event->xbutton.button == Button2)
12747           {
12748             /*
12749               Update magnified image.
12750             */
12751             x=event->xbutton.x;
12752             y=event->xbutton.y;
12753             if (x < 0)
12754               x=0;
12755             else
12756               if (x >= (int) windows->image.width)
12757                 x=(int) (windows->image.width-1);
12758             windows->magnify.x=(int) windows->image.x+x;
12759             if (y < 0)
12760               y=0;
12761             else
12762              if (y >= (int) windows->image.height)
12763                y=(int) (windows->image.height-1);
12764             windows->magnify.y=windows->image.y+y;
12765             if (windows->magnify.mapped == MagickFalse)
12766               (void) XMapRaised(display,windows->magnify.id);
12767             XMakeMagnifyImage(display,windows,exception);
12768             if (event->type == ButtonRelease)
12769               (void) XWithdrawWindow(display,windows->info.id,
12770                 windows->info.screen);
12771             break;
12772           }
12773       break;
12774     }
12775     case ClientMessage:
12776     {
12777       /*
12778         If client window delete message, exit.
12779       */
12780       if (event->xclient.message_type != windows->wm_protocols)
12781         break;
12782       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12783         break;
12784       if (event->xclient.window == windows->magnify.id)
12785         {
12786           (void) XWithdrawWindow(display,windows->magnify.id,
12787             windows->magnify.screen);
12788           break;
12789         }
12790       break;
12791     }
12792     case ConfigureNotify:
12793     {
12794       if (event->xconfigure.window == windows->magnify.id)
12795         {
12796           unsigned int
12797             magnify;
12798 
12799           /*
12800             Magnify window has a new configuration.
12801           */
12802           windows->magnify.width=(unsigned int) event->xconfigure.width;
12803           windows->magnify.height=(unsigned int) event->xconfigure.height;
12804           if (windows->magnify.mapped == MagickFalse)
12805             break;
12806           magnify=1;
12807           while ((int) magnify <= event->xconfigure.width)
12808             magnify<<=1;
12809           while ((int) magnify <= event->xconfigure.height)
12810             magnify<<=1;
12811           magnify>>=1;
12812           if (((int) magnify != event->xconfigure.width) ||
12813               ((int) magnify != event->xconfigure.height))
12814             {
12815               XWindowChanges
12816                 window_changes;
12817 
12818               window_changes.width=(int) magnify;
12819               window_changes.height=(int) magnify;
12820               (void) XReconfigureWMWindow(display,windows->magnify.id,
12821                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12822                 &window_changes);
12823               break;
12824             }
12825           XMakeMagnifyImage(display,windows,exception);
12826           break;
12827         }
12828       break;
12829     }
12830     case Expose:
12831     {
12832       if (event->xexpose.window == windows->image.id)
12833         {
12834           XRefreshWindow(display,&windows->image,event);
12835           break;
12836         }
12837       if (event->xexpose.window == windows->pan.id)
12838         if (event->xexpose.count == 0)
12839           {
12840             XDrawPanRectangle(display,windows);
12841             break;
12842           }
12843       if (event->xexpose.window == windows->magnify.id)
12844         if (event->xexpose.count == 0)
12845           {
12846             XMakeMagnifyImage(display,windows,exception);
12847             break;
12848           }
12849       break;
12850     }
12851     case KeyPress:
12852     {
12853       char
12854         command[MagickPathExtent];
12855 
12856       KeySym
12857         key_symbol;
12858 
12859       if (event->xkey.window != windows->magnify.id)
12860         break;
12861       /*
12862         Respond to a user key press.
12863       */
12864       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12865         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12866       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12867         exception);
12868       break;
12869     }
12870     case MapNotify:
12871     {
12872       if (event->xmap.window == windows->magnify.id)
12873         {
12874           windows->magnify.mapped=MagickTrue;
12875           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12876           break;
12877         }
12878       if (event->xmap.window == windows->info.id)
12879         {
12880           windows->info.mapped=MagickTrue;
12881           break;
12882         }
12883       break;
12884     }
12885     case MotionNotify:
12886     {
12887       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12888       if (event->xmotion.window == windows->image.id)
12889         if (windows->magnify.mapped != MagickFalse )
12890           {
12891             /*
12892               Update magnified image.
12893             */
12894             x=event->xmotion.x;
12895             y=event->xmotion.y;
12896             if (x < 0)
12897               x=0;
12898             else
12899               if (x >= (int) windows->image.width)
12900                 x=(int) (windows->image.width-1);
12901             windows->magnify.x=(int) windows->image.x+x;
12902             if (y < 0)
12903               y=0;
12904             else
12905              if (y >= (int) windows->image.height)
12906                y=(int) (windows->image.height-1);
12907             windows->magnify.y=windows->image.y+y;
12908             XMakeMagnifyImage(display,windows,exception);
12909           }
12910       break;
12911     }
12912     case UnmapNotify:
12913     {
12914       if (event->xunmap.window == windows->magnify.id)
12915         {
12916           windows->magnify.mapped=MagickFalse;
12917           break;
12918         }
12919       if (event->xunmap.window == windows->info.id)
12920         {
12921           windows->info.mapped=MagickFalse;
12922           break;
12923         }
12924       break;
12925     }
12926     default:
12927       break;
12928   }
12929 }
12930 
12931 /*
12932 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12933 %                                                                             %
12934 %                                                                             %
12935 %                                                                             %
12936 +   X S e t C r o p G e o m e t r y                                           %
12937 %                                                                             %
12938 %                                                                             %
12939 %                                                                             %
12940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12941 %
12942 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12943 %  and translates it to a cropping geometry relative to the image.
12944 %
12945 %  The format of the XSetCropGeometry method is:
12946 %
12947 %      void XSetCropGeometry(Display *display,XWindows *windows,
12948 %        RectangleInfo *crop_info,Image *image)
12949 %
12950 %  A description of each parameter follows:
12951 %
12952 %    o display: Specifies a connection to an X server; returned from
12953 %      XOpenDisplay.
12954 %
12955 %    o windows: Specifies a pointer to a XWindows structure.
12956 %
12957 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12958 %      Image window to crop.
12959 %
12960 %    o image: the image.
12961 %
12962 */
XSetCropGeometry(Display * display,XWindows * windows,RectangleInfo * crop_info,Image * image)12963 static void XSetCropGeometry(Display *display,XWindows *windows,
12964   RectangleInfo *crop_info,Image *image)
12965 {
12966   char
12967     text[MagickPathExtent];
12968 
12969   int
12970     x,
12971     y;
12972 
12973   double
12974     scale_factor;
12975 
12976   unsigned int
12977     height,
12978     width;
12979 
12980   if (windows->info.mapped != MagickFalse )
12981     {
12982       /*
12983         Display info on cropping rectangle.
12984       */
12985       (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
12986         (double) crop_info->width,(double) crop_info->height,(double)
12987         crop_info->x,(double) crop_info->y);
12988       XInfoWidget(display,windows,text);
12989     }
12990   /*
12991     Cropping geometry is relative to any previous crop geometry.
12992   */
12993   x=0;
12994   y=0;
12995   width=(unsigned int) image->columns;
12996   height=(unsigned int) image->rows;
12997   if (windows->image.crop_geometry != (char *) NULL)
12998     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12999   else
13000     windows->image.crop_geometry=AcquireString((char *) NULL);
13001   /*
13002     Define the crop geometry string from the cropping rectangle.
13003   */
13004   scale_factor=(double) width/windows->image.ximage->width;
13005   if (crop_info->x > 0)
13006     x+=(int) (scale_factor*crop_info->x+0.5);
13007   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13008   if (width == 0)
13009     width=1;
13010   scale_factor=(double) height/windows->image.ximage->height;
13011   if (crop_info->y > 0)
13012     y+=(int) (scale_factor*crop_info->y+0.5);
13013   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13014   if (height == 0)
13015     height=1;
13016   (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13017     "%ux%u%+d%+d",width,height,x,y);
13018 }
13019 
13020 /*
13021 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13022 %                                                                             %
13023 %                                                                             %
13024 %                                                                             %
13025 +   X T i l e I m a g e                                                       %
13026 %                                                                             %
13027 %                                                                             %
13028 %                                                                             %
13029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13030 %
13031 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13032 %  The load or delete command is chosen from a menu.
13033 %
13034 %  The format of the XTileImage method is:
13035 %
13036 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13037 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13038 %
13039 %  A description of each parameter follows:
13040 %
13041 %    o tile_image:  XTileImage reads or deletes the tile image
13042 %      and returns it.  A null image is returned if an error occurs.
13043 %
13044 %    o display: Specifies a connection to an X server;  returned from
13045 %      XOpenDisplay.
13046 %
13047 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13048 %
13049 %    o windows: Specifies a pointer to a XWindows structure.
13050 %
13051 %    o image: the image; returned from ReadImage.
13052 %
13053 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13054 %      the entire image is refreshed.
13055 %
13056 %    o exception: return any errors or warnings in this structure.
13057 %
13058 */
XTileImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,XEvent * event,ExceptionInfo * exception)13059 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13060   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13061 {
13062   static const char
13063     *VerbMenu[] =
13064     {
13065       "Load",
13066       "Next",
13067       "Former",
13068       "Delete",
13069       "Update",
13070       (char *) NULL,
13071     };
13072 
13073   static const ModeType
13074     TileCommands[] =
13075     {
13076       TileLoadCommand,
13077       TileNextCommand,
13078       TileFormerCommand,
13079       TileDeleteCommand,
13080       TileUpdateCommand
13081     };
13082 
13083   char
13084     command[MagickPathExtent],
13085     filename[MagickPathExtent];
13086 
13087   Image
13088     *tile_image;
13089 
13090   int
13091     id,
13092     status,
13093     tile,
13094     x,
13095     y;
13096 
13097   double
13098     scale_factor;
13099 
13100   register char
13101     *p,
13102     *q;
13103 
13104   register int
13105     i;
13106 
13107   unsigned int
13108     height,
13109     width;
13110 
13111   /*
13112     Tile image is relative to montage image configuration.
13113   */
13114   x=0;
13115   y=0;
13116   width=(unsigned int) image->columns;
13117   height=(unsigned int) image->rows;
13118   if (windows->image.crop_geometry != (char *) NULL)
13119     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13120   scale_factor=(double) width/windows->image.ximage->width;
13121   event->xbutton.x+=windows->image.x;
13122   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13123   scale_factor=(double) height/windows->image.ximage->height;
13124   event->xbutton.y+=windows->image.y;
13125   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13126   /*
13127     Determine size and location of each tile in the visual image directory.
13128   */
13129   width=(unsigned int) image->columns;
13130   height=(unsigned int) image->rows;
13131   x=0;
13132   y=0;
13133   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13134   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13135     (event->xbutton.x-x)/width;
13136   if (tile < 0)
13137     {
13138       /*
13139         Button press is outside any tile.
13140       */
13141       (void) XBell(display,0);
13142       return((Image *) NULL);
13143     }
13144   /*
13145     Determine file name from the tile directory.
13146   */
13147   p=image->directory;
13148   for (i=tile; (i != 0) && (*p != '\0'); )
13149   {
13150     if (*p == '\n')
13151       i--;
13152     p++;
13153   }
13154   if (*p == '\0')
13155     {
13156       /*
13157         Button press is outside any tile.
13158       */
13159       (void) XBell(display,0);
13160       return((Image *) NULL);
13161     }
13162   /*
13163     Select a command from the pop-up menu.
13164   */
13165   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13166   if (id < 0)
13167     return((Image *) NULL);
13168   q=p;
13169   while ((*q != '\n') && (*q != '\0'))
13170     q++;
13171   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13172   /*
13173     Perform command for the selected tile.
13174   */
13175   XSetCursorState(display,windows,MagickTrue);
13176   XCheckRefreshWindows(display,windows);
13177   tile_image=NewImageList();
13178   switch (TileCommands[id])
13179   {
13180     case TileLoadCommand:
13181     {
13182       /*
13183         Load tile image.
13184       */
13185       XCheckRefreshWindows(display,windows);
13186       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13187         MagickPathExtent);
13188       (void) CopyMagickString(resource_info->image_info->filename,filename,
13189         MagickPathExtent);
13190       tile_image=ReadImage(resource_info->image_info,exception);
13191       CatchException(exception);
13192       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13193       break;
13194     }
13195     case TileNextCommand:
13196     {
13197       /*
13198         Display next image.
13199       */
13200       XClientMessage(display,windows->image.id,windows->im_protocols,
13201         windows->im_next_image,CurrentTime);
13202       break;
13203     }
13204     case TileFormerCommand:
13205     {
13206       /*
13207         Display former image.
13208       */
13209       XClientMessage(display,windows->image.id,windows->im_protocols,
13210         windows->im_former_image,CurrentTime);
13211       break;
13212     }
13213     case TileDeleteCommand:
13214     {
13215       /*
13216         Delete tile image.
13217       */
13218       if (IsPathAccessible(filename) == MagickFalse)
13219         {
13220           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13221           break;
13222         }
13223       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13224       if (status <= 0)
13225         break;
13226       status=ShredFile(filename);
13227       if (status != MagickFalse )
13228         {
13229           XNoticeWidget(display,windows,"Unable to delete image file:",
13230             filename);
13231           break;
13232         }
13233     }
13234     case TileUpdateCommand:
13235     {
13236       int
13237         x_offset,
13238         y_offset;
13239 
13240       PixelInfo
13241         pixel;
13242 
13243       register int
13244         j;
13245 
13246       register Quantum
13247         *s;
13248 
13249       /*
13250         Ensure all the images exist.
13251       */
13252       tile=0;
13253       GetPixelInfo(image,&pixel);
13254       for (p=image->directory; *p != '\0'; p++)
13255       {
13256         CacheView
13257           *image_view;
13258 
13259         q=p;
13260         while ((*q != '\n') && (*q != '\0'))
13261           q++;
13262         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13263         p=q;
13264         if (IsPathAccessible(filename) != MagickFalse )
13265           {
13266             tile++;
13267             continue;
13268           }
13269         /*
13270           Overwrite tile with background color.
13271         */
13272         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13273         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13274         image_view=AcquireAuthenticCacheView(image,exception);
13275         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13276         for (i=0; i < (int) height; i++)
13277         {
13278           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13279             y_offset+i,width,1,exception);
13280           if (s == (Quantum *) NULL)
13281             break;
13282           for (j=0; j < (int) width; j++)
13283           {
13284             SetPixelViaPixelInfo(image,&pixel,s);
13285             s+=GetPixelChannels(image);
13286           }
13287           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13288             break;
13289         }
13290         image_view=DestroyCacheView(image_view);
13291         tile++;
13292       }
13293       windows->image.window_changes.width=(int) image->columns;
13294       windows->image.window_changes.height=(int) image->rows;
13295       XConfigureImageColormap(display,resource_info,windows,image,exception);
13296       (void) XConfigureImage(display,resource_info,windows,image,exception);
13297       break;
13298     }
13299     default:
13300       break;
13301   }
13302   XSetCursorState(display,windows,MagickFalse);
13303   return(tile_image);
13304 }
13305 
13306 /*
13307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13308 %                                                                             %
13309 %                                                                             %
13310 %                                                                             %
13311 +   X T r a n s l a t e I m a g e                                             %
13312 %                                                                             %
13313 %                                                                             %
13314 %                                                                             %
13315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13316 %
13317 %  XTranslateImage() translates the image within an Image window by one pixel
13318 %  as specified by the key symbol.  If the image has a montage string the
13319 %  translation is respect to the width and height contained within the string.
13320 %
13321 %  The format of the XTranslateImage method is:
13322 %
13323 %      void XTranslateImage(Display *display,XWindows *windows,
13324 %        Image *image,const KeySym key_symbol)
13325 %
13326 %  A description of each parameter follows:
13327 %
13328 %    o display: Specifies a connection to an X server; returned from
13329 %      XOpenDisplay.
13330 %
13331 %    o windows: Specifies a pointer to a XWindows structure.
13332 %
13333 %    o image: the image.
13334 %
13335 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13336 %      to trim.
13337 %
13338 */
XTranslateImage(Display * display,XWindows * windows,Image * image,const KeySym key_symbol)13339 static void XTranslateImage(Display *display,XWindows *windows,
13340   Image *image,const KeySym key_symbol)
13341 {
13342   char
13343     text[MagickPathExtent];
13344 
13345   int
13346     x,
13347     y;
13348 
13349   unsigned int
13350     x_offset,
13351     y_offset;
13352 
13353   /*
13354     User specified a pan position offset.
13355   */
13356   x_offset=windows->image.width;
13357   y_offset=windows->image.height;
13358   if (image->montage != (char *) NULL)
13359     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13360   switch ((int) key_symbol)
13361   {
13362     case XK_Home:
13363     case XK_KP_Home:
13364     {
13365       windows->image.x=(int) windows->image.width/2;
13366       windows->image.y=(int) windows->image.height/2;
13367       break;
13368     }
13369     case XK_Left:
13370     case XK_KP_Left:
13371     {
13372       windows->image.x-=x_offset;
13373       break;
13374     }
13375     case XK_Next:
13376     case XK_Up:
13377     case XK_KP_Up:
13378     {
13379       windows->image.y-=y_offset;
13380       break;
13381     }
13382     case XK_Right:
13383     case XK_KP_Right:
13384     {
13385       windows->image.x+=x_offset;
13386       break;
13387     }
13388     case XK_Prior:
13389     case XK_Down:
13390     case XK_KP_Down:
13391     {
13392       windows->image.y+=y_offset;
13393       break;
13394     }
13395     default:
13396       return;
13397   }
13398   /*
13399     Check boundary conditions.
13400   */
13401   if (windows->image.x < 0)
13402     windows->image.x=0;
13403   else
13404     if ((int) (windows->image.x+windows->image.width) >
13405         windows->image.ximage->width)
13406       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13407   if (windows->image.y < 0)
13408     windows->image.y=0;
13409   else
13410     if ((int) (windows->image.y+windows->image.height) >
13411         windows->image.ximage->height)
13412       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13413   /*
13414     Refresh Image window.
13415   */
13416   (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13417     windows->image.width,windows->image.height,windows->image.x,
13418     windows->image.y);
13419   XInfoWidget(display,windows,text);
13420   XCheckRefreshWindows(display,windows);
13421   XDrawPanRectangle(display,windows);
13422   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13423   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13424 }
13425 
13426 /*
13427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13428 %                                                                             %
13429 %                                                                             %
13430 %                                                                             %
13431 +   X T r i m I m a g e                                                       %
13432 %                                                                             %
13433 %                                                                             %
13434 %                                                                             %
13435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13436 %
13437 %  XTrimImage() trims the edges from the Image window.
13438 %
13439 %  The format of the XTrimImage method is:
13440 %
13441 %      MagickBooleanType XTrimImage(Display *display,
13442 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13443 %        ExceptionInfo *exception)
13444 %
13445 %  A description of each parameter follows:
13446 %
13447 %    o display: Specifies a connection to an X server; returned from
13448 %      XOpenDisplay.
13449 %
13450 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13451 %
13452 %    o windows: Specifies a pointer to a XWindows structure.
13453 %
13454 %    o image: the image.
13455 %
13456 %    o exception: return any errors or warnings in this structure.
13457 %
13458 */
XTrimImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)13459 static MagickBooleanType XTrimImage(Display *display,
13460   XResourceInfo *resource_info,XWindows *windows,Image *image,
13461   ExceptionInfo *exception)
13462 {
13463   RectangleInfo
13464     trim_info;
13465 
13466   register int
13467     x,
13468     y;
13469 
13470   size_t
13471     background,
13472     pixel;
13473 
13474   /*
13475     Trim edges from image.
13476   */
13477   XSetCursorState(display,windows,MagickTrue);
13478   XCheckRefreshWindows(display,windows);
13479   /*
13480     Crop the left edge.
13481   */
13482   background=XGetPixel(windows->image.ximage,0,0);
13483   trim_info.width=(size_t) windows->image.ximage->width;
13484   for (x=0; x < windows->image.ximage->width; x++)
13485   {
13486     for (y=0; y < windows->image.ximage->height; y++)
13487     {
13488       pixel=XGetPixel(windows->image.ximage,x,y);
13489       if (pixel != background)
13490         break;
13491     }
13492     if (y < windows->image.ximage->height)
13493       break;
13494   }
13495   trim_info.x=(ssize_t) x;
13496   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13497     {
13498       XSetCursorState(display,windows,MagickFalse);
13499       return(MagickFalse);
13500     }
13501   /*
13502     Crop the right edge.
13503   */
13504   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13505   for (x=windows->image.ximage->width-1; x != 0; x--)
13506   {
13507     for (y=0; y < windows->image.ximage->height; y++)
13508     {
13509       pixel=XGetPixel(windows->image.ximage,x,y);
13510       if (pixel != background)
13511         break;
13512     }
13513     if (y < windows->image.ximage->height)
13514       break;
13515   }
13516   trim_info.width=(size_t) (x-trim_info.x+1);
13517   /*
13518     Crop the top edge.
13519   */
13520   background=XGetPixel(windows->image.ximage,0,0);
13521   trim_info.height=(size_t) windows->image.ximage->height;
13522   for (y=0; y < windows->image.ximage->height; y++)
13523   {
13524     for (x=0; x < windows->image.ximage->width; x++)
13525     {
13526       pixel=XGetPixel(windows->image.ximage,x,y);
13527       if (pixel != background)
13528         break;
13529     }
13530     if (x < windows->image.ximage->width)
13531       break;
13532   }
13533   trim_info.y=(ssize_t) y;
13534   /*
13535     Crop the bottom edge.
13536   */
13537   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13538   for (y=windows->image.ximage->height-1; y != 0; y--)
13539   {
13540     for (x=0; x < windows->image.ximage->width; x++)
13541     {
13542       pixel=XGetPixel(windows->image.ximage,x,y);
13543       if (pixel != background)
13544         break;
13545     }
13546     if (x < windows->image.ximage->width)
13547       break;
13548   }
13549   trim_info.height=(size_t) y-trim_info.y+1;
13550   if (((unsigned int) trim_info.width != windows->image.width) ||
13551       ((unsigned int) trim_info.height != windows->image.height))
13552     {
13553       /*
13554         Reconfigure Image window as defined by the trimming rectangle.
13555       */
13556       XSetCropGeometry(display,windows,&trim_info,image);
13557       windows->image.window_changes.width=(int) trim_info.width;
13558       windows->image.window_changes.height=(int) trim_info.height;
13559       (void) XConfigureImage(display,resource_info,windows,image,exception);
13560     }
13561   XSetCursorState(display,windows,MagickFalse);
13562   return(MagickTrue);
13563 }
13564 
13565 /*
13566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13567 %                                                                             %
13568 %                                                                             %
13569 %                                                                             %
13570 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13571 %                                                                             %
13572 %                                                                             %
13573 %                                                                             %
13574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13575 %
13576 %  XVisualDirectoryImage() creates a Visual Image Directory.
13577 %
13578 %  The format of the XVisualDirectoryImage method is:
13579 %
13580 %      Image *XVisualDirectoryImage(Display *display,
13581 %        XResourceInfo *resource_info,XWindows *windows,
13582 %        ExceptionInfo *exception)
13583 %
13584 %  A description of each parameter follows:
13585 %
13586 %    o display: Specifies a connection to an X server; returned from
13587 %      XOpenDisplay.
13588 %
13589 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13590 %
13591 %    o windows: Specifies a pointer to a XWindows structure.
13592 %
13593 %    o exception: return any errors or warnings in this structure.
13594 %
13595 */
XVisualDirectoryImage(Display * display,XResourceInfo * resource_info,XWindows * windows,ExceptionInfo * exception)13596 static Image *XVisualDirectoryImage(Display *display,
13597   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13598 {
13599 #define TileImageTag  "Scale/Image"
13600 #define XClientName  "montage"
13601 
13602   char
13603     **filelist;
13604 
13605   Image
13606     *images,
13607     *montage_image,
13608     *next_image,
13609     *thumbnail_image;
13610 
13611   ImageInfo
13612     *read_info;
13613 
13614   int
13615     number_files;
13616 
13617   MagickBooleanType
13618     backdrop;
13619 
13620   MagickStatusType
13621     status;
13622 
13623   MontageInfo
13624     *montage_info;
13625 
13626   RectangleInfo
13627     geometry;
13628 
13629   register int
13630     i;
13631 
13632   static char
13633     filename[MagickPathExtent] = "\0",
13634     filenames[MagickPathExtent] = "*";
13635 
13636   XResourceInfo
13637     background_resources;
13638 
13639   /*
13640     Request file name from user.
13641   */
13642   XFileBrowserWidget(display,windows,"Directory",filenames);
13643   if (*filenames == '\0')
13644     return((Image *) NULL);
13645   /*
13646     Expand the filenames.
13647   */
13648   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13649   if (filelist == (char **) NULL)
13650     {
13651       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13652         filenames);
13653       return((Image *) NULL);
13654     }
13655   number_files=1;
13656   filelist[0]=filenames;
13657   status=ExpandFilenames(&number_files,&filelist);
13658   if ((status == MagickFalse) || (number_files == 0))
13659     {
13660       if (number_files == 0)
13661         ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13662       else
13663         ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13664           filenames);
13665       return((Image *) NULL);
13666     }
13667   /*
13668     Set image background resources.
13669   */
13670   background_resources=(*resource_info);
13671   background_resources.window_id=AcquireString("");
13672   (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13673     "0x%lx",windows->image.id);
13674   background_resources.backdrop=MagickTrue;
13675   /*
13676     Read each image and convert them to a tile.
13677   */
13678   backdrop=((windows->visual_info->klass == TrueColor) ||
13679     (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13680   read_info=CloneImageInfo(resource_info->image_info);
13681   (void) SetImageOption(read_info,"jpeg:size","120x120");
13682   (void) CloneString(&read_info->size,DefaultTileGeometry);
13683   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13684     (void *) NULL);
13685   images=NewImageList();
13686   XSetCursorState(display,windows,MagickTrue);
13687   XCheckRefreshWindows(display,windows);
13688   for (i=0; i < (int) number_files; i++)
13689   {
13690     (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13691     filelist[i]=DestroyString(filelist[i]);
13692     *read_info->magick='\0';
13693     next_image=ReadImage(read_info,exception);
13694     CatchException(exception);
13695     if (next_image != (Image *) NULL)
13696       {
13697         (void) DeleteImageProperty(next_image,"label");
13698         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13699           read_info,next_image,DefaultTileLabel,exception),exception);
13700         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13701           exception);
13702         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13703           geometry.height,exception);
13704         if (thumbnail_image != (Image *) NULL)
13705           {
13706             next_image=DestroyImage(next_image);
13707             next_image=thumbnail_image;
13708           }
13709         if (backdrop)
13710           {
13711             (void) XDisplayBackgroundImage(display,&background_resources,
13712               next_image,exception);
13713             XSetCursorState(display,windows,MagickTrue);
13714           }
13715         AppendImageToList(&images,next_image);
13716         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13717           {
13718             MagickBooleanType
13719               proceed;
13720 
13721             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13722               (MagickSizeType) number_files);
13723             if (proceed == MagickFalse)
13724               break;
13725           }
13726       }
13727   }
13728   filelist=(char **) RelinquishMagickMemory(filelist);
13729   if (images == (Image *) NULL)
13730     {
13731       read_info=DestroyImageInfo(read_info);
13732       XSetCursorState(display,windows,MagickFalse);
13733       ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13734       return((Image *) NULL);
13735     }
13736   /*
13737     Create the Visual Image Directory.
13738   */
13739   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13740   montage_info->pointsize=10;
13741   if (resource_info->font != (char *) NULL)
13742     (void) CloneString(&montage_info->font,resource_info->font);
13743   (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13744   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13745     images),exception);
13746   images=DestroyImageList(images);
13747   montage_info=DestroyMontageInfo(montage_info);
13748   read_info=DestroyImageInfo(read_info);
13749   XSetCursorState(display,windows,MagickFalse);
13750   if (montage_image == (Image *) NULL)
13751     return(montage_image);
13752   XClientMessage(display,windows->image.id,windows->im_protocols,
13753     windows->im_next_image,CurrentTime);
13754   return(montage_image);
13755 }
13756 
13757 /*
13758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13759 %                                                                             %
13760 %                                                                             %
13761 %                                                                             %
13762 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13763 %                                                                             %
13764 %                                                                             %
13765 %                                                                             %
13766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13767 %
13768 %  XDisplayBackgroundImage() displays an image in the background of a window.
13769 %
13770 %  The format of the XDisplayBackgroundImage method is:
13771 %
13772 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13773 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13774 %
13775 %  A description of each parameter follows:
13776 %
13777 %    o display: Specifies a connection to an X server;  returned from
13778 %      XOpenDisplay.
13779 %
13780 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13781 %
13782 %    o image: the image.
13783 %
13784 %    o exception: return any errors or warnings in this structure.
13785 %
13786 */
XDisplayBackgroundImage(Display * display,XResourceInfo * resource_info,Image * image,ExceptionInfo * exception)13787 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13788   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13789 {
13790   char
13791     geometry[MagickPathExtent],
13792     visual_type[MagickPathExtent];
13793 
13794   int
13795     height,
13796     status,
13797     width;
13798 
13799   RectangleInfo
13800     geometry_info;
13801 
13802   static XPixelInfo
13803     pixel;
13804 
13805   static XStandardColormap
13806     *map_info;
13807 
13808   static XVisualInfo
13809     *visual_info = (XVisualInfo *) NULL;
13810 
13811   static XWindowInfo
13812     window_info;
13813 
13814   size_t
13815     delay;
13816 
13817   Window
13818     root_window;
13819 
13820   XGCValues
13821     context_values;
13822 
13823   XResourceInfo
13824     resources;
13825 
13826   XWindowAttributes
13827     window_attributes;
13828 
13829   /*
13830     Determine target window.
13831   */
13832   assert(image != (Image *) NULL);
13833   assert(image->signature == MagickCoreSignature);
13834   if (image->debug != MagickFalse )
13835     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13836   resources=(*resource_info);
13837   window_info.id=(Window) NULL;
13838   root_window=XRootWindow(display,XDefaultScreen(display));
13839   if (LocaleCompare(resources.window_id,"root") == 0)
13840     window_info.id=root_window;
13841   else
13842     {
13843       if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13844         window_info.id=XWindowByID(display,root_window,
13845           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13846       if (window_info.id == (Window) NULL)
13847         window_info.id=XWindowByName(display,root_window,resources.window_id);
13848     }
13849   if (window_info.id == (Window) NULL)
13850     {
13851       ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13852         resources.window_id);
13853       return(MagickFalse);
13854     }
13855   /*
13856     Determine window visual id.
13857   */
13858   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13859   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13860   (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13861   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13862   if (status != 0)
13863     (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13864       XVisualIDFromVisual(window_attributes.visual));
13865   if (visual_info == (XVisualInfo *) NULL)
13866     {
13867       /*
13868         Allocate standard colormap.
13869       */
13870       map_info=XAllocStandardColormap();
13871       if (map_info == (XStandardColormap *) NULL)
13872         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13873           image->filename);
13874       map_info->colormap=(Colormap) NULL;
13875       pixel.pixels=(unsigned long *) NULL;
13876       /*
13877         Initialize visual info.
13878       */
13879       resources.map_type=(char *) NULL;
13880       resources.visual_type=visual_type;
13881       visual_info=XBestVisualInfo(display,map_info,&resources);
13882       if (visual_info == (XVisualInfo *) NULL)
13883         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13884           resources.visual_type);
13885       /*
13886         Initialize window info.
13887       */
13888       window_info.ximage=(XImage *) NULL;
13889       window_info.matte_image=(XImage *) NULL;
13890       window_info.pixmap=(Pixmap) NULL;
13891       window_info.matte_pixmap=(Pixmap) NULL;
13892     }
13893   /*
13894     Free previous root colors.
13895   */
13896   if (window_info.id == root_window)
13897     (void) XDestroyWindowColors(display,root_window);
13898   /*
13899     Initialize Standard Colormap.
13900   */
13901   resources.colormap=SharedColormap;
13902   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13903     exception);
13904   /*
13905     Graphic context superclass.
13906   */
13907   context_values.background=pixel.background_color.pixel;
13908   context_values.foreground=pixel.foreground_color.pixel;
13909   pixel.annotate_context=XCreateGC(display,window_info.id,
13910     (size_t) (GCBackground | GCForeground),&context_values);
13911   if (pixel.annotate_context == (GC) NULL)
13912     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13913       image->filename);
13914   /*
13915     Initialize Image window attributes.
13916   */
13917   window_info.name=AcquireString("\0");
13918   window_info.icon_name=AcquireString("\0");
13919   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13920     &resources,&window_info);
13921   /*
13922     Create the X image.
13923   */
13924   window_info.width=(unsigned int) image->columns;
13925   window_info.height=(unsigned int) image->rows;
13926   if ((image->columns != window_info.width) ||
13927       (image->rows != window_info.height))
13928     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13929       image->filename);
13930   (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13931     window_attributes.width,window_attributes.height);
13932   geometry_info.width=window_info.width;
13933   geometry_info.height=window_info.height;
13934   geometry_info.x=(ssize_t) window_info.x;
13935   geometry_info.y=(ssize_t) window_info.y;
13936   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13937     &geometry_info.width,&geometry_info.height);
13938   window_info.width=(unsigned int) geometry_info.width;
13939   window_info.height=(unsigned int) geometry_info.height;
13940   window_info.x=(int) geometry_info.x;
13941   window_info.y=(int) geometry_info.y;
13942   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13943     window_info.height,exception);
13944   if (status == MagickFalse)
13945     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13946       image->filename);
13947   window_info.x=0;
13948   window_info.y=0;
13949   if (image->debug != MagickFalse )
13950     {
13951       (void) LogMagickEvent(X11Event,GetMagickModule(),
13952         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13953         (double) image->columns,(double) image->rows);
13954       if (image->colors != 0)
13955         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13956           image->colors);
13957       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13958     }
13959   /*
13960     Adjust image dimensions as specified by backdrop or geometry options.
13961   */
13962   width=(int) window_info.width;
13963   height=(int) window_info.height;
13964   if (resources.backdrop != MagickFalse )
13965     {
13966       /*
13967         Center image on window.
13968       */
13969       window_info.x=(window_attributes.width/2)-
13970         (window_info.ximage->width/2);
13971       window_info.y=(window_attributes.height/2)-
13972         (window_info.ximage->height/2);
13973       width=window_attributes.width;
13974       height=window_attributes.height;
13975     }
13976   if ((resources.image_geometry != (char *) NULL) &&
13977       (*resources.image_geometry != '\0'))
13978     {
13979       char
13980         default_geometry[MagickPathExtent];
13981 
13982       int
13983         flags,
13984         gravity;
13985 
13986       XSizeHints
13987         *size_hints;
13988 
13989       /*
13990         User specified geometry.
13991       */
13992       size_hints=XAllocSizeHints();
13993       if (size_hints == (XSizeHints *) NULL)
13994         ThrowXWindowFatalException(ResourceLimitFatalError,
13995           "MemoryAllocationFailed",image->filename);
13996       size_hints->flags=0L;
13997       (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
13998         width,height);
13999       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14000         default_geometry,window_info.border_width,size_hints,&window_info.x,
14001         &window_info.y,&width,&height,&gravity);
14002       if (flags & (XValue | YValue))
14003         {
14004           width=window_attributes.width;
14005           height=window_attributes.height;
14006         }
14007       (void) XFree((void *) size_hints);
14008     }
14009   /*
14010     Create the X pixmap.
14011   */
14012   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14013     (unsigned int) height,window_info.depth);
14014   if (window_info.pixmap == (Pixmap) NULL)
14015     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14016       image->filename);
14017   /*
14018     Display pixmap on the window.
14019   */
14020   if (((unsigned int) width > window_info.width) ||
14021       ((unsigned int) height > window_info.height))
14022     (void) XFillRectangle(display,window_info.pixmap,
14023       window_info.annotate_context,0,0,(unsigned int) width,
14024       (unsigned int) height);
14025   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14026     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14027     window_info.width,(unsigned int) window_info.height);
14028   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14029   (void) XClearWindow(display,window_info.id);
14030   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14031   XDelay(display,delay == 0UL ? 10UL : delay);
14032   (void) XSync(display,MagickFalse);
14033   return(window_info.id == root_window ? MagickTrue : MagickFalse);
14034 }
14035 
14036 /*
14037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14038 %                                                                             %
14039 %                                                                             %
14040 %                                                                             %
14041 +   X D i s p l a y I m a g e                                                 %
14042 %                                                                             %
14043 %                                                                             %
14044 %                                                                             %
14045 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14046 %
14047 %  XDisplayImage() displays an image via X11.  A new image is created and
14048 %  returned if the user interactively transforms the displayed image.
14049 %
14050 %  The format of the XDisplayImage method is:
14051 %
14052 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14053 %        char **argv,int argc,Image **image,size_t *state,
14054 %        ExceptionInfo *exception)
14055 %
14056 %  A description of each parameter follows:
14057 %
14058 %    o nexus:  Method XDisplayImage returns an image when the
14059 %      user chooses 'Open Image' from the command menu or picks a tile
14060 %      from the image directory.  Otherwise a null image is returned.
14061 %
14062 %    o display: Specifies a connection to an X server;  returned from
14063 %      XOpenDisplay.
14064 %
14065 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14066 %
14067 %    o argv: Specifies the application's argument list.
14068 %
14069 %    o argc: Specifies the number of arguments.
14070 %
14071 %    o image: Specifies an address to an address of an Image structure;
14072 %
14073 %    o exception: return any errors or warnings in this structure.
14074 %
14075 */
XDisplayImage(Display * display,XResourceInfo * resource_info,char ** argv,int argc,Image ** image,size_t * state,ExceptionInfo * exception)14076 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14077   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14078 {
14079 #define MagnifySize  256  /* must be a power of 2 */
14080 #define MagickMenus  10
14081 #define MagickTitle  "Commands"
14082 
14083   static const char
14084     *CommandMenu[] =
14085     {
14086       "File",
14087       "Edit",
14088       "View",
14089       "Transform",
14090       "Enhance",
14091       "Effects",
14092       "F/X",
14093       "Image Edit",
14094       "Miscellany",
14095       "Help",
14096       (char *) NULL
14097     },
14098     *FileMenu[] =
14099     {
14100       "Open...",
14101       "Next",
14102       "Former",
14103       "Select...",
14104       "Save...",
14105       "Print...",
14106       "Delete...",
14107       "New...",
14108       "Visual Directory...",
14109       "Quit",
14110       (char *) NULL
14111     },
14112     *EditMenu[] =
14113     {
14114       "Undo",
14115       "Redo",
14116       "Cut",
14117       "Copy",
14118       "Paste",
14119       (char *) NULL
14120     },
14121     *ViewMenu[] =
14122     {
14123       "Half Size",
14124       "Original Size",
14125       "Double Size",
14126       "Resize...",
14127       "Apply",
14128       "Refresh",
14129       "Restore",
14130       (char *) NULL
14131     },
14132     *TransformMenu[] =
14133     {
14134       "Crop",
14135       "Chop",
14136       "Flop",
14137       "Flip",
14138       "Rotate Right",
14139       "Rotate Left",
14140       "Rotate...",
14141       "Shear...",
14142       "Roll...",
14143       "Trim Edges",
14144       (char *) NULL
14145     },
14146     *EnhanceMenu[] =
14147     {
14148       "Hue...",
14149       "Saturation...",
14150       "Brightness...",
14151       "Gamma...",
14152       "Spiff",
14153       "Dull",
14154       "Contrast Stretch...",
14155       "Sigmoidal Contrast...",
14156       "Normalize",
14157       "Equalize",
14158       "Negate",
14159       "Grayscale",
14160       "Map...",
14161       "Quantize...",
14162       (char *) NULL
14163     },
14164     *EffectsMenu[] =
14165     {
14166       "Despeckle",
14167       "Emboss",
14168       "Reduce Noise",
14169       "Add Noise...",
14170       "Sharpen...",
14171       "Blur...",
14172       "Threshold...",
14173       "Edge Detect...",
14174       "Spread...",
14175       "Shade...",
14176       "Raise...",
14177       "Segment...",
14178       (char *) NULL
14179     },
14180     *FXMenu[] =
14181     {
14182       "Solarize...",
14183       "Sepia Tone...",
14184       "Swirl...",
14185       "Implode...",
14186       "Vignette...",
14187       "Wave...",
14188       "Oil Paint...",
14189       "Charcoal Draw...",
14190       (char *) NULL
14191     },
14192     *ImageEditMenu[] =
14193     {
14194       "Annotate...",
14195       "Draw...",
14196       "Color...",
14197       "Matte...",
14198       "Composite...",
14199       "Add Border...",
14200       "Add Frame...",
14201       "Comment...",
14202       "Launch...",
14203       "Region of Interest...",
14204       (char *) NULL
14205     },
14206     *MiscellanyMenu[] =
14207     {
14208       "Image Info",
14209       "Zoom Image",
14210       "Show Preview...",
14211       "Show Histogram",
14212       "Show Matte",
14213       "Background...",
14214       "Slide Show...",
14215       "Preferences...",
14216       (char *) NULL
14217     },
14218     *HelpMenu[] =
14219     {
14220       "Overview",
14221       "Browse Documentation",
14222       "About Display",
14223       (char *) NULL
14224     },
14225     *ShortCutsMenu[] =
14226     {
14227       "Next",
14228       "Former",
14229       "Open...",
14230       "Save...",
14231       "Print...",
14232       "Undo",
14233       "Restore",
14234       "Image Info",
14235       "Quit",
14236       (char *) NULL
14237     },
14238     *VirtualMenu[] =
14239     {
14240       "Image Info",
14241       "Print",
14242       "Next",
14243       "Quit",
14244       (char *) NULL
14245     };
14246 
14247   static const char
14248     **Menus[MagickMenus] =
14249     {
14250       FileMenu,
14251       EditMenu,
14252       ViewMenu,
14253       TransformMenu,
14254       EnhanceMenu,
14255       EffectsMenu,
14256       FXMenu,
14257       ImageEditMenu,
14258       MiscellanyMenu,
14259       HelpMenu
14260     };
14261 
14262   static CommandType
14263     CommandMenus[] =
14264     {
14265       NullCommand,
14266       NullCommand,
14267       NullCommand,
14268       NullCommand,
14269       NullCommand,
14270       NullCommand,
14271       NullCommand,
14272       NullCommand,
14273       NullCommand,
14274       NullCommand,
14275     },
14276     FileCommands[] =
14277     {
14278       OpenCommand,
14279       NextCommand,
14280       FormerCommand,
14281       SelectCommand,
14282       SaveCommand,
14283       PrintCommand,
14284       DeleteCommand,
14285       NewCommand,
14286       VisualDirectoryCommand,
14287       QuitCommand
14288     },
14289     EditCommands[] =
14290     {
14291       UndoCommand,
14292       RedoCommand,
14293       CutCommand,
14294       CopyCommand,
14295       PasteCommand
14296     },
14297     ViewCommands[] =
14298     {
14299       HalfSizeCommand,
14300       OriginalSizeCommand,
14301       DoubleSizeCommand,
14302       ResizeCommand,
14303       ApplyCommand,
14304       RefreshCommand,
14305       RestoreCommand
14306     },
14307     TransformCommands[] =
14308     {
14309       CropCommand,
14310       ChopCommand,
14311       FlopCommand,
14312       FlipCommand,
14313       RotateRightCommand,
14314       RotateLeftCommand,
14315       RotateCommand,
14316       ShearCommand,
14317       RollCommand,
14318       TrimCommand
14319     },
14320     EnhanceCommands[] =
14321     {
14322       HueCommand,
14323       SaturationCommand,
14324       BrightnessCommand,
14325       GammaCommand,
14326       SpiffCommand,
14327       DullCommand,
14328       ContrastStretchCommand,
14329       SigmoidalContrastCommand,
14330       NormalizeCommand,
14331       EqualizeCommand,
14332       NegateCommand,
14333       GrayscaleCommand,
14334       MapCommand,
14335       QuantizeCommand
14336     },
14337     EffectsCommands[] =
14338     {
14339       DespeckleCommand,
14340       EmbossCommand,
14341       ReduceNoiseCommand,
14342       AddNoiseCommand,
14343       SharpenCommand,
14344       BlurCommand,
14345       ThresholdCommand,
14346       EdgeDetectCommand,
14347       SpreadCommand,
14348       ShadeCommand,
14349       RaiseCommand,
14350       SegmentCommand
14351     },
14352     FXCommands[] =
14353     {
14354       SolarizeCommand,
14355       SepiaToneCommand,
14356       SwirlCommand,
14357       ImplodeCommand,
14358       VignetteCommand,
14359       WaveCommand,
14360       OilPaintCommand,
14361       CharcoalDrawCommand
14362     },
14363     ImageEditCommands[] =
14364     {
14365       AnnotateCommand,
14366       DrawCommand,
14367       ColorCommand,
14368       MatteCommand,
14369       CompositeCommand,
14370       AddBorderCommand,
14371       AddFrameCommand,
14372       CommentCommand,
14373       LaunchCommand,
14374       RegionofInterestCommand
14375     },
14376     MiscellanyCommands[] =
14377     {
14378       InfoCommand,
14379       ZoomCommand,
14380       ShowPreviewCommand,
14381       ShowHistogramCommand,
14382       ShowMatteCommand,
14383       BackgroundCommand,
14384       SlideShowCommand,
14385       PreferencesCommand
14386     },
14387     HelpCommands[] =
14388     {
14389       HelpCommand,
14390       BrowseDocumentationCommand,
14391       VersionCommand
14392     },
14393     ShortCutsCommands[] =
14394     {
14395       NextCommand,
14396       FormerCommand,
14397       OpenCommand,
14398       SaveCommand,
14399       PrintCommand,
14400       UndoCommand,
14401       RestoreCommand,
14402       InfoCommand,
14403       QuitCommand
14404     },
14405     VirtualCommands[] =
14406     {
14407       InfoCommand,
14408       PrintCommand,
14409       NextCommand,
14410       QuitCommand
14411     };
14412 
14413   static CommandType
14414     *Commands[MagickMenus] =
14415     {
14416       FileCommands,
14417       EditCommands,
14418       ViewCommands,
14419       TransformCommands,
14420       EnhanceCommands,
14421       EffectsCommands,
14422       FXCommands,
14423       ImageEditCommands,
14424       MiscellanyCommands,
14425       HelpCommands
14426     };
14427 
14428   char
14429     command[MagickPathExtent],
14430     *directory,
14431     geometry[MagickPathExtent],
14432     resource_name[MagickPathExtent];
14433 
14434   CommandType
14435     command_type;
14436 
14437   Image
14438     *display_image,
14439     *nexus;
14440 
14441   int
14442     entry,
14443     id;
14444 
14445   KeySym
14446     key_symbol;
14447 
14448   MagickStatusType
14449     context_mask,
14450     status;
14451 
14452   RectangleInfo
14453     geometry_info;
14454 
14455   register int
14456     i;
14457 
14458   static char
14459     working_directory[MagickPathExtent];
14460 
14461   static XPoint
14462     vid_info;
14463 
14464   static XWindowInfo
14465     *magick_windows[MaxXWindows];
14466 
14467   static unsigned int
14468     number_windows;
14469 
14470   struct stat
14471     attributes;
14472 
14473   time_t
14474     timer,
14475     timestamp,
14476     update_time;
14477 
14478   unsigned int
14479     height,
14480     width;
14481 
14482   size_t
14483     delay;
14484 
14485   WarningHandler
14486     warning_handler;
14487 
14488   Window
14489     root_window;
14490 
14491   XClassHint
14492     *class_hints;
14493 
14494   XEvent
14495     event;
14496 
14497   XFontStruct
14498     *font_info;
14499 
14500   XGCValues
14501     context_values;
14502 
14503   XPixelInfo
14504     *icon_pixel,
14505     *pixel;
14506 
14507   XResourceInfo
14508     *icon_resources;
14509 
14510   XStandardColormap
14511     *icon_map,
14512     *map_info;
14513 
14514   XVisualInfo
14515     *icon_visual,
14516     *visual_info;
14517 
14518   XWindowChanges
14519     window_changes;
14520 
14521   XWindows
14522     *windows;
14523 
14524   XWMHints
14525     *manager_hints;
14526 
14527   assert(image != (Image **) NULL);
14528   assert((*image)->signature == MagickCoreSignature);
14529   if ((*image)->debug != MagickFalse )
14530     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14531   display_image=(*image);
14532   warning_handler=(WarningHandler) NULL;
14533   windows=XSetWindows((XWindows *) ~0);
14534   if (windows != (XWindows *) NULL)
14535     {
14536       int
14537         status;
14538 
14539       if (*working_directory == '\0')
14540         (void) CopyMagickString(working_directory,".",MagickPathExtent);
14541       status=chdir(working_directory);
14542       if (status == -1)
14543         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14544           "UnableToOpenFile","%s",working_directory);
14545       warning_handler=resource_info->display_warnings ?
14546         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14547       warning_handler=resource_info->display_warnings ?
14548         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14549     }
14550   else
14551     {
14552       /*
14553         Allocate windows structure.
14554       */
14555       resource_info->colors=display_image->colors;
14556       windows=XSetWindows(XInitializeWindows(display,resource_info));
14557       if (windows == (XWindows *) NULL)
14558         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14559           (*image)->filename);
14560       /*
14561         Initialize window id's.
14562       */
14563       number_windows=0;
14564       magick_windows[number_windows++]=(&windows->icon);
14565       magick_windows[number_windows++]=(&windows->backdrop);
14566       magick_windows[number_windows++]=(&windows->image);
14567       magick_windows[number_windows++]=(&windows->info);
14568       magick_windows[number_windows++]=(&windows->command);
14569       magick_windows[number_windows++]=(&windows->widget);
14570       magick_windows[number_windows++]=(&windows->popup);
14571       magick_windows[number_windows++]=(&windows->magnify);
14572       magick_windows[number_windows++]=(&windows->pan);
14573       for (i=0; i < (int) number_windows; i++)
14574         magick_windows[i]->id=(Window) NULL;
14575       vid_info.x=0;
14576       vid_info.y=0;
14577     }
14578   /*
14579     Initialize font info.
14580   */
14581   if (windows->font_info != (XFontStruct *) NULL)
14582     (void) XFreeFont(display,windows->font_info);
14583   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14584   if (windows->font_info == (XFontStruct *) NULL)
14585     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14586       resource_info->font);
14587   /*
14588     Initialize Standard Colormap.
14589   */
14590   map_info=windows->map_info;
14591   icon_map=windows->icon_map;
14592   visual_info=windows->visual_info;
14593   icon_visual=windows->icon_visual;
14594   pixel=windows->pixel_info;
14595   icon_pixel=windows->icon_pixel;
14596   font_info=windows->font_info;
14597   icon_resources=windows->icon_resources;
14598   class_hints=windows->class_hints;
14599   manager_hints=windows->manager_hints;
14600   root_window=XRootWindow(display,visual_info->screen);
14601   nexus=NewImageList();
14602   if (display_image->debug != MagickFalse )
14603     {
14604       (void) LogMagickEvent(X11Event,GetMagickModule(),
14605         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14606         (double) display_image->scene,(double) display_image->columns,
14607         (double) display_image->rows);
14608       if (display_image->colors != 0)
14609         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14610           display_image->colors);
14611       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14612         display_image->magick);
14613     }
14614   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14615     map_info,pixel,exception);
14616   display_image->taint=MagickFalse;
14617   /*
14618     Initialize graphic context.
14619   */
14620   windows->context.id=(Window) NULL;
14621   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14622     resource_info,&windows->context);
14623   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14624   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14625   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14626   manager_hints->flags=InputHint | StateHint;
14627   manager_hints->input=MagickFalse;
14628   manager_hints->initial_state=WithdrawnState;
14629   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14630     &windows->context);
14631   if (display_image->debug != MagickFalse )
14632     (void) LogMagickEvent(X11Event,GetMagickModule(),
14633       "Window id: 0x%lx (context)",windows->context.id);
14634   context_values.background=pixel->background_color.pixel;
14635   context_values.font=font_info->fid;
14636   context_values.foreground=pixel->foreground_color.pixel;
14637   context_values.graphics_exposures=MagickFalse;
14638   context_mask=(MagickStatusType)
14639     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14640   if (pixel->annotate_context != (GC) NULL)
14641     (void) XFreeGC(display,pixel->annotate_context);
14642   pixel->annotate_context=XCreateGC(display,windows->context.id,
14643     context_mask,&context_values);
14644   if (pixel->annotate_context == (GC) NULL)
14645     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14646       display_image->filename);
14647   context_values.background=pixel->depth_color.pixel;
14648   if (pixel->widget_context != (GC) NULL)
14649     (void) XFreeGC(display,pixel->widget_context);
14650   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14651     &context_values);
14652   if (pixel->widget_context == (GC) NULL)
14653     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14654       display_image->filename);
14655   context_values.background=pixel->foreground_color.pixel;
14656   context_values.foreground=pixel->background_color.pixel;
14657   context_values.plane_mask=context_values.background ^
14658     context_values.foreground;
14659   if (pixel->highlight_context != (GC) NULL)
14660     (void) XFreeGC(display,pixel->highlight_context);
14661   pixel->highlight_context=XCreateGC(display,windows->context.id,
14662     (size_t) (context_mask | GCPlaneMask),&context_values);
14663   if (pixel->highlight_context == (GC) NULL)
14664     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14665       display_image->filename);
14666   (void) XDestroyWindow(display,windows->context.id);
14667   /*
14668     Initialize icon window.
14669   */
14670   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14671     icon_resources,&windows->icon);
14672   windows->icon.geometry=resource_info->icon_geometry;
14673   XBestIconSize(display,&windows->icon,display_image);
14674   windows->icon.attributes.colormap=XDefaultColormap(display,
14675     icon_visual->screen);
14676   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14677   manager_hints->flags=InputHint | StateHint;
14678   manager_hints->input=MagickFalse;
14679   manager_hints->initial_state=IconicState;
14680   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14681     &windows->icon);
14682   if (display_image->debug != MagickFalse )
14683     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14684       windows->icon.id);
14685   /*
14686     Initialize graphic context for icon window.
14687   */
14688   if (icon_pixel->annotate_context != (GC) NULL)
14689     (void) XFreeGC(display,icon_pixel->annotate_context);
14690   context_values.background=icon_pixel->background_color.pixel;
14691   context_values.foreground=icon_pixel->foreground_color.pixel;
14692   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14693     (size_t) (GCBackground | GCForeground),&context_values);
14694   if (icon_pixel->annotate_context == (GC) NULL)
14695     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14696       display_image->filename);
14697   windows->icon.annotate_context=icon_pixel->annotate_context;
14698   /*
14699     Initialize Image window.
14700   */
14701   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14702     &windows->image);
14703   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14704   if (resource_info->use_shared_memory == MagickFalse)
14705     windows->image.shared_memory=MagickFalse;
14706   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14707     {
14708       char
14709         *title;
14710 
14711       title=InterpretImageProperties(resource_info->image_info,display_image,
14712         resource_info->title,exception);
14713       (void) CopyMagickString(windows->image.name,title,MagickPathExtent);
14714       (void) CopyMagickString(windows->image.icon_name,title,MagickPathExtent);
14715       title=DestroyString(title);
14716     }
14717   else
14718     {
14719       char
14720         filename[MagickPathExtent];
14721 
14722       /*
14723         Window name is the base of the filename.
14724       */
14725       GetPathComponent(display_image->magick_filename,TailPath,filename);
14726       if (display_image->scene == 0)
14727         (void) FormatLocaleString(windows->image.name,MagickPathExtent,
14728           "%s: %s",MagickPackageName,filename);
14729       else
14730         (void) FormatLocaleString(windows->image.name,MagickPathExtent,
14731           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14732           (double) display_image->scene,(double) GetImageListLength(
14733           display_image));
14734       (void) CopyMagickString(windows->image.icon_name,filename,MagickPathExtent);
14735     }
14736   if (resource_info->immutable)
14737     windows->image.immutable=MagickTrue;
14738   windows->image.use_pixmap=resource_info->use_pixmap;
14739   windows->image.geometry=resource_info->image_geometry;
14740   (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14741     XDisplayWidth(display,visual_info->screen),
14742     XDisplayHeight(display,visual_info->screen));
14743   geometry_info.width=display_image->columns;
14744   geometry_info.height=display_image->rows;
14745   geometry_info.x=0;
14746   geometry_info.y=0;
14747   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14748     &geometry_info.width,&geometry_info.height);
14749   windows->image.width=(unsigned int) geometry_info.width;
14750   windows->image.height=(unsigned int) geometry_info.height;
14751   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14752     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14753     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14754     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14755   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14756     resource_info,&windows->backdrop);
14757   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14758     {
14759       /*
14760         Initialize backdrop window.
14761       */
14762       windows->backdrop.x=0;
14763       windows->backdrop.y=0;
14764       (void) CloneString(&windows->backdrop.name,"Backdrop");
14765       windows->backdrop.flags=(size_t) (USSize | USPosition);
14766       windows->backdrop.width=(unsigned int)
14767         XDisplayWidth(display,visual_info->screen);
14768       windows->backdrop.height=(unsigned int)
14769         XDisplayHeight(display,visual_info->screen);
14770       windows->backdrop.border_width=0;
14771       windows->backdrop.immutable=MagickTrue;
14772       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14773         ButtonReleaseMask;
14774       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14775         StructureNotifyMask;
14776       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14777       manager_hints->icon_window=windows->icon.id;
14778       manager_hints->input=MagickTrue;
14779       manager_hints->initial_state=resource_info->iconic ? IconicState :
14780         NormalState;
14781       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14782         &windows->backdrop);
14783       if (display_image->debug != MagickFalse )
14784         (void) LogMagickEvent(X11Event,GetMagickModule(),
14785           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14786       (void) XMapWindow(display,windows->backdrop.id);
14787       (void) XClearWindow(display,windows->backdrop.id);
14788       if (windows->image.id != (Window) NULL)
14789         {
14790           (void) XDestroyWindow(display,windows->image.id);
14791           windows->image.id=(Window) NULL;
14792         }
14793       /*
14794         Position image in the center the backdrop.
14795       */
14796       windows->image.flags|=USPosition;
14797       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14798         (windows->image.width/2);
14799       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14800         (windows->image.height/2);
14801     }
14802   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14803   manager_hints->icon_window=windows->icon.id;
14804   manager_hints->input=MagickTrue;
14805   manager_hints->initial_state=resource_info->iconic ? IconicState :
14806     NormalState;
14807   if (windows->group_leader.id != (Window) NULL)
14808     {
14809       /*
14810         Follow the leader.
14811       */
14812       manager_hints->flags|=WindowGroupHint;
14813       manager_hints->window_group=windows->group_leader.id;
14814       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14815       if (display_image->debug != MagickFalse )
14816         (void) LogMagickEvent(X11Event,GetMagickModule(),
14817           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14818     }
14819   XMakeWindow(display,
14820     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14821     argv,argc,class_hints,manager_hints,&windows->image);
14822   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14823     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14824   if (windows->group_leader.id != (Window) NULL)
14825     (void) XSetTransientForHint(display,windows->image.id,
14826       windows->group_leader.id);
14827   if (display_image->debug != MagickFalse )
14828     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14829       windows->image.id);
14830   /*
14831     Initialize Info widget.
14832   */
14833   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14834     &windows->info);
14835   (void) CloneString(&windows->info.name,"Info");
14836   (void) CloneString(&windows->info.icon_name,"Info");
14837   windows->info.border_width=1;
14838   windows->info.x=2;
14839   windows->info.y=2;
14840   windows->info.flags|=PPosition;
14841   windows->info.attributes.win_gravity=UnmapGravity;
14842   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14843     StructureNotifyMask;
14844   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14845   manager_hints->input=MagickFalse;
14846   manager_hints->initial_state=NormalState;
14847   manager_hints->window_group=windows->image.id;
14848   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14849     &windows->info);
14850   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14851     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14852   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14853     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14854   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14855   if (windows->image.mapped != MagickFalse )
14856     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14857   if (display_image->debug != MagickFalse )
14858     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14859       windows->info.id);
14860   /*
14861     Initialize Command widget.
14862   */
14863   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14864     resource_info,&windows->command);
14865   windows->command.data=MagickMenus;
14866   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14867   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14868     resource_info->client_name);
14869   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14870     resource_name,"geometry",(char *) NULL);
14871   (void) CloneString(&windows->command.name,MagickTitle);
14872   windows->command.border_width=0;
14873   windows->command.flags|=PPosition;
14874   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14875     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14876     OwnerGrabButtonMask | StructureNotifyMask;
14877   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14878   manager_hints->input=MagickTrue;
14879   manager_hints->initial_state=NormalState;
14880   manager_hints->window_group=windows->image.id;
14881   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14882     &windows->command);
14883   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14884     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14885     HighlightHeight);
14886   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14887     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14888   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14889   if (windows->command.mapped != MagickFalse )
14890     (void) XMapRaised(display,windows->command.id);
14891   if (display_image->debug != MagickFalse )
14892     (void) LogMagickEvent(X11Event,GetMagickModule(),
14893       "Window id: 0x%lx (command)",windows->command.id);
14894   /*
14895     Initialize Widget window.
14896   */
14897   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14898     resource_info,&windows->widget);
14899   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14900     resource_info->client_name);
14901   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14902     resource_name,"geometry",(char *) NULL);
14903   windows->widget.border_width=0;
14904   windows->widget.flags|=PPosition;
14905   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14906     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14907     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14908     StructureNotifyMask;
14909   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14910   manager_hints->input=MagickTrue;
14911   manager_hints->initial_state=NormalState;
14912   manager_hints->window_group=windows->image.id;
14913   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14914     &windows->widget);
14915   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14916     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14917   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14918     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14919   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14920   if (display_image->debug != MagickFalse )
14921     (void) LogMagickEvent(X11Event,GetMagickModule(),
14922       "Window id: 0x%lx (widget)",windows->widget.id);
14923   /*
14924     Initialize popup window.
14925   */
14926   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14927     resource_info,&windows->popup);
14928   windows->popup.border_width=0;
14929   windows->popup.flags|=PPosition;
14930   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14931     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14932     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14933   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14934   manager_hints->input=MagickTrue;
14935   manager_hints->initial_state=NormalState;
14936   manager_hints->window_group=windows->image.id;
14937   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14938     &windows->popup);
14939   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14940     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14941   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14942     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14943   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14944   if (display_image->debug != MagickFalse )
14945     (void) LogMagickEvent(X11Event,GetMagickModule(),
14946       "Window id: 0x%lx (pop up)",windows->popup.id);
14947   /*
14948     Initialize Magnify window and cursor.
14949   */
14950   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14951     resource_info,&windows->magnify);
14952   if (resource_info->use_shared_memory == MagickFalse)
14953     windows->magnify.shared_memory=MagickFalse;
14954   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14955     resource_info->client_name);
14956   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14957     resource_name,"geometry",(char *) NULL);
14958   (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,"Magnify %uX",
14959     resource_info->magnify);
14960   if (windows->magnify.cursor != (Cursor) NULL)
14961     (void) XFreeCursor(display,windows->magnify.cursor);
14962   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14963     map_info->colormap,resource_info->background_color,
14964     resource_info->foreground_color);
14965   if (windows->magnify.cursor == (Cursor) NULL)
14966     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14967       display_image->filename);
14968   windows->magnify.width=MagnifySize;
14969   windows->magnify.height=MagnifySize;
14970   windows->magnify.flags|=PPosition;
14971   windows->magnify.min_width=MagnifySize;
14972   windows->magnify.min_height=MagnifySize;
14973   windows->magnify.width_inc=MagnifySize;
14974   windows->magnify.height_inc=MagnifySize;
14975   windows->magnify.data=resource_info->magnify;
14976   windows->magnify.attributes.cursor=windows->magnify.cursor;
14977   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14978     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14979     StructureNotifyMask;
14980   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14981   manager_hints->input=MagickTrue;
14982   manager_hints->initial_state=NormalState;
14983   manager_hints->window_group=windows->image.id;
14984   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14985     &windows->magnify);
14986   if (display_image->debug != MagickFalse )
14987     (void) LogMagickEvent(X11Event,GetMagickModule(),
14988       "Window id: 0x%lx (magnify)",windows->magnify.id);
14989   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14990   /*
14991     Initialize panning window.
14992   */
14993   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14994     resource_info,&windows->pan);
14995   (void) CloneString(&windows->pan.name,"Pan Icon");
14996   windows->pan.width=windows->icon.width;
14997   windows->pan.height=windows->icon.height;
14998   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
14999     resource_info->client_name);
15000   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15001     resource_name,"geometry",(char *) NULL);
15002   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15003     &windows->pan.width,&windows->pan.height);
15004   windows->pan.flags|=PPosition;
15005   windows->pan.immutable=MagickTrue;
15006   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15007     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15008     StructureNotifyMask;
15009   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15010   manager_hints->input=MagickFalse;
15011   manager_hints->initial_state=NormalState;
15012   manager_hints->window_group=windows->image.id;
15013   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15014     &windows->pan);
15015   if (display_image->debug != MagickFalse )
15016     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15017       windows->pan.id);
15018   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15019   if (windows->info.mapped != MagickFalse )
15020     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15021   if ((windows->image.mapped == MagickFalse) ||
15022       (windows->backdrop.id != (Window) NULL))
15023     (void) XMapWindow(display,windows->image.id);
15024   /*
15025     Set our progress monitor and warning handlers.
15026   */
15027   if (warning_handler == (WarningHandler) NULL)
15028     {
15029       warning_handler=resource_info->display_warnings ?
15030         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15031       warning_handler=resource_info->display_warnings ?
15032         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15033     }
15034   /*
15035     Initialize Image and Magnify X images.
15036   */
15037   windows->image.x=0;
15038   windows->image.y=0;
15039   windows->magnify.shape=MagickFalse;
15040   width=(unsigned int) display_image->columns;
15041   height=(unsigned int) display_image->rows;
15042   if ((display_image->columns != width) || (display_image->rows != height))
15043     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15044       display_image->filename);
15045   status=XMakeImage(display,resource_info,&windows->image,display_image,
15046     width,height,exception);
15047   if (status == MagickFalse)
15048     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15049       display_image->filename);
15050   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15051     windows->magnify.width,windows->magnify.height,exception);
15052   if (status == MagickFalse)
15053     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15054       display_image->filename);
15055   if (windows->magnify.mapped != MagickFalse )
15056     (void) XMapRaised(display,windows->magnify.id);
15057   if (windows->pan.mapped != MagickFalse )
15058     (void) XMapRaised(display,windows->pan.id);
15059   windows->image.window_changes.width=(int) display_image->columns;
15060   windows->image.window_changes.height=(int) display_image->rows;
15061   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15062   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15063   (void) XSync(display,MagickFalse);
15064   /*
15065     Respond to events.
15066   */
15067   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15068   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15069   update_time=0;
15070   if (resource_info->update != MagickFalse )
15071     {
15072       MagickBooleanType
15073         status;
15074 
15075       /*
15076         Determine when file data was last modified.
15077       */
15078       status=GetPathAttributes(display_image->filename,&attributes);
15079       if (status != MagickFalse )
15080         update_time=attributes.st_mtime;
15081     }
15082   *state&=(~FormerImageState);
15083   *state&=(~MontageImageState);
15084   *state&=(~NextImageState);
15085   do
15086   {
15087     /*
15088       Handle a window event.
15089     */
15090     if (windows->image.mapped != MagickFalse )
15091       if ((display_image->delay != 0) || (resource_info->update != 0))
15092         {
15093           if (timer < time((time_t *) NULL))
15094             {
15095               if (resource_info->update == MagickFalse)
15096                 *state|=NextImageState | ExitState;
15097               else
15098                 {
15099                   MagickBooleanType
15100                     status;
15101 
15102                   /*
15103                     Determine if image file was modified.
15104                   */
15105                   status=GetPathAttributes(display_image->filename,&attributes);
15106                   if (status != MagickFalse )
15107                     if (update_time != attributes.st_mtime)
15108                       {
15109                         /*
15110                           Redisplay image.
15111                         */
15112                         (void) FormatLocaleString(
15113                           resource_info->image_info->filename,MagickPathExtent,
15114                           "%s:%s",display_image->magick,
15115                           display_image->filename);
15116                         nexus=ReadImage(resource_info->image_info,exception);
15117                         if (nexus != (Image *) NULL)
15118                           *state|=NextImageState | ExitState;
15119                       }
15120                   delay=display_image->delay/MagickMax(
15121                     display_image->ticks_per_second,1L);
15122                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15123                 }
15124             }
15125           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15126             {
15127               /*
15128                 Do not block if delay > 0.
15129               */
15130               XDelay(display,SuspendTime << 2);
15131               continue;
15132             }
15133         }
15134     timestamp=time((time_t *) NULL);
15135     (void) XNextEvent(display,&event);
15136     if ((windows->image.stasis == MagickFalse) ||
15137         (windows->magnify.stasis == MagickFalse))
15138       {
15139         if ((time((time_t *) NULL)-timestamp) > 0)
15140           {
15141             windows->image.stasis=MagickTrue;
15142             windows->magnify.stasis=MagickTrue;
15143           }
15144       }
15145     if (event.xany.window == windows->command.id)
15146       {
15147         /*
15148           Select a command from the Command widget.
15149         */
15150         id=XCommandWidget(display,windows,CommandMenu,&event);
15151         if (id < 0)
15152           continue;
15153         (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15154         command_type=CommandMenus[id];
15155         if (id < MagickMenus)
15156           {
15157             /*
15158               Select a command from a pop-up menu.
15159             */
15160             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15161               command);
15162             if (entry < 0)
15163               continue;
15164             (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15165             command_type=Commands[id][entry];
15166           }
15167         if (command_type != NullCommand)
15168           nexus=XMagickCommand(display,resource_info,windows,command_type,
15169             &display_image,exception);
15170         continue;
15171       }
15172     switch (event.type)
15173     {
15174       case ButtonPress:
15175       {
15176         if (display_image->debug != MagickFalse )
15177           (void) LogMagickEvent(X11Event,GetMagickModule(),
15178             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15179             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15180         if ((event.xbutton.button == Button3) &&
15181             (event.xbutton.state & Mod1Mask))
15182           {
15183             /*
15184               Convert Alt-Button3 to Button2.
15185             */
15186             event.xbutton.button=Button2;
15187             event.xbutton.state&=(~Mod1Mask);
15188           }
15189         if (event.xbutton.window == windows->backdrop.id)
15190           {
15191             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15192               event.xbutton.time);
15193             break;
15194           }
15195         if (event.xbutton.window == windows->image.id)
15196           {
15197             switch (event.xbutton.button)
15198             {
15199               case Button1:
15200               {
15201                 if (resource_info->immutable)
15202                   {
15203                     /*
15204                       Select a command from the Virtual menu.
15205                     */
15206                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15207                       command);
15208                     if (entry >= 0)
15209                       nexus=XMagickCommand(display,resource_info,windows,
15210                         VirtualCommands[entry],&display_image,exception);
15211                     break;
15212                   }
15213                 /*
15214                   Map/unmap Command widget.
15215                 */
15216                 if (windows->command.mapped != MagickFalse )
15217                   (void) XWithdrawWindow(display,windows->command.id,
15218                     windows->command.screen);
15219                 else
15220                   {
15221                     (void) XCommandWidget(display,windows,CommandMenu,
15222                       (XEvent *) NULL);
15223                     (void) XMapRaised(display,windows->command.id);
15224                   }
15225                 break;
15226               }
15227               case Button2:
15228               {
15229                 /*
15230                   User pressed the image magnify button.
15231                 */
15232                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15233                   &display_image,exception);
15234                 XMagnifyImage(display,windows,&event,exception);
15235                 break;
15236               }
15237               case Button3:
15238               {
15239                 if (resource_info->immutable)
15240                   {
15241                     /*
15242                       Select a command from the Virtual menu.
15243                     */
15244                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15245                       command);
15246                     if (entry >= 0)
15247                       nexus=XMagickCommand(display,resource_info,windows,
15248                         VirtualCommands[entry],&display_image,exception);
15249                     break;
15250                   }
15251                 if (display_image->montage != (char *) NULL)
15252                   {
15253                     /*
15254                       Open or delete a tile from a visual image directory.
15255                     */
15256                     nexus=XTileImage(display,resource_info,windows,
15257                       display_image,&event,exception);
15258                     if (nexus != (Image *) NULL)
15259                       *state|=MontageImageState | NextImageState | ExitState;
15260                     vid_info.x=(short int) windows->image.x;
15261                     vid_info.y=(short int) windows->image.y;
15262                     break;
15263                   }
15264                 /*
15265                   Select a command from the Short Cuts menu.
15266                 */
15267                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15268                   command);
15269                 if (entry >= 0)
15270                   nexus=XMagickCommand(display,resource_info,windows,
15271                     ShortCutsCommands[entry],&display_image,exception);
15272                 break;
15273               }
15274               case Button4:
15275               {
15276                 /*
15277                   Wheel up.
15278                 */
15279                 XTranslateImage(display,windows,*image,XK_Up);
15280                 break;
15281               }
15282               case Button5:
15283               {
15284                 /*
15285                   Wheel down.
15286                 */
15287                 XTranslateImage(display,windows,*image,XK_Down);
15288                 break;
15289               }
15290               default:
15291                 break;
15292             }
15293             break;
15294           }
15295         if (event.xbutton.window == windows->magnify.id)
15296           {
15297             int
15298               factor;
15299 
15300             static const char
15301               *MagnifyMenu[] =
15302               {
15303                 "2",
15304                 "4",
15305                 "5",
15306                 "6",
15307                 "7",
15308                 "8",
15309                 "9",
15310                 "3",
15311                 (char *) NULL,
15312               };
15313 
15314             static KeySym
15315               MagnifyCommands[] =
15316               {
15317                 XK_2,
15318                 XK_4,
15319                 XK_5,
15320                 XK_6,
15321                 XK_7,
15322                 XK_8,
15323                 XK_9,
15324                 XK_3
15325               };
15326 
15327             /*
15328               Select a magnify factor from the pop-up menu.
15329             */
15330             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15331             if (factor >= 0)
15332               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15333                 exception);
15334             break;
15335           }
15336         if (event.xbutton.window == windows->pan.id)
15337           {
15338             switch (event.xbutton.button)
15339             {
15340               case Button4:
15341               {
15342                 /*
15343                   Wheel up.
15344                 */
15345                 XTranslateImage(display,windows,*image,XK_Up);
15346                 break;
15347               }
15348               case Button5:
15349               {
15350                 /*
15351                   Wheel down.
15352                 */
15353                 XTranslateImage(display,windows,*image,XK_Down);
15354                 break;
15355               }
15356               default:
15357               {
15358                 XPanImage(display,windows,&event,exception);
15359                 break;
15360               }
15361             }
15362             break;
15363           }
15364         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15365           1L);
15366         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15367         break;
15368       }
15369       case ButtonRelease:
15370       {
15371         if (display_image->debug != MagickFalse )
15372           (void) LogMagickEvent(X11Event,GetMagickModule(),
15373             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15374             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15375         break;
15376       }
15377       case ClientMessage:
15378       {
15379         if (display_image->debug != MagickFalse )
15380           (void) LogMagickEvent(X11Event,GetMagickModule(),
15381             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15382             event.xclient.message_type,event.xclient.format,(unsigned long)
15383             event.xclient.data.l[0]);
15384         if (event.xclient.message_type == windows->im_protocols)
15385           {
15386             if (*event.xclient.data.l == (long) windows->im_update_widget)
15387               {
15388                 (void) CloneString(&windows->command.name,MagickTitle);
15389                 windows->command.data=MagickMenus;
15390                 (void) XCommandWidget(display,windows,CommandMenu,
15391                   (XEvent *) NULL);
15392                 break;
15393               }
15394             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15395               {
15396                 /*
15397                   Update graphic context and window colormap.
15398                 */
15399                 for (i=0; i < (int) number_windows; i++)
15400                 {
15401                   if (magick_windows[i]->id == windows->icon.id)
15402                     continue;
15403                   context_values.background=pixel->background_color.pixel;
15404                   context_values.foreground=pixel->foreground_color.pixel;
15405                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15406                     context_mask,&context_values);
15407                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15408                     context_mask,&context_values);
15409                   context_values.background=pixel->foreground_color.pixel;
15410                   context_values.foreground=pixel->background_color.pixel;
15411                   context_values.plane_mask=context_values.background ^
15412                     context_values.foreground;
15413                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15414                     (size_t) (context_mask | GCPlaneMask),
15415                     &context_values);
15416                   magick_windows[i]->attributes.background_pixel=
15417                     pixel->background_color.pixel;
15418                   magick_windows[i]->attributes.border_pixel=
15419                     pixel->border_color.pixel;
15420                   magick_windows[i]->attributes.colormap=map_info->colormap;
15421                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15422                     (unsigned long) magick_windows[i]->mask,
15423                     &magick_windows[i]->attributes);
15424                 }
15425                 if (windows->pan.mapped != MagickFalse )
15426                   {
15427                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15428                       windows->pan.pixmap);
15429                     (void) XClearWindow(display,windows->pan.id);
15430                     XDrawPanRectangle(display,windows);
15431                   }
15432                 if (windows->backdrop.id != (Window) NULL)
15433                   (void) XInstallColormap(display,map_info->colormap);
15434                 break;
15435               }
15436             if (*event.xclient.data.l == (long) windows->im_former_image)
15437               {
15438                 *state|=FormerImageState | ExitState;
15439                 break;
15440               }
15441             if (*event.xclient.data.l == (long) windows->im_next_image)
15442               {
15443                 *state|=NextImageState | ExitState;
15444                 break;
15445               }
15446             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15447               {
15448                 *state|=RetainColorsState;
15449                 break;
15450               }
15451             if (*event.xclient.data.l == (long) windows->im_exit)
15452               {
15453                 *state|=ExitState;
15454                 break;
15455               }
15456             break;
15457           }
15458         if (event.xclient.message_type == windows->dnd_protocols)
15459           {
15460             Atom
15461               selection,
15462               type;
15463 
15464             int
15465               format,
15466               status;
15467 
15468             unsigned char
15469               *data;
15470 
15471             unsigned long
15472               after,
15473               length;
15474 
15475             /*
15476               Display image named by the Drag-and-Drop selection.
15477             */
15478             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15479               break;
15480             selection=XInternAtom(display,"DndSelection",MagickFalse);
15481             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15482               MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15483               &length,&after,&data);
15484             if ((status != Success) || (length == 0))
15485               break;
15486             if (*event.xclient.data.l == 2)
15487               {
15488                 /*
15489                   Offix DND.
15490                 */
15491                 (void) CopyMagickString(resource_info->image_info->filename,
15492                   (char *) data,MagickPathExtent);
15493               }
15494             else
15495               {
15496                 /*
15497                   XDND.
15498                 */
15499                 if (strncmp((char *) data, "file:", 5) != 0)
15500                   {
15501                     (void) XFree((void *) data);
15502                     break;
15503                   }
15504                 (void) CopyMagickString(resource_info->image_info->filename,
15505                   ((char *) data)+5,MagickPathExtent);
15506               }
15507             nexus=ReadImage(resource_info->image_info,exception);
15508             CatchException(exception);
15509             if (nexus != (Image *) NULL)
15510               *state|=NextImageState | ExitState;
15511             (void) XFree((void *) data);
15512             break;
15513           }
15514         /*
15515           If client window delete message, exit.
15516         */
15517         if (event.xclient.message_type != windows->wm_protocols)
15518           break;
15519         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15520           break;
15521         (void) XWithdrawWindow(display,event.xclient.window,
15522           visual_info->screen);
15523         if (event.xclient.window == windows->image.id)
15524           {
15525             *state|=ExitState;
15526             break;
15527           }
15528         if (event.xclient.window == windows->pan.id)
15529           {
15530             /*
15531               Restore original image size when pan window is deleted.
15532             */
15533             windows->image.window_changes.width=windows->image.ximage->width;
15534             windows->image.window_changes.height=windows->image.ximage->height;
15535             (void) XConfigureImage(display,resource_info,windows,
15536               display_image,exception);
15537           }
15538         break;
15539       }
15540       case ConfigureNotify:
15541       {
15542         if (display_image->debug != MagickFalse )
15543           (void) LogMagickEvent(X11Event,GetMagickModule(),
15544             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15545             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15546             event.xconfigure.y,event.xconfigure.send_event);
15547         if (event.xconfigure.window == windows->image.id)
15548           {
15549             /*
15550               Image window has a new configuration.
15551             */
15552             if (event.xconfigure.send_event != 0)
15553               {
15554                 XWindowChanges
15555                   window_changes;
15556 
15557                 /*
15558                   Position the transient windows relative of the Image window.
15559                 */
15560                 if (windows->command.geometry == (char *) NULL)
15561                   if (windows->command.mapped == MagickFalse)
15562                     {
15563                       windows->command.x=event.xconfigure.x-
15564                         windows->command.width-25;
15565                       windows->command.y=event.xconfigure.y;
15566                       XConstrainWindowPosition(display,&windows->command);
15567                       window_changes.x=windows->command.x;
15568                       window_changes.y=windows->command.y;
15569                       (void) XReconfigureWMWindow(display,windows->command.id,
15570                         windows->command.screen,(unsigned int) (CWX | CWY),
15571                         &window_changes);
15572                     }
15573                 if (windows->widget.geometry == (char *) NULL)
15574                   if (windows->widget.mapped == MagickFalse)
15575                     {
15576                       windows->widget.x=event.xconfigure.x+
15577                         event.xconfigure.width/10;
15578                       windows->widget.y=event.xconfigure.y+
15579                         event.xconfigure.height/10;
15580                       XConstrainWindowPosition(display,&windows->widget);
15581                       window_changes.x=windows->widget.x;
15582                       window_changes.y=windows->widget.y;
15583                       (void) XReconfigureWMWindow(display,windows->widget.id,
15584                         windows->widget.screen,(unsigned int) (CWX | CWY),
15585                         &window_changes);
15586                     }
15587                 if (windows->magnify.geometry == (char *) NULL)
15588                   if (windows->magnify.mapped == MagickFalse)
15589                     {
15590                       windows->magnify.x=event.xconfigure.x+
15591                         event.xconfigure.width+25;
15592                       windows->magnify.y=event.xconfigure.y;
15593                       XConstrainWindowPosition(display,&windows->magnify);
15594                       window_changes.x=windows->magnify.x;
15595                       window_changes.y=windows->magnify.y;
15596                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15597                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15598                         &window_changes);
15599                     }
15600                 if (windows->pan.geometry == (char *) NULL)
15601                   if (windows->pan.mapped == MagickFalse)
15602                     {
15603                       windows->pan.x=event.xconfigure.x+
15604                         event.xconfigure.width+25;
15605                       windows->pan.y=event.xconfigure.y+
15606                         windows->magnify.height+50;
15607                       XConstrainWindowPosition(display,&windows->pan);
15608                       window_changes.x=windows->pan.x;
15609                       window_changes.y=windows->pan.y;
15610                       (void) XReconfigureWMWindow(display,windows->pan.id,
15611                         windows->pan.screen,(unsigned int) (CWX | CWY),
15612                         &window_changes);
15613                     }
15614               }
15615             if ((event.xconfigure.width == (int) windows->image.width) &&
15616                 (event.xconfigure.height == (int) windows->image.height))
15617               break;
15618             windows->image.width=(unsigned int) event.xconfigure.width;
15619             windows->image.height=(unsigned int) event.xconfigure.height;
15620             windows->image.x=0;
15621             windows->image.y=0;
15622             if (display_image->montage != (char *) NULL)
15623               {
15624                 windows->image.x=vid_info.x;
15625                 windows->image.y=vid_info.y;
15626               }
15627             if (windows->image.mapped != MagickFalse &&
15628                 windows->image.stasis != MagickFalse )
15629               {
15630                 /*
15631                   Update image window configuration.
15632                 */
15633                 windows->image.window_changes.width=event.xconfigure.width;
15634                 windows->image.window_changes.height=event.xconfigure.height;
15635                 (void) XConfigureImage(display,resource_info,windows,
15636                   display_image,exception);
15637               }
15638             /*
15639               Update pan window configuration.
15640             */
15641             if ((event.xconfigure.width < windows->image.ximage->width) ||
15642                 (event.xconfigure.height < windows->image.ximage->height))
15643               {
15644                 (void) XMapRaised(display,windows->pan.id);
15645                 XDrawPanRectangle(display,windows);
15646               }
15647             else
15648               if (windows->pan.mapped != MagickFalse )
15649                 (void) XWithdrawWindow(display,windows->pan.id,
15650                   windows->pan.screen);
15651             break;
15652           }
15653         if (event.xconfigure.window == windows->magnify.id)
15654           {
15655             unsigned int
15656               magnify;
15657 
15658             /*
15659               Magnify window has a new configuration.
15660             */
15661             windows->magnify.width=(unsigned int) event.xconfigure.width;
15662             windows->magnify.height=(unsigned int) event.xconfigure.height;
15663             if (windows->magnify.mapped == MagickFalse)
15664               break;
15665             magnify=1;
15666             while ((int) magnify <= event.xconfigure.width)
15667               magnify<<=1;
15668             while ((int) magnify <= event.xconfigure.height)
15669               magnify<<=1;
15670             magnify>>=1;
15671             if (((int) magnify != event.xconfigure.width) ||
15672                 ((int) magnify != event.xconfigure.height))
15673               {
15674                 window_changes.width=(int) magnify;
15675                 window_changes.height=(int) magnify;
15676                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15677                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15678                   &window_changes);
15679                 break;
15680               }
15681             if (windows->magnify.mapped != MagickFalse &&
15682                 windows->magnify.stasis != MagickFalse )
15683               {
15684                 status=XMakeImage(display,resource_info,&windows->magnify,
15685                   display_image,windows->magnify.width,windows->magnify.height,
15686                   exception);
15687                 XMakeMagnifyImage(display,windows,exception);
15688               }
15689             break;
15690           }
15691         if (windows->magnify.mapped != MagickFalse &&
15692             (event.xconfigure.window == windows->pan.id))
15693           {
15694             /*
15695               Pan icon window has a new configuration.
15696             */
15697             if (event.xconfigure.send_event != 0)
15698               {
15699                 windows->pan.x=event.xconfigure.x;
15700                 windows->pan.y=event.xconfigure.y;
15701               }
15702             windows->pan.width=(unsigned int) event.xconfigure.width;
15703             windows->pan.height=(unsigned int) event.xconfigure.height;
15704             break;
15705           }
15706         if (event.xconfigure.window == windows->icon.id)
15707           {
15708             /*
15709               Icon window has a new configuration.
15710             */
15711             windows->icon.width=(unsigned int) event.xconfigure.width;
15712             windows->icon.height=(unsigned int) event.xconfigure.height;
15713             break;
15714           }
15715         break;
15716       }
15717       case DestroyNotify:
15718       {
15719         /*
15720           Group leader has exited.
15721         */
15722         if (display_image->debug != MagickFalse )
15723           (void) LogMagickEvent(X11Event,GetMagickModule(),
15724             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15725         if (event.xdestroywindow.window == windows->group_leader.id)
15726           {
15727             *state|=ExitState;
15728             break;
15729           }
15730         break;
15731       }
15732       case EnterNotify:
15733       {
15734         /*
15735           Selectively install colormap.
15736         */
15737         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15738           if (event.xcrossing.mode != NotifyUngrab)
15739             XInstallColormap(display,map_info->colormap);
15740         break;
15741       }
15742       case Expose:
15743       {
15744         if (display_image->debug != MagickFalse )
15745           (void) LogMagickEvent(X11Event,GetMagickModule(),
15746             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15747             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15748             event.xexpose.y);
15749         /*
15750           Refresh windows that are now exposed.
15751         */
15752         if ((event.xexpose.window == windows->image.id) &&
15753             windows->image.mapped != MagickFalse )
15754           {
15755             XRefreshWindow(display,&windows->image,&event);
15756             delay=display_image->delay/MagickMax(
15757               display_image->ticks_per_second,1L);
15758             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15759             break;
15760           }
15761         if ((event.xexpose.window == windows->magnify.id) &&
15762             windows->magnify.mapped != MagickFalse)
15763           {
15764             XMakeMagnifyImage(display,windows,exception);
15765             break;
15766           }
15767         if (event.xexpose.window == windows->pan.id)
15768           {
15769             XDrawPanRectangle(display,windows);
15770             break;
15771           }
15772         if (event.xexpose.window == windows->icon.id)
15773           {
15774             XRefreshWindow(display,&windows->icon,&event);
15775             break;
15776           }
15777         break;
15778       }
15779       case KeyPress:
15780       {
15781         int
15782           length;
15783 
15784         /*
15785           Respond to a user key press.
15786         */
15787         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15788           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15789         *(command+length)='\0';
15790         if (display_image->debug != MagickFalse )
15791           (void) LogMagickEvent(X11Event,GetMagickModule(),
15792             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15793             key_symbol,command);
15794         if (event.xkey.window == windows->image.id)
15795           {
15796             command_type=XImageWindowCommand(display,resource_info,windows,
15797               event.xkey.state,key_symbol,&display_image,exception);
15798             if (command_type != NullCommand)
15799               nexus=XMagickCommand(display,resource_info,windows,command_type,
15800                 &display_image,exception);
15801           }
15802         if (event.xkey.window == windows->magnify.id)
15803           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15804             exception);
15805         if (event.xkey.window == windows->pan.id)
15806           {
15807             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15808               (void) XWithdrawWindow(display,windows->pan.id,
15809                 windows->pan.screen);
15810             else
15811               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15812                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15813                   "Help Viewer - Image Pan",ImagePanHelp);
15814               else
15815                 XTranslateImage(display,windows,*image,key_symbol);
15816           }
15817         delay=display_image->delay/MagickMax(
15818           display_image->ticks_per_second,1L);
15819         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15820         break;
15821       }
15822       case KeyRelease:
15823       {
15824         /*
15825           Respond to a user key release.
15826         */
15827         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15828           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15829         if (display_image->debug != MagickFalse )
15830           (void) LogMagickEvent(X11Event,GetMagickModule(),
15831             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15832         break;
15833       }
15834       case LeaveNotify:
15835       {
15836         /*
15837           Selectively uninstall colormap.
15838         */
15839         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15840           if (event.xcrossing.mode != NotifyUngrab)
15841             XUninstallColormap(display,map_info->colormap);
15842         break;
15843       }
15844       case MapNotify:
15845       {
15846         if (display_image->debug != MagickFalse )
15847           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15848             event.xmap.window);
15849         if (event.xmap.window == windows->backdrop.id)
15850           {
15851             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15852               CurrentTime);
15853             windows->backdrop.mapped=MagickTrue;
15854             break;
15855           }
15856         if (event.xmap.window == windows->image.id)
15857           {
15858             if (windows->backdrop.id != (Window) NULL)
15859               (void) XInstallColormap(display,map_info->colormap);
15860             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15861               {
15862                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15863                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15864               }
15865             if (((int) windows->image.width < windows->image.ximage->width) ||
15866                 ((int) windows->image.height < windows->image.ximage->height))
15867               (void) XMapRaised(display,windows->pan.id);
15868             windows->image.mapped=MagickTrue;
15869             break;
15870           }
15871         if (event.xmap.window == windows->magnify.id)
15872           {
15873             XMakeMagnifyImage(display,windows,exception);
15874             windows->magnify.mapped=MagickTrue;
15875             (void) XWithdrawWindow(display,windows->info.id,
15876               windows->info.screen);
15877             break;
15878           }
15879         if (event.xmap.window == windows->pan.id)
15880           {
15881             XMakePanImage(display,resource_info,windows,display_image,
15882               exception);
15883             windows->pan.mapped=MagickTrue;
15884             break;
15885           }
15886         if (event.xmap.window == windows->info.id)
15887           {
15888             windows->info.mapped=MagickTrue;
15889             break;
15890           }
15891         if (event.xmap.window == windows->icon.id)
15892           {
15893             MagickBooleanType
15894               taint;
15895 
15896             /*
15897               Create an icon image.
15898             */
15899             taint=display_image->taint;
15900             XMakeStandardColormap(display,icon_visual,icon_resources,
15901               display_image,icon_map,icon_pixel,exception);
15902             (void) XMakeImage(display,icon_resources,&windows->icon,
15903               display_image,windows->icon.width,windows->icon.height,
15904               exception);
15905             display_image->taint=taint;
15906             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15907               windows->icon.pixmap);
15908             (void) XClearWindow(display,windows->icon.id);
15909             (void) XWithdrawWindow(display,windows->info.id,
15910               windows->info.screen);
15911             windows->icon.mapped=MagickTrue;
15912             break;
15913           }
15914         if (event.xmap.window == windows->command.id)
15915           {
15916             windows->command.mapped=MagickTrue;
15917             break;
15918           }
15919         if (event.xmap.window == windows->popup.id)
15920           {
15921             windows->popup.mapped=MagickTrue;
15922             break;
15923           }
15924         if (event.xmap.window == windows->widget.id)
15925           {
15926             windows->widget.mapped=MagickTrue;
15927             break;
15928           }
15929         break;
15930       }
15931       case MappingNotify:
15932       {
15933         (void) XRefreshKeyboardMapping(&event.xmapping);
15934         break;
15935       }
15936       case NoExpose:
15937         break;
15938       case PropertyNotify:
15939       {
15940         Atom
15941           type;
15942 
15943         int
15944           format,
15945           status;
15946 
15947         unsigned char
15948           *data;
15949 
15950         unsigned long
15951           after,
15952           length;
15953 
15954         if (display_image->debug != MagickFalse )
15955           (void) LogMagickEvent(X11Event,GetMagickModule(),
15956             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15957             event.xproperty.atom,event.xproperty.state);
15958         if (event.xproperty.atom != windows->im_remote_command)
15959           break;
15960         /*
15961           Display image named by the remote command protocol.
15962         */
15963         status=XGetWindowProperty(display,event.xproperty.window,
15964           event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15965           AnyPropertyType,&type,&format,&length,&after,&data);
15966         if ((status != Success) || (length == 0))
15967           break;
15968         if (LocaleCompare((char *) data,"-quit") == 0)
15969           {
15970             XClientMessage(display,windows->image.id,windows->im_protocols,
15971               windows->im_exit,CurrentTime);
15972             (void) XFree((void *) data);
15973             break;
15974           }
15975         (void) CopyMagickString(resource_info->image_info->filename,
15976           (char *) data,MagickPathExtent);
15977         (void) XFree((void *) data);
15978         nexus=ReadImage(resource_info->image_info,exception);
15979         CatchException(exception);
15980         if (nexus != (Image *) NULL)
15981           *state|=NextImageState | ExitState;
15982         break;
15983       }
15984       case ReparentNotify:
15985       {
15986         if (display_image->debug != MagickFalse )
15987           (void) LogMagickEvent(X11Event,GetMagickModule(),
15988             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15989             event.xreparent.window);
15990         break;
15991       }
15992       case UnmapNotify:
15993       {
15994         if (display_image->debug != MagickFalse )
15995           (void) LogMagickEvent(X11Event,GetMagickModule(),
15996             "Unmap Notify: 0x%lx",event.xunmap.window);
15997         if (event.xunmap.window == windows->backdrop.id)
15998           {
15999             windows->backdrop.mapped=MagickFalse;
16000             break;
16001           }
16002         if (event.xunmap.window == windows->image.id)
16003           {
16004             windows->image.mapped=MagickFalse;
16005             break;
16006           }
16007         if (event.xunmap.window == windows->magnify.id)
16008           {
16009             windows->magnify.mapped=MagickFalse;
16010             break;
16011           }
16012         if (event.xunmap.window == windows->pan.id)
16013           {
16014             windows->pan.mapped=MagickFalse;
16015             break;
16016           }
16017         if (event.xunmap.window == windows->info.id)
16018           {
16019             windows->info.mapped=MagickFalse;
16020             break;
16021           }
16022         if (event.xunmap.window == windows->icon.id)
16023           {
16024             if (map_info->colormap == icon_map->colormap)
16025               XConfigureImageColormap(display,resource_info,windows,
16026                 display_image,exception);
16027             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16028               icon_pixel);
16029             windows->icon.mapped=MagickFalse;
16030             break;
16031           }
16032         if (event.xunmap.window == windows->command.id)
16033           {
16034             windows->command.mapped=MagickFalse;
16035             break;
16036           }
16037         if (event.xunmap.window == windows->popup.id)
16038           {
16039             if (windows->backdrop.id != (Window) NULL)
16040               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16041                 CurrentTime);
16042             windows->popup.mapped=MagickFalse;
16043             break;
16044           }
16045         if (event.xunmap.window == windows->widget.id)
16046           {
16047             if (windows->backdrop.id != (Window) NULL)
16048               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16049                 CurrentTime);
16050             windows->widget.mapped=MagickFalse;
16051             break;
16052           }
16053         break;
16054       }
16055       default:
16056       {
16057         if (display_image->debug != MagickFalse )
16058           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16059             event.type);
16060         break;
16061       }
16062     }
16063   } while (!(*state & ExitState));
16064   if ((*state & ExitState) == 0)
16065     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16066       &display_image,exception);
16067   else
16068     if (resource_info->confirm_edit != MagickFalse )
16069       {
16070         /*
16071           Query user if image has changed.
16072         */
16073         if ((resource_info->immutable == MagickFalse) &&
16074             display_image->taint != MagickFalse)
16075           {
16076             int
16077               status;
16078 
16079             status=XConfirmWidget(display,windows,"Your image changed.",
16080               "Do you want to save it");
16081             if (status == 0)
16082               *state&=(~ExitState);
16083             else
16084               if (status > 0)
16085                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16086                   &display_image,exception);
16087           }
16088       }
16089   if ((windows->visual_info->klass == GrayScale) ||
16090       (windows->visual_info->klass == PseudoColor) ||
16091       (windows->visual_info->klass == DirectColor))
16092     {
16093       /*
16094         Withdraw pan and Magnify window.
16095       */
16096       if (windows->info.mapped != MagickFalse )
16097         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16098       if (windows->magnify.mapped != MagickFalse )
16099         (void) XWithdrawWindow(display,windows->magnify.id,
16100           windows->magnify.screen);
16101       if (windows->command.mapped != MagickFalse )
16102         (void) XWithdrawWindow(display,windows->command.id,
16103           windows->command.screen);
16104     }
16105   if (windows->pan.mapped != MagickFalse )
16106     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16107   if (resource_info->backdrop == MagickFalse)
16108     if (windows->backdrop.mapped)
16109       {
16110         (void) XWithdrawWindow(display,windows->backdrop.id,
16111           windows->backdrop.screen);
16112         (void) XDestroyWindow(display,windows->backdrop.id);
16113         windows->backdrop.id=(Window) NULL;
16114         (void) XWithdrawWindow(display,windows->image.id,
16115           windows->image.screen);
16116         (void) XDestroyWindow(display,windows->image.id);
16117         windows->image.id=(Window) NULL;
16118       }
16119   XSetCursorState(display,windows,MagickTrue);
16120   XCheckRefreshWindows(display,windows);
16121   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16122     *state&=(~ExitState);
16123   if (*state & ExitState)
16124     {
16125       /*
16126         Free Standard Colormap.
16127       */
16128       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16129       if (resource_info->map_type == (char *) NULL)
16130         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16131       /*
16132         Free X resources.
16133       */
16134       if (resource_info->copy_image != (Image *) NULL)
16135         {
16136           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16137           resource_info->copy_image=NewImageList();
16138         }
16139       DestroyXResources();
16140     }
16141   (void) XSync(display,MagickFalse);
16142   /*
16143     Restore our progress monitor and warning handlers.
16144   */
16145   (void) SetErrorHandler(warning_handler);
16146   (void) SetWarningHandler(warning_handler);
16147   /*
16148     Change to home directory.
16149   */
16150   directory=getcwd(working_directory,MagickPathExtent);
16151   (void) directory;
16152   {
16153     int
16154       status;
16155 
16156     if (*resource_info->home_directory == '\0')
16157       (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16158     status=chdir(resource_info->home_directory);
16159     if (status == -1)
16160       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16161         "UnableToOpenFile","%s",resource_info->home_directory);
16162   }
16163   *image=display_image;
16164   return(nexus);
16165 }
16166 #else
16167 
16168 /*
16169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16170 %                                                                             %
16171 %                                                                             %
16172 %                                                                             %
16173 +   D i s p l a y I m a g e s                                                 %
16174 %                                                                             %
16175 %                                                                             %
16176 %                                                                             %
16177 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16178 %
16179 %  DisplayImages() displays an image sequence to any X window screen.  It
16180 %  returns a value other than 0 if successful.  Check the exception member
16181 %  of image to determine the reason for any failure.
16182 %
16183 %  The format of the DisplayImages method is:
16184 %
16185 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16186 %        Image *images,ExceptionInfo *exception)
16187 %
16188 %  A description of each parameter follows:
16189 %
16190 %    o image_info: the image info.
16191 %
16192 %    o image: the image.
16193 %
16194 %    o exception: return any errors or warnings in this structure.
16195 %
16196 */
DisplayImages(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)16197 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16198   Image *image,ExceptionInfo *exception)
16199 {
16200   assert(image_info != (const ImageInfo *) NULL);
16201   assert(image_info->signature == MagickCoreSignature);
16202   assert(image != (Image *) NULL);
16203   assert(image->signature == MagickCoreSignature);
16204   (void) image_info;
16205   if (image->debug != MagickFalse )
16206     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16207   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16208     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16209   return(MagickFalse);
16210 }
16211 
16212 /*
16213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16214 %                                                                             %
16215 %                                                                             %
16216 %                                                                             %
16217 +   R e m o t e D i s p l a y C o m m a n d                                   %
16218 %                                                                             %
16219 %                                                                             %
16220 %                                                                             %
16221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16222 %
16223 %  RemoteDisplayCommand() encourages a remote display program to display the
16224 %  specified image filename.
16225 %
16226 %  The format of the RemoteDisplayCommand method is:
16227 %
16228 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16229 %        const char *window,const char *filename,ExceptionInfo *exception)
16230 %
16231 %  A description of each parameter follows:
16232 %
16233 %    o image_info: the image info.
16234 %
16235 %    o window: Specifies the name or id of an X window.
16236 %
16237 %    o filename: the name of the image filename to display.
16238 %
16239 %    o exception: return any errors or warnings in this structure.
16240 %
16241 */
RemoteDisplayCommand(const ImageInfo * image_info,const char * window,const char * filename,ExceptionInfo * exception)16242 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16243   const char *window,const char *filename,ExceptionInfo *exception)
16244 {
16245   assert(image_info != (const ImageInfo *) NULL);
16246   assert(image_info->signature == MagickCoreSignature);
16247   assert(filename != (char *) NULL);
16248   (void) window;
16249   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16250   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16251     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16252   return(MagickFalse);
16253 }
16254 #endif
16255