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-2021 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 %    https://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 "magick/studio.h"
43 #include "magick/artifact.h"
44 #include "magick/attribute.h"
45 #include "magick/blob.h"
46 #include "magick/cache.h"
47 #include "magick/channel.h"
48 #include "magick/client.h"
49 #include "magick/color.h"
50 #include "magick/colorspace.h"
51 #include "magick/composite.h"
52 #include "magick/constitute.h"
53 #include "magick/decorate.h"
54 #include "magick/delegate.h"
55 #include "magick/display.h"
56 #include "magick/display-private.h"
57 #include "magick/distort.h"
58 #include "magick/draw.h"
59 #include "magick/effect.h"
60 #include "magick/enhance.h"
61 #include "magick/exception.h"
62 #include "magick/exception-private.h"
63 #include "magick/fx.h"
64 #include "magick/geometry.h"
65 #include "magick/image.h"
66 #include "magick/image-private.h"
67 #include "magick/list.h"
68 #include "magick/log.h"
69 #include "magick/magick.h"
70 #include "magick/memory_.h"
71 #include "magick/monitor.h"
72 #include "magick/monitor-private.h"
73 #include "magick/montage.h"
74 #include "magick/nt-base-private.h"
75 #include "magick/option.h"
76 #include "magick/paint.h"
77 #include "magick/pixel.h"
78 #include "magick/pixel-private.h"
79 #include "magick/property.h"
80 #include "magick/quantum.h"
81 #include "magick/resize.h"
82 #include "magick/resource_.h"
83 #include "magick/shear.h"
84 #include "magick/segment.h"
85 #include "magick/statistic.h"
86 #include "magick/string_.h"
87 #include "magick/string-private.h"
88 #include "magick/timer-private.h"
89 #include "magick/transform.h"
90 #include "magick/threshold.h"
91 #include "magick/utility.h"
92 #include "magick/utility-private.h"
93 #include "magick/version.h"
94 #include "magick/visual-effects.h"
95 #include "magick/widget.h"
96 #include "magick/xwindow-private.h"
97 
98 #if defined(MAGICKCORE_X11_DELEGATE)
99 /*
100   Define declarations.
101 */
102 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
103 
104 /*
105   Constant declarations.
106 */
107 static const unsigned char
108   HighlightBitmap[8] =
109   {
110     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
111   },
112   OpaqueBitmap[8] =
113   {
114     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
115   },
116   ShadowBitmap[8] =
117   {
118     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
119   };
120 
121 /*
122   Help widget declarations.
123 */
124 static const char
125   ImageAnnotateHelp[] =
126   {
127     "In annotate mode, the Command widget has these options:\n"
128     "\n"
129     "    Font Name\n"
130     "      fixed\n"
131     "      variable\n"
132     "      5x8\n"
133     "      6x10\n"
134     "      7x13bold\n"
135     "      8x13bold\n"
136     "      9x15bold\n"
137     "      10x20\n"
138     "      12x24\n"
139     "      Browser...\n"
140     "    Font Color\n"
141     "      black\n"
142     "      blue\n"
143     "      cyan\n"
144     "      green\n"
145     "      gray\n"
146     "      red\n"
147     "      magenta\n"
148     "      yellow\n"
149     "      white\n"
150     "      transparent\n"
151     "      Browser...\n"
152     "    Font Color\n"
153     "      black\n"
154     "      blue\n"
155     "      cyan\n"
156     "      green\n"
157     "      gray\n"
158     "      red\n"
159     "      magenta\n"
160     "      yellow\n"
161     "      white\n"
162     "      transparent\n"
163     "      Browser...\n"
164     "    Rotate Text\n"
165     "      -90\n"
166     "      -45\n"
167     "      -30\n"
168     "      0\n"
169     "      30\n"
170     "      45\n"
171     "      90\n"
172     "      180\n"
173     "      Dialog...\n"
174     "    Help\n"
175     "    Dismiss\n"
176     "\n"
177     "Choose a font name from the Font Name sub-menu.  Additional\n"
178     "font names can be specified with the font browser.  You can\n"
179     "change the menu names by setting the X resources font1\n"
180     "through font9.\n"
181     "\n"
182     "Choose a font color from the Font Color sub-menu.\n"
183     "Additional font colors can be specified with the color\n"
184     "browser.  You can change the menu colors by setting the X\n"
185     "resources pen1 through pen9.\n"
186     "\n"
187     "If you select the color browser and press Grab, you can\n"
188     "choose the font color by moving the pointer to the desired\n"
189     "color on the screen and press any button.\n"
190     "\n"
191     "If you choose to rotate the text, choose Rotate Text from the\n"
192     "menu and select an angle.  Typically you will only want to\n"
193     "rotate one line of text at a time.  Depending on the angle you\n"
194     "choose, subsequent lines may end up overwriting each other.\n"
195     "\n"
196     "Choosing a font and its color is optional.  The default font\n"
197     "is fixed and the default color is black.  However, you must\n"
198     "choose a location to begin entering text and press button 1.\n"
199     "An underscore character will appear at the location of the\n"
200     "pointer.  The cursor changes to a pencil to indicate you are\n"
201     "in text mode.  To exit immediately, press Dismiss.\n"
202     "\n"
203     "In text mode, any key presses will display the character at\n"
204     "the location of the underscore and advance the underscore\n"
205     "cursor.  Enter your text and once completed press Apply to\n"
206     "finish your image annotation.  To correct errors press BACK\n"
207     "SPACE.  To delete an entire line of text, press DELETE.  Any\n"
208     "text that exceeds the boundaries of the image window is\n"
209     "automagically continued onto the next line.\n"
210     "\n"
211     "The actual color you request for the font is saved in the\n"
212     "image.  However, the color that appears in your image window\n"
213     "may be different.  For example, on a monochrome screen the\n"
214     "text will appear black or white even if you choose the color\n"
215     "red as the font color.  However, the image saved to a file\n"
216     "with -write is written with red lettering.  To assure the\n"
217     "correct color text in the final image, any PseudoClass image\n"
218     "is promoted to DirectClass (see miff(5)).  To force a\n"
219     "PseudoClass image to remain PseudoClass, use -colors.\n"
220   },
221   ImageChopHelp[] =
222   {
223     "In chop mode, the Command widget has these options:\n"
224     "\n"
225     "    Direction\n"
226     "      horizontal\n"
227     "      vertical\n"
228     "    Help\n"
229     "    Dismiss\n"
230     "\n"
231     "If the you choose the horizontal direction (this the\n"
232     "default), the area of the image between the two horizontal\n"
233     "endpoints of the chop line is removed.  Otherwise, the area\n"
234     "of the image between the two vertical endpoints of the chop\n"
235     "line is removed.\n"
236     "\n"
237     "Select a location within the image window to begin your chop,\n"
238     "press and hold any button.  Next, move the pointer to\n"
239     "another location in the image.  As you move a line will\n"
240     "connect the initial location and the pointer.  When you\n"
241     "release the button, the area within the image to chop is\n"
242     "determined by which direction you choose from the Command\n"
243     "widget.\n"
244     "\n"
245     "To cancel the image chopping, move the pointer back to the\n"
246     "starting point of the line and release the button.\n"
247   },
248   ImageColorEditHelp[] =
249   {
250     "In color edit mode, the Command widget has these options:\n"
251     "\n"
252     "    Method\n"
253     "      point\n"
254     "      replace\n"
255     "      floodfill\n"
256     "      filltoborder\n"
257     "      reset\n"
258     "    Pixel Color\n"
259     "      black\n"
260     "      blue\n"
261     "      cyan\n"
262     "      green\n"
263     "      gray\n"
264     "      red\n"
265     "      magenta\n"
266     "      yellow\n"
267     "      white\n"
268     "      Browser...\n"
269     "    Border Color\n"
270     "      black\n"
271     "      blue\n"
272     "      cyan\n"
273     "      green\n"
274     "      gray\n"
275     "      red\n"
276     "      magenta\n"
277     "      yellow\n"
278     "      white\n"
279     "      Browser...\n"
280     "    Fuzz\n"
281     "      0%\n"
282     "      2%\n"
283     "      5%\n"
284     "      10%\n"
285     "      15%\n"
286     "      Dialog...\n"
287     "    Undo\n"
288     "    Help\n"
289     "    Dismiss\n"
290     "\n"
291     "Choose a color editing method from the Method sub-menu\n"
292     "of the Command widget.  The point method recolors any pixel\n"
293     "selected with the pointer until the button is released.  The\n"
294     "replace method recolors any pixel that matches the color of\n"
295     "the pixel you select with a button press.  Floodfill recolors\n"
296     "any pixel that matches the color of the pixel you select with\n"
297     "a button press and is a neighbor.  Whereas filltoborder recolors\n"
298     "any neighbor pixel that is not the border color.  Finally reset\n"
299     "changes the entire image to the designated color.\n"
300     "\n"
301     "Next, choose a pixel color from the Pixel Color sub-menu.\n"
302     "Additional pixel colors can be specified with the color\n"
303     "browser.  You can change the menu colors by setting the X\n"
304     "resources pen1 through pen9.\n"
305     "\n"
306     "Now press button 1 to select a pixel within the image window\n"
307     "to change its color.  Additional pixels may be recolored as\n"
308     "prescribed by the method you choose.\n"
309     "\n"
310     "If the Magnify widget is mapped, it can be helpful in positioning\n"
311     "your pointer within the image (refer to button 2).\n"
312     "\n"
313     "The actual color you request for the pixels is saved in the\n"
314     "image.  However, the color that appears in your image window\n"
315     "may be different.  For example, on a monochrome screen the\n"
316     "pixel will appear black or white even if you choose the\n"
317     "color red as the pixel color.  However, the image saved to a\n"
318     "file with -write is written with red pixels.  To assure the\n"
319     "correct color text in the final image, any PseudoClass image\n"
320     "is promoted to DirectClass (see miff(5)).  To force a\n"
321     "PseudoClass image to remain PseudoClass, use -colors.\n"
322   },
323   ImageCompositeHelp[] =
324   {
325     "First a widget window is displayed requesting you to enter an\n"
326     "image name. Press Composite, Grab or type a file name.\n"
327     "Press Cancel if you choose not to create a composite image.\n"
328     "When you choose Grab, move the pointer to the desired window\n"
329     "and press any button.\n"
330     "\n"
331     "If the Composite image does not have any matte information,\n"
332     "you are informed and the file browser is displayed again.\n"
333     "Enter the name of a mask image.  The image is typically\n"
334     "grayscale and the same size as the composite image.  If the\n"
335     "image is not grayscale, it is converted to grayscale and the\n"
336     "resulting intensities are used as matte information.\n"
337     "\n"
338     "A small window appears showing the location of the cursor in\n"
339     "the image window. You are now in composite mode.  To exit\n"
340     "immediately, press Dismiss.  In composite mode, the Command\n"
341     "widget has these options:\n"
342     "\n"
343     "    Operators\n"
344     "      Over\n"
345     "      In\n"
346     "      Out\n"
347     "      Atop\n"
348     "      Xor\n"
349     "      Plus\n"
350     "      Minus\n"
351     "      Add\n"
352     "      Subtract\n"
353     "      Difference\n"
354     "      Multiply\n"
355     "      Bumpmap\n"
356     "      Copy\n"
357     "      CopyRed\n"
358     "      CopyGreen\n"
359     "      CopyBlue\n"
360     "      CopyOpacity\n"
361     "      Clear\n"
362     "    Dissolve\n"
363     "    Displace\n"
364     "    Help\n"
365     "    Dismiss\n"
366     "\n"
367     "Choose a composite operation from the Operators sub-menu of\n"
368     "the Command widget.  How each operator behaves is described\n"
369     "below.  Image window is the image currently displayed on\n"
370     "your X server and image is the image obtained with the File\n"
371     "Browser widget.\n"
372     "\n"
373     "Over     The result is the union of the two image shapes,\n"
374     "         with image obscuring image window in the region of\n"
375     "         overlap.\n"
376     "\n"
377     "In       The result is simply image cut by the shape of\n"
378     "         image window.  None of the image data of image\n"
379     "         window is in the result.\n"
380     "\n"
381     "Out      The resulting image is image with the shape of\n"
382     "         image window cut out.\n"
383     "\n"
384     "Atop     The result is the same shape as image image window,\n"
385     "         with image obscuring image window where the image\n"
386     "         shapes overlap.  Note this differs from over\n"
387     "         because the portion of image outside image window's\n"
388     "         shape does not appear in the result.\n"
389     "\n"
390     "Xor      The result is the image data from both image and\n"
391     "         image window that is outside the overlap region.\n"
392     "         The overlap region is blank.\n"
393     "\n"
394     "Plus     The result is just the sum of the image data.\n"
395     "         Output values are cropped to QuantumRange (no overflow).\n"
396     "\n"
397     "Minus    The result of image - image window, with underflow\n"
398     "         cropped to zero.\n"
399     "\n"
400     "Add      The result of image + image window, with overflow\n"
401     "         wrapping around (mod 256).\n"
402     "\n"
403     "Subtract The result of image - image window, with underflow\n"
404     "         wrapping around (mod 256).  The add and subtract\n"
405     "         operators can be used to perform reversible\n"
406     "         transformations.\n"
407     "\n"
408     "Difference\n"
409     "         The result of abs(image - image window).  This\n"
410     "         useful for comparing two very similar images.\n"
411     "\n"
412     "Multiply\n"
413     "         The result of image * image window.  This\n"
414     "         useful for the creation of drop-shadows.\n"
415     "\n"
416     "Bumpmap  The result of surface normals from image * image\n"
417     "         window.\n"
418     "\n"
419     "Copy     The resulting image is image window replaced with\n"
420     "         image.  Here the matte information is ignored.\n"
421     "\n"
422     "CopyRed  The red layer of the image window is replace with\n"
423     "         the red layer of the image.  The other layers are\n"
424     "         untouched.\n"
425     "\n"
426     "CopyGreen\n"
427     "         The green layer of the image window is replace with\n"
428     "         the green layer of the image.  The other layers are\n"
429     "         untouched.\n"
430     "\n"
431     "CopyBlue The blue layer of the image window is replace with\n"
432     "         the blue layer of the image.  The other layers are\n"
433     "         untouched.\n"
434     "\n"
435     "CopyOpacity\n"
436     "         The matte layer of the image window is replace with\n"
437     "         the matte layer of the image.  The other layers are\n"
438     "         untouched.\n"
439     "\n"
440     "The image compositor requires a matte, or alpha channel in\n"
441     "the image for some operations.  This extra channel usually\n"
442     "defines a mask which represents a sort of a cookie-cutter\n"
443     "for the image.  This the case when matte is opaque (full\n"
444     "coverage) for pixels inside the shape, zero outside, and\n"
445     "between 0 and QuantumRange on the boundary.  If image does not\n"
446     "have a matte channel, it is initialized with 0 for any pixel\n"
447     "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
448     "\n"
449     "If you choose Dissolve, the composite operator becomes Over.  The\n"
450     "image matte channel percent transparency is initialized to factor.\n"
451     "The image window is initialized to (100-factor). Where factor is the\n"
452     "value you specify in the Dialog widget.\n"
453     "\n"
454     "Displace shifts the image pixels as defined by a displacement\n"
455     "map.  With this option, image is used as a displacement map.\n"
456     "Black, within the displacement map, is a maximum positive\n"
457     "displacement.  White is a maximum negative displacement and\n"
458     "middle gray is neutral.  The displacement is scaled to determine\n"
459     "the pixel shift.  By default, the displacement applies in both the\n"
460     "horizontal and vertical directions.  However, if you specify a mask,\n"
461     "image is the horizontal X displacement and mask the vertical Y\n"
462     "displacement.\n"
463     "\n"
464     "Note that matte information for image window is not retained\n"
465     "for colormapped X server visuals (e.g. StaticColor,\n"
466     "StaticColor, GrayScale, PseudoColor).  Correct compositing\n"
467     "behavior may require a TrueColor or DirectColor visual or a\n"
468     "Standard Colormap.\n"
469     "\n"
470     "Choosing a composite operator is optional.  The default\n"
471     "operator is replace.  However, you must choose a location to\n"
472     "composite your image and press button 1.  Press and hold the\n"
473     "button before releasing and an outline of the image will\n"
474     "appear to help you identify your location.\n"
475     "\n"
476     "The actual colors of the composite image is saved.  However,\n"
477     "the color that appears in image window may be different.\n"
478     "For example, on a monochrome screen image window will appear\n"
479     "black or white even though your composited image may have\n"
480     "many colors.  If the image is saved to a file it is written\n"
481     "with the correct colors.  To assure the correct colors are\n"
482     "saved in the final image, any PseudoClass image is promoted\n"
483     "to DirectClass (see miff(5)).  To force a PseudoClass image\n"
484     "to remain PseudoClass, use -colors.\n"
485   },
486   ImageCutHelp[] =
487   {
488     "In cut mode, the Command widget has these options:\n"
489     "\n"
490     "    Help\n"
491     "    Dismiss\n"
492     "\n"
493     "To define a cut region, press button 1 and drag.  The\n"
494     "cut region is defined by a highlighted rectangle that\n"
495     "expands or contracts as it follows the pointer.  Once you\n"
496     "are satisfied with the cut region, release the button.\n"
497     "You are now in rectify mode.  In rectify mode, the Command\n"
498     "widget has these options:\n"
499     "\n"
500     "    Cut\n"
501     "    Help\n"
502     "    Dismiss\n"
503     "\n"
504     "You can make adjustments by moving the pointer to one of the\n"
505     "cut rectangle corners, pressing a button, and dragging.\n"
506     "Finally, press Cut to commit your copy region.  To\n"
507     "exit without cutting the image, press Dismiss.\n"
508   },
509   ImageCopyHelp[] =
510   {
511     "In copy mode, the Command widget has these options:\n"
512     "\n"
513     "    Help\n"
514     "    Dismiss\n"
515     "\n"
516     "To define a copy region, press button 1 and drag.  The\n"
517     "copy region is defined by a highlighted rectangle that\n"
518     "expands or contracts as it follows the pointer.  Once you\n"
519     "are satisfied with the copy region, release the button.\n"
520     "You are now in rectify mode.  In rectify mode, the Command\n"
521     "widget has these options:\n"
522     "\n"
523     "    Copy\n"
524     "    Help\n"
525     "    Dismiss\n"
526     "\n"
527     "You can make adjustments by moving the pointer to one of the\n"
528     "copy rectangle corners, pressing a button, and dragging.\n"
529     "Finally, press Copy to commit your copy region.  To\n"
530     "exit without copying the image, press Dismiss.\n"
531   },
532   ImageCropHelp[] =
533   {
534     "In crop mode, the Command widget has these options:\n"
535     "\n"
536     "    Help\n"
537     "    Dismiss\n"
538     "\n"
539     "To define a cropping region, press button 1 and drag.  The\n"
540     "cropping region is defined by a highlighted rectangle that\n"
541     "expands or contracts as it follows the pointer.  Once you\n"
542     "are satisfied with the cropping region, release the button.\n"
543     "You are now in rectify mode.  In rectify mode, the Command\n"
544     "widget has these options:\n"
545     "\n"
546     "    Crop\n"
547     "    Help\n"
548     "    Dismiss\n"
549     "\n"
550     "You can make adjustments by moving the pointer to one of the\n"
551     "cropping rectangle corners, pressing a button, and dragging.\n"
552     "Finally, press Crop to commit your cropping region.  To\n"
553     "exit without cropping the image, press Dismiss.\n"
554   },
555   ImageDrawHelp[] =
556   {
557     "The cursor changes to a crosshair to indicate you are in\n"
558     "draw mode.  To exit immediately, press Dismiss.  In draw mode,\n"
559     "the Command widget has these options:\n"
560     "\n"
561     "    Element\n"
562     "      point\n"
563     "      line\n"
564     "      rectangle\n"
565     "      fill rectangle\n"
566     "      circle\n"
567     "      fill circle\n"
568     "      ellipse\n"
569     "      fill ellipse\n"
570     "      polygon\n"
571     "      fill polygon\n"
572     "    Color\n"
573     "      black\n"
574     "      blue\n"
575     "      cyan\n"
576     "      green\n"
577     "      gray\n"
578     "      red\n"
579     "      magenta\n"
580     "      yellow\n"
581     "      white\n"
582     "      transparent\n"
583     "      Browser...\n"
584     "    Stipple\n"
585     "      Brick\n"
586     "      Diagonal\n"
587     "      Scales\n"
588     "      Vertical\n"
589     "      Wavy\n"
590     "      Translucent\n"
591     "      Opaque\n"
592     "      Open...\n"
593     "    Width\n"
594     "      1\n"
595     "      2\n"
596     "      4\n"
597     "      8\n"
598     "      16\n"
599     "      Dialog...\n"
600     "    Undo\n"
601     "    Help\n"
602     "    Dismiss\n"
603     "\n"
604     "Choose a drawing primitive from the Element sub-menu.\n"
605     "\n"
606     "Choose a color from the Color sub-menu.  Additional\n"
607     "colors can be specified with the color browser.\n"
608     "\n"
609     "If you choose the color browser and press Grab, you can\n"
610     "select the color by moving the pointer to the desired\n"
611     "color on the screen and press any button.  The transparent\n"
612     "color updates the image matte channel and is useful for\n"
613     "image compositing.\n"
614     "\n"
615     "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
616     "Additional stipples can be specified with the file browser.\n"
617     "Stipples obtained from the file browser must be on disk in the\n"
618     "X11 bitmap format.\n"
619     "\n"
620     "Choose a width, if appropriate, from the Width sub-menu.  To\n"
621     "choose a specific width select the Dialog widget.\n"
622     "\n"
623     "Choose a point in the Image window and press button 1 and\n"
624     "hold.  Next, move the pointer to another location in the\n"
625     "image.  As you move, a line connects the initial location and\n"
626     "the pointer.  When you release the button, the image is\n"
627     "updated with the primitive you just drew.  For polygons, the\n"
628     "image is updated when you press and release the button without\n"
629     "moving the pointer.\n"
630     "\n"
631     "To cancel image drawing, move the pointer back to the\n"
632     "starting point of the line and release the button.\n"
633   },
634   DisplayHelp[] =
635   {
636     "BUTTONS\n"
637     "  The effects of each button press is described below.  Three\n"
638     "  buttons are required.  If you have a two button mouse,\n"
639     "  button 1 and 3 are returned.  Press ALT and button 3 to\n"
640     "  simulate button 2.\n"
641     "\n"
642     "  1    Press this button to map or unmap the Command widget.\n"
643     "\n"
644     "  2    Press and drag to define a region of the image to\n"
645     "       magnify.\n"
646     "\n"
647     "  3    Press and drag to choose from a select set of commands.\n"
648     "       This button behaves differently if the image being\n"
649     "       displayed is a visual image directory.  Here, choose a\n"
650     "       particular tile of the directory and press this button and\n"
651     "       drag to select a command from a pop-up menu.  Choose from\n"
652     "       these menu items:\n"
653     "\n"
654     "           Open\n"
655     "           Next\n"
656     "           Former\n"
657     "           Delete\n"
658     "           Update\n"
659     "\n"
660     "       If you choose Open, the image represented by the tile is\n"
661     "       displayed.  To return to the visual image directory, choose\n"
662     "       Next from the Command widget.  Next and Former moves to the\n"
663     "       next or former image respectively.  Choose Delete to delete\n"
664     "       a particular image tile.  Finally, choose Update to\n"
665     "       synchronize all the image tiles with their respective\n"
666     "       images.\n"
667     "\n"
668     "COMMAND WIDGET\n"
669     "  The Command widget lists a number of sub-menus and commands.\n"
670     "  They are\n"
671     "\n"
672     "      File\n"
673     "        Open...\n"
674     "        Next\n"
675     "        Former\n"
676     "        Select...\n"
677     "        Save...\n"
678     "        Print...\n"
679     "        Delete...\n"
680     "        New...\n"
681     "        Visual Directory...\n"
682     "        Quit\n"
683     "      Edit\n"
684     "        Undo\n"
685     "        Redo\n"
686     "        Cut\n"
687     "        Copy\n"
688     "        Paste\n"
689     "      View\n"
690     "        Half Size\n"
691     "        Original Size\n"
692     "        Double Size\n"
693     "        Resize...\n"
694     "        Apply\n"
695     "        Refresh\n"
696     "        Restore\n"
697     "      Transform\n"
698     "        Crop\n"
699     "        Chop\n"
700     "        Flop\n"
701     "        Flip\n"
702     "        Rotate Right\n"
703     "        Rotate Left\n"
704     "        Rotate...\n"
705     "        Shear...\n"
706     "        Roll...\n"
707     "        Trim Edges\n"
708     "      Enhance\n"
709     "        Brightness...\n"
710     "        Saturation...\n"
711     "        Hue...\n"
712     "        Gamma...\n"
713     "        Sharpen...\n"
714     "        Dull\n"
715     "        Contrast Stretch...\n"
716     "        Sigmoidal Contrast...\n"
717     "        Normalize\n"
718     "        Equalize\n"
719     "        Negate\n"
720     "        Grayscale\n"
721     "        Map...\n"
722     "        Quantize...\n"
723     "      Effects\n"
724     "        Despeckle\n"
725     "        Emboss\n"
726     "        Reduce Noise\n"
727     "        Add Noise\n"
728     "        Sharpen...\n"
729     "        Blur...\n"
730     "        Threshold...\n"
731     "        Edge Detect...\n"
732     "        Spread...\n"
733     "        Shade...\n"
734     "        Painting...\n"
735     "        Segment...\n"
736     "      F/X\n"
737     "        Solarize...\n"
738     "        Sepia Tone...\n"
739     "        Swirl...\n"
740     "        Implode...\n"
741     "        Vignette...\n"
742     "        Wave...\n"
743     "        Oil Painting...\n"
744     "        Charcoal Drawing...\n"
745     "      Image Edit\n"
746     "        Annotate...\n"
747     "        Draw...\n"
748     "        Color...\n"
749     "        Matte...\n"
750     "        Composite...\n"
751     "        Add Border...\n"
752     "        Add Frame...\n"
753     "        Comment...\n"
754     "        Launch...\n"
755     "        Region of Interest...\n"
756     "      Miscellany\n"
757     "        Image Info\n"
758     "        Zoom Image\n"
759     "        Show Preview...\n"
760     "        Show Histogram\n"
761     "        Show Matte\n"
762     "        Background...\n"
763     "        Slide Show\n"
764     "        Preferences...\n"
765     "      Help\n"
766     "        Overview\n"
767     "        Browse Documentation\n"
768     "        About Display\n"
769     "\n"
770     "  Menu items with a indented triangle have a sub-menu.  They\n"
771     "  are represented above as the indented items.  To access a\n"
772     "  sub-menu item, move the pointer to the appropriate menu and\n"
773     "  press a button and drag.  When you find the desired sub-menu\n"
774     "  item, release the button and the command is executed.  Move\n"
775     "  the pointer away from the sub-menu if you decide not to\n"
776     "  execute a particular command.\n"
777     "\n"
778     "KEYBOARD ACCELERATORS\n"
779     "  Accelerators are one or two key presses that effect a\n"
780     "  particular command.  The keyboard accelerators that\n"
781     "  display(1) understands is:\n"
782     "\n"
783     "  Ctl+O     Press to open an image from a file.\n"
784     "\n"
785     "  space     Press to display the next image.\n"
786     "\n"
787     "            If the image is a multi-paged document such as a Postscript\n"
788     "            document, you can skip ahead several pages by preceding\n"
789     "            this command with a number.  For example to display the\n"
790     "            third page beyond the current page, press 3<space>.\n"
791     "\n"
792     "  backspace Press to display the former image.\n"
793     "\n"
794     "            If the image is a multi-paged document such as a Postscript\n"
795     "            document, you can skip behind several pages by preceding\n"
796     "            this command with a number.  For example to display the\n"
797     "            third page preceding the current page, press 3<backspace>.\n"
798     "\n"
799     "  Ctl+S     Press to write the image to a file.\n"
800     "\n"
801     "  Ctl+P     Press to print the image to a Postscript printer.\n"
802     "\n"
803     "  Ctl+D     Press to delete an image file.\n"
804     "\n"
805     "  Ctl+N     Press to create a blank canvas.\n"
806     "\n"
807     "  Ctl+Q     Press to discard all images and exit program.\n"
808     "\n"
809     "  Ctl+Z     Press to undo last image transformation.\n"
810     "\n"
811     "  Ctl+R     Press to redo last image transformation.\n"
812     "\n"
813     "  Ctl+X     Press to cut a region of the image.\n"
814     "\n"
815     "  Ctl+C     Press to copy a region of the image.\n"
816     "\n"
817     "  Ctl+V     Press to paste a region to the image.\n"
818     "\n"
819     "  <         Press to half the image size.\n"
820     "\n"
821     "  -         Press to return to the original image size.\n"
822     "\n"
823     "  >         Press to double the image size.\n"
824     "\n"
825     "  %         Press to resize the image to a width and height you\n"
826     "            specify.\n"
827     "\n"
828     "Cmd-A       Press to make any image transformations permanent."
829     "\n"
830     "            By default, any image size transformations are applied\n"
831     "            to the original image to create the image displayed on\n"
832     "            the X server.  However, the transformations are not\n"
833     "            permanent (i.e. the original image does not change\n"
834     "            size only the X image does).  For example, if you\n"
835     "            press > the X image will appear to double in size,\n"
836     "            but the original image will in fact remain the same size.\n"
837     "            To force the original image to double in size, press >\n"
838     "            followed by Cmd-A.\n"
839     "\n"
840     "  @         Press to refresh the image window.\n"
841     "\n"
842     "  C         Press to cut out a rectangular region of the image.\n"
843     "\n"
844     "  [         Press to chop the image.\n"
845     "\n"
846     "  H         Press to flop image in the horizontal direction.\n"
847     "\n"
848     "  V         Press to flip image in the vertical direction.\n"
849     "\n"
850     "  /         Press to rotate the image 90 degrees clockwise.\n"
851     "\n"
852     " \\         Press to rotate the image 90 degrees counter-clockwise.\n"
853     "\n"
854     "  *         Press to rotate the image the number of degrees you\n"
855     "            specify.\n"
856     "\n"
857     "  S         Press to shear the image the number of degrees you\n"
858     "            specify.\n"
859     "\n"
860     "  R         Press to roll the image.\n"
861     "\n"
862     "  T         Press to trim the image edges.\n"
863     "\n"
864     "  Shft-H    Press to vary the image hue.\n"
865     "\n"
866     "  Shft-S    Press to vary the color saturation.\n"
867     "\n"
868     "  Shft-L    Press to vary the color brightness.\n"
869     "\n"
870     "  Shft-G    Press to gamma correct the image.\n"
871     "\n"
872     "  Shft-C    Press to sharpen the image contrast.\n"
873     "\n"
874     "  Shft-Z    Press to dull the image contrast.\n"
875     "\n"
876     "  =         Press to perform histogram equalization on the image.\n"
877     "\n"
878     "  Shft-N    Press to perform histogram normalization on the image.\n"
879     "\n"
880     "  Shft-~    Press to negate the colors of the image.\n"
881     "\n"
882     "  .         Press to convert the image colors to gray.\n"
883     "\n"
884     "  Shft-#    Press to set the maximum number of unique colors in the\n"
885     "            image.\n"
886     "\n"
887     "  F2        Press to reduce the speckles in an image.\n"
888     "\n"
889     "  F3        Press to eliminate peak noise from an image.\n"
890     "\n"
891     "  F4        Press to add noise to an image.\n"
892     "\n"
893     "  F5        Press to sharpen an image.\n"
894     "\n"
895     "  F6        Press to delete an image file.\n"
896     "\n"
897     "  F7        Press to threshold the image.\n"
898     "\n"
899     "  F8        Press to detect edges within an image.\n"
900     "\n"
901     "  F9        Press to emboss an image.\n"
902     "\n"
903     "  F10       Press to displace pixels by a random amount.\n"
904     "\n"
905     "  F11       Press to negate all pixels above the threshold level.\n"
906     "\n"
907     "  F12       Press to shade the image using a distant light source.\n"
908     "\n"
909     "  F13       Press to lighten or darken image edges to create a 3-D effect.\n"
910     "\n"
911     "  F14       Press to segment the image by color.\n"
912     "\n"
913     "  Meta-S    Press to swirl image pixels about the center.\n"
914     "\n"
915     "  Meta-I    Press to implode image pixels about the center.\n"
916     "\n"
917     "  Meta-W    Press to alter an image along a sine wave.\n"
918     "\n"
919     "  Meta-P    Press to simulate an oil painting.\n"
920     "\n"
921     "  Meta-C    Press to simulate a charcoal drawing.\n"
922     "\n"
923     "  Alt-A     Press to annotate the image with text.\n"
924     "\n"
925     "  Alt-D     Press to draw on an image.\n"
926     "\n"
927     "  Alt-P     Press to edit an image pixel color.\n"
928     "\n"
929     "  Alt-M     Press to edit the image matte information.\n"
930     "\n"
931     "  Alt-V     Press to composite the image with another.\n"
932     "\n"
933     "  Alt-B     Press to add a border to the image.\n"
934     "\n"
935     "  Alt-F     Press to add an ornamental border to the image.\n"
936     "\n"
937     "  Alt-Shft-!\n"
938     "            Press to add an image comment.\n"
939     "\n"
940     "  Ctl-A     Press to apply image processing techniques to a region\n"
941     "            of interest.\n"
942     "\n"
943     "  Shft-?    Press to display information about the image.\n"
944     "\n"
945     "  Shft-+    Press to map the zoom image window.\n"
946     "\n"
947     "  Shft-P    Press to preview an image enhancement, effect, or f/x.\n"
948     "\n"
949     "  F1        Press to display helpful information about display(1).\n"
950     "\n"
951     "  Find      Press to browse documentation about ImageMagick.\n"
952     "\n"
953     "  1-9       Press to change the level of magnification.\n"
954     "\n"
955     "  Use the arrow keys to move the image one pixel up, down,\n"
956     "  left, or right within the magnify window.  Be sure to first\n"
957     "  map the magnify window by pressing button 2.\n"
958     "\n"
959     "  Press ALT and one of the arrow keys to trim off one pixel\n"
960     "  from any side of the image.\n"
961   },
962   ImageMatteEditHelp[] =
963   {
964     "Matte information within an image is useful for some\n"
965     "operations such as image compositing (See IMAGE\n"
966     "COMPOSITING).  This extra channel usually defines a mask\n"
967     "which represents a sort of a cookie-cutter for the image.\n"
968     "This the case when matte is opaque (full coverage) for\n"
969     "pixels inside the shape, zero outside, and between 0 and\n"
970     "QuantumRange on the boundary.\n"
971     "\n"
972     "A small window appears showing the location of the cursor in\n"
973     "the image window. You are now in matte edit mode.  To exit\n"
974     "immediately, press Dismiss.  In matte edit mode, the Command\n"
975     "widget has these options:\n"
976     "\n"
977     "    Method\n"
978     "      point\n"
979     "      replace\n"
980     "      floodfill\n"
981     "      filltoborder\n"
982     "      reset\n"
983     "    Border Color\n"
984     "      black\n"
985     "      blue\n"
986     "      cyan\n"
987     "      green\n"
988     "      gray\n"
989     "      red\n"
990     "      magenta\n"
991     "      yellow\n"
992     "      white\n"
993     "      Browser...\n"
994     "    Fuzz\n"
995     "      0%\n"
996     "      2%\n"
997     "      5%\n"
998     "      10%\n"
999     "      15%\n"
1000     "      Dialog...\n"
1001     "    Matte\n"
1002     "      Opaque\n"
1003     "      Transparent\n"
1004     "      Dialog...\n"
1005     "    Undo\n"
1006     "    Help\n"
1007     "    Dismiss\n"
1008     "\n"
1009     "Choose a matte editing method from the Method sub-menu of\n"
1010     "the Command widget.  The point method changes the matte value\n"
1011     "of any pixel selected with the pointer until the button is\n"
1012     "is released.  The replace method changes the matte value of\n"
1013     "any pixel that matches the color of the pixel you select with\n"
1014     "a button press.  Floodfill changes the matte value of any pixel\n"
1015     "that matches the color of the pixel you select with a button\n"
1016     "press and is a neighbor.  Whereas filltoborder changes the matte\n"
1017     "value any neighbor pixel that is not the border color.  Finally\n"
1018     "reset changes the entire image to the designated matte value.\n"
1019     "\n"
1020     "Choose Matte Value and pick Opaque or Transarent.  For other values\n"
1021     "select the Dialog entry.  Here a dialog appears requesting a matte\n"
1022     "value.  The value you select is assigned as the opacity value of the\n"
1023     "selected pixel or pixels.\n"
1024     "\n"
1025     "Now, press any button to select a pixel within the image\n"
1026     "window to change its matte value.\n"
1027     "\n"
1028     "If the Magnify widget is mapped, it can be helpful in positioning\n"
1029     "your pointer within the image (refer to button 2).\n"
1030     "\n"
1031     "Matte information is only valid in a DirectClass image.\n"
1032     "Therefore, any PseudoClass image is promoted to DirectClass\n"
1033     "(see miff(5)).  Note that matte information for PseudoClass\n"
1034     "is not retained for colormapped X server visuals (e.g.\n"
1035     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1036     "immediately save your image to a file (refer to Write).\n"
1037     "Correct matte editing behavior may require a TrueColor or\n"
1038     "DirectColor visual or a Standard Colormap.\n"
1039   },
1040   ImagePanHelp[] =
1041   {
1042     "When an image exceeds the width or height of the X server\n"
1043     "screen, display maps a small panning icon.  The rectangle\n"
1044     "within the panning icon shows the area that is currently\n"
1045     "displayed in the image window.  To pan about the image,\n"
1046     "press any button and drag the pointer within the panning\n"
1047     "icon.  The pan rectangle moves with the pointer and the\n"
1048     "image window is updated to reflect the location of the\n"
1049     "rectangle within the panning icon.  When you have selected\n"
1050     "the area of the image you wish to view, release the button.\n"
1051     "\n"
1052     "Use the arrow keys to pan the image one pixel up, down,\n"
1053     "left, or right within the image window.\n"
1054     "\n"
1055     "The panning icon is withdrawn if the image becomes smaller\n"
1056     "than the dimensions of the X server screen.\n"
1057   },
1058   ImagePasteHelp[] =
1059   {
1060     "A small window appears showing the location of the cursor in\n"
1061     "the image window. You are now in paste mode.  To exit\n"
1062     "immediately, press Dismiss.  In paste mode, the Command\n"
1063     "widget has these options:\n"
1064     "\n"
1065     "    Operators\n"
1066     "      over\n"
1067     "      in\n"
1068     "      out\n"
1069     "      atop\n"
1070     "      xor\n"
1071     "      plus\n"
1072     "      minus\n"
1073     "      add\n"
1074     "      subtract\n"
1075     "      difference\n"
1076     "      replace\n"
1077     "    Help\n"
1078     "    Dismiss\n"
1079     "\n"
1080     "Choose a composite operation from the Operators sub-menu of\n"
1081     "the Command widget.  How each operator behaves is described\n"
1082     "below.  Image window is the image currently displayed on\n"
1083     "your X server and image is the image obtained with the File\n"
1084     "Browser widget.\n"
1085     "\n"
1086     "Over     The result is the union of the two image shapes,\n"
1087     "         with image obscuring image window in the region of\n"
1088     "         overlap.\n"
1089     "\n"
1090     "In       The result is simply image cut by the shape of\n"
1091     "         image window.  None of the image data of image\n"
1092     "         window is in the result.\n"
1093     "\n"
1094     "Out      The resulting image is image with the shape of\n"
1095     "         image window cut out.\n"
1096     "\n"
1097     "Atop     The result is the same shape as image image window,\n"
1098     "         with image obscuring image window where the image\n"
1099     "         shapes overlap.  Note this differs from over\n"
1100     "         because the portion of image outside image window's\n"
1101     "         shape does not appear in the result.\n"
1102     "\n"
1103     "Xor      The result is the image data from both image and\n"
1104     "         image window that is outside the overlap region.\n"
1105     "         The overlap region is blank.\n"
1106     "\n"
1107     "Plus     The result is just the sum of the image data.\n"
1108     "         Output values are cropped to QuantumRange (no overflow).\n"
1109     "         This operation is independent of the matte\n"
1110     "         channels.\n"
1111     "\n"
1112     "Minus    The result of image - image window, with underflow\n"
1113     "         cropped to zero.\n"
1114     "\n"
1115     "Add      The result of image + image window, with overflow\n"
1116     "         wrapping around (mod 256).\n"
1117     "\n"
1118     "Subtract The result of image - image window, with underflow\n"
1119     "         wrapping around (mod 256).  The add and subtract\n"
1120     "         operators can be used to perform reversible\n"
1121     "         transformations.\n"
1122     "\n"
1123     "Difference\n"
1124     "         The result of abs(image - image window).  This\n"
1125     "         useful for comparing two very similar images.\n"
1126     "\n"
1127     "Copy     The resulting image is image window replaced with\n"
1128     "         image.  Here the matte information is ignored.\n"
1129     "\n"
1130     "CopyRed  The red layer of the image window is replace with\n"
1131     "         the red layer of the image.  The other layers are\n"
1132     "         untouched.\n"
1133     "\n"
1134     "CopyGreen\n"
1135     "         The green layer of the image window is replace with\n"
1136     "         the green layer of the image.  The other layers are\n"
1137     "         untouched.\n"
1138     "\n"
1139     "CopyBlue The blue layer of the image window is replace with\n"
1140     "         the blue layer of the image.  The other layers are\n"
1141     "         untouched.\n"
1142     "\n"
1143     "CopyOpacity\n"
1144     "         The matte layer of the image window is replace with\n"
1145     "         the matte layer of the image.  The other layers are\n"
1146     "         untouched.\n"
1147     "\n"
1148     "The image compositor requires a matte, or alpha channel in\n"
1149     "the image for some operations.  This extra channel usually\n"
1150     "defines a mask which represents a sort of a cookie-cutter\n"
1151     "for the image.  This the case when matte is opaque (full\n"
1152     "coverage) for pixels inside the shape, zero outside, and\n"
1153     "between 0 and QuantumRange on the boundary.  If image does not\n"
1154     "have a matte channel, it is initialized with 0 for any pixel\n"
1155     "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1156     "\n"
1157     "Note that matte information for image window is not retained\n"
1158     "for colormapped X server visuals (e.g. StaticColor,\n"
1159     "StaticColor, GrayScale, PseudoColor).  Correct compositing\n"
1160     "behavior may require a TrueColor or DirectColor visual or a\n"
1161     "Standard Colormap.\n"
1162     "\n"
1163     "Choosing a composite operator is optional.  The default\n"
1164     "operator is replace.  However, you must choose a location to\n"
1165     "paste your image and press button 1.  Press and hold the\n"
1166     "button before releasing and an outline of the image will\n"
1167     "appear to help you identify your location.\n"
1168     "\n"
1169     "The actual colors of the pasted image is saved.  However,\n"
1170     "the color that appears in image window may be different.\n"
1171     "For example, on a monochrome screen image window will appear\n"
1172     "black or white even though your pasted image may have\n"
1173     "many colors.  If the image is saved to a file it is written\n"
1174     "with the correct colors.  To assure the correct colors are\n"
1175     "saved in the final image, any PseudoClass image is promoted\n"
1176     "to DirectClass (see miff(5)).  To force a PseudoClass image\n"
1177     "to remain PseudoClass, use -colors.\n"
1178   },
1179   ImageROIHelp[] =
1180   {
1181     "In region of interest mode, the Command widget has these\n"
1182     "options:\n"
1183     "\n"
1184     "    Help\n"
1185     "    Dismiss\n"
1186     "\n"
1187     "To define a region of interest, press button 1 and drag.\n"
1188     "The region of interest is defined by a highlighted rectangle\n"
1189     "that expands or contracts as it follows the pointer.  Once\n"
1190     "you are satisfied with the region of interest, release the\n"
1191     "button.  You are now in apply mode.  In apply mode the\n"
1192     "Command widget has these options:\n"
1193     "\n"
1194     "      File\n"
1195     "        Save...\n"
1196     "        Print...\n"
1197     "      Edit\n"
1198     "        Undo\n"
1199     "        Redo\n"
1200     "      Transform\n"
1201     "        Flop\n"
1202     "        Flip\n"
1203     "        Rotate Right\n"
1204     "        Rotate Left\n"
1205     "      Enhance\n"
1206     "        Hue...\n"
1207     "        Saturation...\n"
1208     "        Brightness...\n"
1209     "        Gamma...\n"
1210     "        Spiff\n"
1211     "        Dull\n"
1212     "        Contrast Stretch\n"
1213     "        Sigmoidal Contrast...\n"
1214     "        Normalize\n"
1215     "        Equalize\n"
1216     "        Negate\n"
1217     "        Grayscale\n"
1218     "        Map...\n"
1219     "        Quantize...\n"
1220     "      Effects\n"
1221     "        Despeckle\n"
1222     "        Emboss\n"
1223     "        Reduce Noise\n"
1224     "        Sharpen...\n"
1225     "        Blur...\n"
1226     "        Threshold...\n"
1227     "        Edge Detect...\n"
1228     "        Spread...\n"
1229     "        Shade...\n"
1230     "        Raise...\n"
1231     "        Segment...\n"
1232     "      F/X\n"
1233     "        Solarize...\n"
1234     "        Sepia Tone...\n"
1235     "        Swirl...\n"
1236     "        Implode...\n"
1237     "        Vignette...\n"
1238     "        Wave...\n"
1239     "        Oil Painting...\n"
1240     "        Charcoal Drawing...\n"
1241     "      Miscellany\n"
1242     "        Image Info\n"
1243     "        Zoom Image\n"
1244     "        Show Preview...\n"
1245     "        Show Histogram\n"
1246     "        Show Matte\n"
1247     "      Help\n"
1248     "      Dismiss\n"
1249     "\n"
1250     "You can make adjustments to the region of interest by moving\n"
1251     "the pointer to one of the rectangle corners, pressing a\n"
1252     "button, and dragging.  Finally, choose an image processing\n"
1253     "technique from the Command widget.  You can choose more than\n"
1254     "one image processing technique to apply to an area.\n"
1255     "Alternatively, you can move the region of interest before\n"
1256     "applying another image processing technique.  To exit, press\n"
1257     "Dismiss.\n"
1258   },
1259   ImageRotateHelp[] =
1260   {
1261     "In rotate mode, the Command widget has these options:\n"
1262     "\n"
1263     "    Pixel Color\n"
1264     "      black\n"
1265     "      blue\n"
1266     "      cyan\n"
1267     "      green\n"
1268     "      gray\n"
1269     "      red\n"
1270     "      magenta\n"
1271     "      yellow\n"
1272     "      white\n"
1273     "      Browser...\n"
1274     "    Direction\n"
1275     "      horizontal\n"
1276     "      vertical\n"
1277     "    Help\n"
1278     "    Dismiss\n"
1279     "\n"
1280     "Choose a background color from the Pixel Color sub-menu.\n"
1281     "Additional background colors can be specified with the color\n"
1282     "browser.  You can change the menu colors by setting the X\n"
1283     "resources pen1 through pen9.\n"
1284     "\n"
1285     "If you choose the color browser and press Grab, you can\n"
1286     "select the background color by moving the pointer to the\n"
1287     "desired color on the screen and press any button.\n"
1288     "\n"
1289     "Choose a point in the image window and press this button and\n"
1290     "hold.  Next, move the pointer to another location in the\n"
1291     "image.  As you move a line connects the initial location and\n"
1292     "the pointer.  When you release the button, the degree of\n"
1293     "image rotation is determined by the slope of the line you\n"
1294     "just drew.  The slope is relative to the direction you\n"
1295     "choose from the Direction sub-menu of the Command widget.\n"
1296     "\n"
1297     "To cancel the image rotation, move the pointer back to the\n"
1298     "starting point of the line and release the button.\n"
1299   };
1300 
1301 /*
1302   Enumeration declarations.
1303 */
1304 typedef enum
1305 {
1306   CopyMode,
1307   CropMode,
1308   CutMode
1309 } ClipboardMode;
1310 
1311 typedef enum
1312 {
1313   OpenCommand,
1314   NextCommand,
1315   FormerCommand,
1316   SelectCommand,
1317   SaveCommand,
1318   PrintCommand,
1319   DeleteCommand,
1320   NewCommand,
1321   VisualDirectoryCommand,
1322   QuitCommand,
1323   UndoCommand,
1324   RedoCommand,
1325   CutCommand,
1326   CopyCommand,
1327   PasteCommand,
1328   HalfSizeCommand,
1329   OriginalSizeCommand,
1330   DoubleSizeCommand,
1331   ResizeCommand,
1332   ApplyCommand,
1333   RefreshCommand,
1334   RestoreCommand,
1335   CropCommand,
1336   ChopCommand,
1337   FlopCommand,
1338   FlipCommand,
1339   RotateRightCommand,
1340   RotateLeftCommand,
1341   RotateCommand,
1342   ShearCommand,
1343   RollCommand,
1344   TrimCommand,
1345   HueCommand,
1346   SaturationCommand,
1347   BrightnessCommand,
1348   GammaCommand,
1349   SpiffCommand,
1350   DullCommand,
1351   ContrastStretchCommand,
1352   SigmoidalContrastCommand,
1353   NormalizeCommand,
1354   EqualizeCommand,
1355   NegateCommand,
1356   GrayscaleCommand,
1357   MapCommand,
1358   QuantizeCommand,
1359   DespeckleCommand,
1360   EmbossCommand,
1361   ReduceNoiseCommand,
1362   AddNoiseCommand,
1363   SharpenCommand,
1364   BlurCommand,
1365   ThresholdCommand,
1366   EdgeDetectCommand,
1367   SpreadCommand,
1368   ShadeCommand,
1369   RaiseCommand,
1370   SegmentCommand,
1371   SolarizeCommand,
1372   SepiaToneCommand,
1373   SwirlCommand,
1374   ImplodeCommand,
1375   VignetteCommand,
1376   WaveCommand,
1377   OilPaintCommand,
1378   CharcoalDrawCommand,
1379   AnnotateCommand,
1380   DrawCommand,
1381   ColorCommand,
1382   MatteCommand,
1383   CompositeCommand,
1384   AddBorderCommand,
1385   AddFrameCommand,
1386   CommentCommand,
1387   LaunchCommand,
1388   RegionofInterestCommand,
1389   ROIHelpCommand,
1390   ROIDismissCommand,
1391   InfoCommand,
1392   ZoomCommand,
1393   ShowPreviewCommand,
1394   ShowHistogramCommand,
1395   ShowMatteCommand,
1396   BackgroundCommand,
1397   SlideShowCommand,
1398   PreferencesCommand,
1399   HelpCommand,
1400   BrowseDocumentationCommand,
1401   VersionCommand,
1402   SaveToUndoBufferCommand,
1403   FreeBuffersCommand,
1404   NullCommand
1405 } CommandType;
1406 
1407 typedef enum
1408 {
1409   AnnotateNameCommand,
1410   AnnotateFontColorCommand,
1411   AnnotateBackgroundColorCommand,
1412   AnnotateRotateCommand,
1413   AnnotateHelpCommand,
1414   AnnotateDismissCommand,
1415   TextHelpCommand,
1416   TextApplyCommand,
1417   ChopDirectionCommand,
1418   ChopHelpCommand,
1419   ChopDismissCommand,
1420   HorizontalChopCommand,
1421   VerticalChopCommand,
1422   ColorEditMethodCommand,
1423   ColorEditColorCommand,
1424   ColorEditBorderCommand,
1425   ColorEditFuzzCommand,
1426   ColorEditUndoCommand,
1427   ColorEditHelpCommand,
1428   ColorEditDismissCommand,
1429   CompositeOperatorsCommand,
1430   CompositeDissolveCommand,
1431   CompositeDisplaceCommand,
1432   CompositeHelpCommand,
1433   CompositeDismissCommand,
1434   CropHelpCommand,
1435   CropDismissCommand,
1436   RectifyCopyCommand,
1437   RectifyHelpCommand,
1438   RectifyDismissCommand,
1439   DrawElementCommand,
1440   DrawColorCommand,
1441   DrawStippleCommand,
1442   DrawWidthCommand,
1443   DrawUndoCommand,
1444   DrawHelpCommand,
1445   DrawDismissCommand,
1446   MatteEditMethod,
1447   MatteEditBorderCommand,
1448   MatteEditFuzzCommand,
1449   MatteEditValueCommand,
1450   MatteEditUndoCommand,
1451   MatteEditHelpCommand,
1452   MatteEditDismissCommand,
1453   PasteOperatorsCommand,
1454   PasteHelpCommand,
1455   PasteDismissCommand,
1456   RotateColorCommand,
1457   RotateDirectionCommand,
1458   RotateCropCommand,
1459   RotateSharpenCommand,
1460   RotateHelpCommand,
1461   RotateDismissCommand,
1462   HorizontalRotateCommand,
1463   VerticalRotateCommand,
1464   TileLoadCommand,
1465   TileNextCommand,
1466   TileFormerCommand,
1467   TileDeleteCommand,
1468   TileUpdateCommand
1469 } ModeType;
1470 
1471 /*
1472   Stipples.
1473 */
1474 #define BricksWidth  20
1475 #define BricksHeight  20
1476 #define DiagonalWidth  16
1477 #define DiagonalHeight  16
1478 #define HighlightWidth  8
1479 #define HighlightHeight  8
1480 #define OpaqueWidth  8
1481 #define OpaqueHeight  8
1482 #define ScalesWidth  16
1483 #define ScalesHeight  16
1484 #define ShadowWidth  8
1485 #define ShadowHeight  8
1486 #define VerticalWidth  16
1487 #define VerticalHeight  16
1488 #define WavyWidth  16
1489 #define WavyHeight  16
1490 
1491 /*
1492   Constant declaration.
1493 */
1494 static const int
1495   RoiDelta = 8;
1496 
1497 static const unsigned char
1498   BricksBitmap[] =
1499   {
1500     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1501     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1502     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1503     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1504     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1505   },
1506   DiagonalBitmap[] =
1507   {
1508     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1509     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1510     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1511   },
1512   ScalesBitmap[] =
1513   {
1514     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1515     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1516     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1517   },
1518   VerticalBitmap[] =
1519   {
1520     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1521     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1522     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1523   },
1524   WavyBitmap[] =
1525   {
1526     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1527     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1528     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1529   };
1530 
1531 /*
1532   Function prototypes.
1533 */
1534 static CommandType
1535   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1536     const MagickStatusType,KeySym,Image **);
1537 
1538 static Image
1539   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1540     Image **),
1541   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1542   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1543   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1544 
1545 static MagickBooleanType
1546   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1547   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1548   XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1549   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1550   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1551   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1552   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1553   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1554   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1555   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1556   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1557   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1558   XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1559   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1560   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1561 
1562 static void
1563   XDrawPanRectangle(Display *,XWindows *),
1564   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
1565   XMagnifyImage(Display *,XWindows *,XEvent *),
1566   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1567   XPanImage(Display *,XWindows *,XEvent *),
1568   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1569     const KeySym),
1570   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1571   XScreenEvent(Display *,XWindows *,XEvent *),
1572   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1573 
1574 /*
1575 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1576 %                                                                             %
1577 %                                                                             %
1578 %                                                                             %
1579 %   D i s p l a y I m a g e s                                                 %
1580 %                                                                             %
1581 %                                                                             %
1582 %                                                                             %
1583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1584 %
1585 %  DisplayImages() displays an image sequence to any X window screen.  It
1586 %  returns a value other than 0 if successful.  Check the exception member
1587 %  of image to determine the reason for any failure.
1588 %
1589 %  The format of the DisplayImages method is:
1590 %
1591 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1592 %        Image *images)
1593 %
1594 %  A description of each parameter follows:
1595 %
1596 %    o image_info: the image info.
1597 %
1598 %    o image: the image.
1599 %
1600 */
DisplayImages(const ImageInfo * image_info,Image * images)1601 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1602   Image *images)
1603 {
1604   char
1605     *argv[1];
1606 
1607   Display
1608     *display;
1609 
1610   Image
1611     *image;
1612 
1613   ssize_t
1614     i;
1615 
1616   size_t
1617     state;
1618 
1619   XrmDatabase
1620     resource_database;
1621 
1622   XResourceInfo
1623     resource_info;
1624 
1625   assert(image_info != (const ImageInfo *) NULL);
1626   assert(image_info->signature == MagickCoreSignature);
1627   assert(images != (Image *) NULL);
1628   assert(images->signature == MagickCoreSignature);
1629   if (images->debug != MagickFalse)
1630     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1631   display=XOpenDisplay(image_info->server_name);
1632   if (display == (Display *) NULL)
1633     {
1634       (void) ThrowMagickException(&images->exception,GetMagickModule(),
1635         XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1636         image_info->server_name));
1637       return(MagickFalse);
1638     }
1639   if (images->exception.severity != UndefinedException)
1640     CatchException(&images->exception);
1641   (void) XSetErrorHandler(XError);
1642   resource_database=XGetResourceDatabase(display,GetClientName());
1643   (void) memset(&resource_info,0,sizeof(resource_info));
1644   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1645   if (image_info->page != (char *) NULL)
1646     resource_info.image_geometry=AcquireString(image_info->page);
1647   resource_info.immutable=MagickTrue;
1648   argv[0]=AcquireString(GetClientName());
1649   state=DefaultState;
1650   for (i=0; (state & ExitState) == 0; i++)
1651   {
1652     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1653       break;
1654     image=GetImageFromList(images,i % GetImageListLength(images));
1655     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1656   }
1657   (void) SetErrorHandler((ErrorHandler) NULL);
1658   (void) SetWarningHandler((WarningHandler) NULL);
1659   argv[0]=DestroyString(argv[0]);
1660   XDestroyResourceInfo(&resource_info);
1661   if (images->exception.severity != UndefinedException)
1662     return(MagickFalse);
1663   return(MagickTrue);
1664 }
1665 
1666 /*
1667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1668 %                                                                             %
1669 %                                                                             %
1670 %                                                                             %
1671 %   R e m o t e D i s p l a y C o m m a n d                                   %
1672 %                                                                             %
1673 %                                                                             %
1674 %                                                                             %
1675 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1676 %
1677 %  RemoteDisplayCommand() encourages a remote display program to display the
1678 %  specified image filename.
1679 %
1680 %  The format of the RemoteDisplayCommand method is:
1681 %
1682 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1683 %        const char *window,const char *filename,ExceptionInfo *exception)
1684 %
1685 %  A description of each parameter follows:
1686 %
1687 %    o image_info: the image info.
1688 %
1689 %    o window: Specifies the name or id of an X window.
1690 %
1691 %    o filename: the name of the image filename to display.
1692 %
1693 %    o exception: return any errors or warnings in this structure.
1694 %
1695 */
RemoteDisplayCommand(const ImageInfo * image_info,const char * window,const char * filename,ExceptionInfo * exception)1696 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1697   const char *window,const char *filename,ExceptionInfo *exception)
1698 {
1699   Display
1700     *display;
1701 
1702   MagickStatusType
1703     status;
1704 
1705   assert(image_info != (const ImageInfo *) NULL);
1706   assert(image_info->signature == MagickCoreSignature);
1707   assert(filename != (char *) NULL);
1708   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1709   display=XOpenDisplay(image_info->server_name);
1710   if (display == (Display *) NULL)
1711     {
1712       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1713         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1714       return(MagickFalse);
1715     }
1716   (void) XSetErrorHandler(XError);
1717   status=XRemoteCommand(display,window,filename);
1718   (void) XCloseDisplay(display);
1719   return(status != 0 ? MagickTrue : MagickFalse);
1720 }
1721 
1722 /*
1723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724 %                                                                             %
1725 %                                                                             %
1726 %                                                                             %
1727 +   X A n n o t a t e E d i t I m a g e                                       %
1728 %                                                                             %
1729 %                                                                             %
1730 %                                                                             %
1731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1732 %
1733 %  XAnnotateEditImage() annotates the image with text.
1734 %
1735 %  The format of the XAnnotateEditImage method is:
1736 %
1737 %      MagickBooleanType XAnnotateEditImage(Display *display,
1738 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
1739 %
1740 %  A description of each parameter follows:
1741 %
1742 %    o display: Specifies a connection to an X server;  returned from
1743 %      XOpenDisplay.
1744 %
1745 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1746 %
1747 %    o windows: Specifies a pointer to a XWindows structure.
1748 %
1749 %    o image: the image; returned from ReadImage.
1750 %
1751 */
1752 
XAnnotateEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image)1753 static MagickBooleanType XAnnotateEditImage(Display *display,
1754   XResourceInfo *resource_info,XWindows *windows,Image *image)
1755 {
1756   const char
1757     *const AnnotateMenu[] =
1758     {
1759       "Font Name",
1760       "Font Color",
1761       "Box Color",
1762       "Rotate Text",
1763       "Help",
1764       "Dismiss",
1765       (char *) NULL
1766     },
1767     *const TextMenu[] =
1768     {
1769       "Help",
1770       "Apply",
1771       (char *) NULL
1772     };
1773 
1774   static const ModeType
1775     AnnotateCommands[] =
1776     {
1777       AnnotateNameCommand,
1778       AnnotateFontColorCommand,
1779       AnnotateBackgroundColorCommand,
1780       AnnotateRotateCommand,
1781       AnnotateHelpCommand,
1782       AnnotateDismissCommand
1783     },
1784     TextCommands[] =
1785     {
1786       TextHelpCommand,
1787       TextApplyCommand
1788     };
1789 
1790   static MagickBooleanType
1791     transparent_box = MagickTrue,
1792     transparent_pen = MagickFalse;
1793 
1794   static MagickRealType
1795     degrees = 0.0;
1796 
1797   static unsigned int
1798     box_id = MaxNumberPens-2,
1799     font_id = 0,
1800     pen_id = 0;
1801 
1802   char
1803     command[MaxTextExtent],
1804     text[MaxTextExtent];
1805 
1806   const char
1807     *ColorMenu[MaxNumberPens+1];
1808 
1809   Cursor
1810     cursor;
1811 
1812   GC
1813     annotate_context;
1814 
1815   int
1816     id,
1817     pen_number,
1818     status,
1819     x,
1820     y;
1821 
1822   KeySym
1823     key_symbol;
1824 
1825   char
1826     *p;
1827 
1828   ssize_t
1829     i;
1830 
1831   unsigned int
1832     height,
1833     width;
1834 
1835   size_t
1836     state;
1837 
1838   XAnnotateInfo
1839     *annotate_info,
1840     *previous_info;
1841 
1842   XColor
1843     color;
1844 
1845   XFontStruct
1846     *font_info;
1847 
1848   XEvent
1849     event,
1850     text_event;
1851 
1852   /*
1853     Map Command widget.
1854   */
1855   (void) CloneString(&windows->command.name,"Annotate");
1856   windows->command.data=4;
1857   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1858   (void) XMapRaised(display,windows->command.id);
1859   XClientMessage(display,windows->image.id,windows->im_protocols,
1860     windows->im_update_widget,CurrentTime);
1861   /*
1862     Track pointer until button 1 is pressed.
1863   */
1864   XQueryPosition(display,windows->image.id,&x,&y);
1865   (void) XSelectInput(display,windows->image.id,
1866     windows->image.attributes.event_mask | PointerMotionMask);
1867   cursor=XCreateFontCursor(display,XC_left_side);
1868   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1869   state=DefaultState;
1870   do
1871   {
1872     if (windows->info.mapped != MagickFalse)
1873       {
1874         /*
1875           Display pointer position.
1876         */
1877         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1878           x+windows->image.x,y+windows->image.y);
1879         XInfoWidget(display,windows,text);
1880       }
1881     /*
1882       Wait for next event.
1883     */
1884     XScreenEvent(display,windows,&event);
1885     if (event.xany.window == windows->command.id)
1886       {
1887         /*
1888           Select a command from the Command widget.
1889         */
1890         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1891         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1892         if (id < 0)
1893           continue;
1894         switch (AnnotateCommands[id])
1895         {
1896           case AnnotateNameCommand:
1897           {
1898             const char
1899               *FontMenu[MaxNumberFonts];
1900 
1901             int
1902               font_number;
1903 
1904             /*
1905               Initialize menu selections.
1906             */
1907             for (i=0; i < MaxNumberFonts; i++)
1908               FontMenu[i]=resource_info->font_name[i];
1909             FontMenu[MaxNumberFonts-2]="Browser...";
1910             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1911             /*
1912               Select a font name from the pop-up menu.
1913             */
1914             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1915               (const char **) FontMenu,command);
1916             if (font_number < 0)
1917               break;
1918             if (font_number == (MaxNumberFonts-2))
1919               {
1920                 static char
1921                   font_name[MaxTextExtent] = "fixed";
1922 
1923                 /*
1924                   Select a font name from a browser.
1925                 */
1926                 resource_info->font_name[font_number]=font_name;
1927                 XFontBrowserWidget(display,windows,"Select",font_name);
1928                 if (*font_name == '\0')
1929                   break;
1930               }
1931             /*
1932               Initialize font info.
1933             */
1934             font_info=XLoadQueryFont(display,resource_info->font_name[
1935               font_number]);
1936             if (font_info == (XFontStruct *) NULL)
1937               {
1938                 XNoticeWidget(display,windows,"Unable to load font:",
1939                   resource_info->font_name[font_number]);
1940                 break;
1941               }
1942             font_id=(unsigned int) font_number;
1943             (void) XFreeFont(display,font_info);
1944             break;
1945           }
1946           case AnnotateFontColorCommand:
1947           {
1948             /*
1949               Initialize menu selections.
1950             */
1951             for (i=0; i < (int) (MaxNumberPens-2); i++)
1952               ColorMenu[i]=resource_info->pen_colors[i];
1953             ColorMenu[MaxNumberPens-2]="transparent";
1954             ColorMenu[MaxNumberPens-1]="Browser...";
1955             ColorMenu[MaxNumberPens]=(const char *) NULL;
1956             /*
1957               Select a pen color from the pop-up menu.
1958             */
1959             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1960               (const char **) ColorMenu,command);
1961             if (pen_number < 0)
1962               break;
1963             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1964               MagickFalse;
1965             if (transparent_pen != MagickFalse)
1966               break;
1967             if (pen_number == (MaxNumberPens-1))
1968               {
1969                 static char
1970                   color_name[MaxTextExtent] = "gray";
1971 
1972                 /*
1973                   Select a pen color from a dialog.
1974                 */
1975                 resource_info->pen_colors[pen_number]=color_name;
1976                 XColorBrowserWidget(display,windows,"Select",color_name);
1977                 if (*color_name == '\0')
1978                   break;
1979               }
1980             /*
1981               Set pen color.
1982             */
1983             (void) XParseColor(display,windows->map_info->colormap,
1984               resource_info->pen_colors[pen_number],&color);
1985             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
1986               (unsigned int) MaxColors,&color);
1987             windows->pixel_info->pen_colors[pen_number]=color;
1988             pen_id=(unsigned int) pen_number;
1989             break;
1990           }
1991           case AnnotateBackgroundColorCommand:
1992           {
1993             /*
1994               Initialize menu selections.
1995             */
1996             for (i=0; i < (int) (MaxNumberPens-2); i++)
1997               ColorMenu[i]=resource_info->pen_colors[i];
1998             ColorMenu[MaxNumberPens-2]="transparent";
1999             ColorMenu[MaxNumberPens-1]="Browser...";
2000             ColorMenu[MaxNumberPens]=(const char *) NULL;
2001             /*
2002               Select a pen color from the pop-up menu.
2003             */
2004             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2005               (const char **) ColorMenu,command);
2006             if (pen_number < 0)
2007               break;
2008             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2009               MagickFalse;
2010             if (transparent_box != MagickFalse)
2011               break;
2012             if (pen_number == (MaxNumberPens-1))
2013               {
2014                 static char
2015                   color_name[MaxTextExtent] = "gray";
2016 
2017                 /*
2018                   Select a pen color from a dialog.
2019                 */
2020                 resource_info->pen_colors[pen_number]=color_name;
2021                 XColorBrowserWidget(display,windows,"Select",color_name);
2022                 if (*color_name == '\0')
2023                   break;
2024               }
2025             /*
2026               Set pen color.
2027             */
2028             (void) XParseColor(display,windows->map_info->colormap,
2029               resource_info->pen_colors[pen_number],&color);
2030             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2031               (unsigned int) MaxColors,&color);
2032             windows->pixel_info->pen_colors[pen_number]=color;
2033             box_id=(unsigned int) pen_number;
2034             break;
2035           }
2036           case AnnotateRotateCommand:
2037           {
2038             int
2039               entry;
2040 
2041             const char
2042               *const RotateMenu[] =
2043               {
2044                 "-90",
2045                 "-45",
2046                 "-30",
2047                 "0",
2048                 "30",
2049                 "45",
2050                 "90",
2051                 "180",
2052                 "Dialog...",
2053                 (char *) NULL,
2054               };
2055 
2056             static char
2057               angle[MaxTextExtent] = "30.0";
2058 
2059             /*
2060               Select a command from the pop-up menu.
2061             */
2062             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2063               command);
2064             if (entry < 0)
2065               break;
2066             if (entry != 8)
2067               {
2068                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2069                 break;
2070               }
2071             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2072               angle);
2073             if (*angle == '\0')
2074               break;
2075             degrees=StringToDouble(angle,(char **) NULL);
2076             break;
2077           }
2078           case AnnotateHelpCommand:
2079           {
2080             XTextViewHelp(display,resource_info,windows,MagickFalse,
2081               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2082             break;
2083           }
2084           case AnnotateDismissCommand:
2085           {
2086             /*
2087               Prematurely exit.
2088             */
2089             state|=EscapeState;
2090             state|=ExitState;
2091             break;
2092           }
2093           default:
2094             break;
2095         }
2096         continue;
2097       }
2098     switch (event.type)
2099     {
2100       case ButtonPress:
2101       {
2102         if (event.xbutton.button != Button1)
2103           break;
2104         if (event.xbutton.window != windows->image.id)
2105           break;
2106         /*
2107           Change to text entering mode.
2108         */
2109         x=event.xbutton.x;
2110         y=event.xbutton.y;
2111         state|=ExitState;
2112         break;
2113       }
2114       case ButtonRelease:
2115         break;
2116       case Expose:
2117         break;
2118       case KeyPress:
2119       {
2120         if (event.xkey.window != windows->image.id)
2121           break;
2122         /*
2123           Respond to a user key press.
2124         */
2125         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2126           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2127         switch ((int) key_symbol)
2128         {
2129           case XK_Escape:
2130           case XK_F20:
2131           {
2132             /*
2133               Prematurely exit.
2134             */
2135             state|=EscapeState;
2136             state|=ExitState;
2137             break;
2138           }
2139           case XK_F1:
2140           case XK_Help:
2141           {
2142             XTextViewHelp(display,resource_info,windows,MagickFalse,
2143               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2144             break;
2145           }
2146           default:
2147           {
2148             (void) XBell(display,0);
2149             break;
2150           }
2151         }
2152         break;
2153       }
2154       case MotionNotify:
2155       {
2156         /*
2157           Map and unmap Info widget as cursor crosses its boundaries.
2158         */
2159         x=event.xmotion.x;
2160         y=event.xmotion.y;
2161         if (windows->info.mapped != MagickFalse)
2162           {
2163             if ((x < (int) (windows->info.x+windows->info.width)) &&
2164                 (y < (int) (windows->info.y+windows->info.height)))
2165               (void) XWithdrawWindow(display,windows->info.id,
2166                 windows->info.screen);
2167           }
2168         else
2169           if ((x > (int) (windows->info.x+windows->info.width)) ||
2170               (y > (int) (windows->info.y+windows->info.height)))
2171             (void) XMapWindow(display,windows->info.id);
2172         break;
2173       }
2174       default:
2175         break;
2176     }
2177   } while ((state & ExitState) == 0);
2178   (void) XSelectInput(display,windows->image.id,
2179     windows->image.attributes.event_mask);
2180   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2181   if ((state & EscapeState) != 0)
2182     return(MagickTrue);
2183   /*
2184     Set font info and check boundary conditions.
2185   */
2186   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2187   if (font_info == (XFontStruct *) NULL)
2188     {
2189       XNoticeWidget(display,windows,"Unable to load font:",
2190         resource_info->font_name[font_id]);
2191       font_info=windows->font_info;
2192     }
2193   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2194     x=(int) windows->image.width-font_info->max_bounds.width;
2195   if (y < (int) (font_info->ascent+font_info->descent))
2196     y=(int) font_info->ascent+font_info->descent;
2197   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2198       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2199     return(MagickFalse);
2200   /*
2201     Initialize annotate structure.
2202   */
2203   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2204   if (annotate_info == (XAnnotateInfo *) NULL)
2205     return(MagickFalse);
2206   XGetAnnotateInfo(annotate_info);
2207   annotate_info->x=x;
2208   annotate_info->y=y;
2209   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2210     annotate_info->stencil=OpaqueStencil;
2211   else
2212     if (transparent_box == MagickFalse)
2213       annotate_info->stencil=BackgroundStencil;
2214     else
2215       annotate_info->stencil=ForegroundStencil;
2216   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2217   annotate_info->degrees=degrees;
2218   annotate_info->font_info=font_info;
2219   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2220     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2221     sizeof(*annotate_info->text));
2222   if (annotate_info->text == (char *) NULL)
2223     return(MagickFalse);
2224   /*
2225     Create cursor and set graphic context.
2226   */
2227   cursor=XCreateFontCursor(display,XC_pencil);
2228   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2229   annotate_context=windows->image.annotate_context;
2230   (void) XSetFont(display,annotate_context,font_info->fid);
2231   (void) XSetBackground(display,annotate_context,
2232     windows->pixel_info->pen_colors[box_id].pixel);
2233   (void) XSetForeground(display,annotate_context,
2234     windows->pixel_info->pen_colors[pen_id].pixel);
2235   /*
2236     Begin annotating the image with text.
2237   */
2238   (void) CloneString(&windows->command.name,"Text");
2239   windows->command.data=0;
2240   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2241   state=DefaultState;
2242   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2243   text_event.xexpose.width=(int) font_info->max_bounds.width;
2244   text_event.xexpose.height=font_info->max_bounds.ascent+
2245     font_info->max_bounds.descent;
2246   p=annotate_info->text;
2247   do
2248   {
2249     /*
2250       Display text cursor.
2251     */
2252     *p='\0';
2253     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2254     /*
2255       Wait for next event.
2256     */
2257     XScreenEvent(display,windows,&event);
2258     if (event.xany.window == windows->command.id)
2259       {
2260         /*
2261           Select a command from the Command widget.
2262         */
2263         (void) XSetBackground(display,annotate_context,
2264           windows->pixel_info->background_color.pixel);
2265         (void) XSetForeground(display,annotate_context,
2266           windows->pixel_info->foreground_color.pixel);
2267         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2268         (void) XSetBackground(display,annotate_context,
2269           windows->pixel_info->pen_colors[box_id].pixel);
2270         (void) XSetForeground(display,annotate_context,
2271           windows->pixel_info->pen_colors[pen_id].pixel);
2272         if (id < 0)
2273           continue;
2274         switch (TextCommands[id])
2275         {
2276           case TextHelpCommand:
2277           {
2278             XTextViewHelp(display,resource_info,windows,MagickFalse,
2279               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2280             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2281             break;
2282           }
2283           case TextApplyCommand:
2284           {
2285             /*
2286               Finished annotating.
2287             */
2288             annotate_info->width=(unsigned int) XTextWidth(font_info,
2289               annotate_info->text,(int) strlen(annotate_info->text));
2290             XRefreshWindow(display,&windows->image,&text_event);
2291             state|=ExitState;
2292             break;
2293           }
2294           default:
2295             break;
2296         }
2297         continue;
2298       }
2299     /*
2300       Erase text cursor.
2301     */
2302     text_event.xexpose.x=x;
2303     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2304     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2305       (unsigned int) text_event.xexpose.width,(unsigned int)
2306       text_event.xexpose.height,MagickFalse);
2307     XRefreshWindow(display,&windows->image,&text_event);
2308     switch (event.type)
2309     {
2310       case ButtonPress:
2311       {
2312         if (event.xbutton.window != windows->image.id)
2313           break;
2314         if (event.xbutton.button == Button2)
2315           {
2316             /*
2317               Request primary selection.
2318             */
2319             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2320               windows->image.id,CurrentTime);
2321             break;
2322           }
2323         break;
2324       }
2325       case Expose:
2326       {
2327         if (event.xexpose.count == 0)
2328           {
2329             XAnnotateInfo
2330               *text_info;
2331 
2332             /*
2333               Refresh Image window.
2334             */
2335             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2336             text_info=annotate_info;
2337             while (text_info != (XAnnotateInfo *) NULL)
2338             {
2339               if (annotate_info->stencil == ForegroundStencil)
2340                 (void) XDrawString(display,windows->image.id,annotate_context,
2341                   text_info->x,text_info->y,text_info->text,
2342                   (int) strlen(text_info->text));
2343               else
2344                 (void) XDrawImageString(display,windows->image.id,
2345                   annotate_context,text_info->x,text_info->y,text_info->text,
2346                   (int) strlen(text_info->text));
2347               text_info=text_info->previous;
2348             }
2349             (void) XDrawString(display,windows->image.id,annotate_context,
2350               x,y,"_",1);
2351           }
2352         break;
2353       }
2354       case KeyPress:
2355       {
2356         int
2357           length;
2358 
2359         if (event.xkey.window != windows->image.id)
2360           break;
2361         /*
2362           Respond to a user key press.
2363         */
2364         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2365           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2366         *(command+length)='\0';
2367         if (((event.xkey.state & ControlMask) != 0) ||
2368             ((event.xkey.state & Mod1Mask) != 0))
2369           state|=ModifierState;
2370         if ((state & ModifierState) != 0)
2371           switch ((int) key_symbol)
2372           {
2373             case XK_u:
2374             case XK_U:
2375             {
2376               key_symbol=DeleteCommand;
2377               break;
2378             }
2379             default:
2380               break;
2381           }
2382         switch ((int) key_symbol)
2383         {
2384           case XK_BackSpace:
2385           {
2386             /*
2387               Erase one character.
2388             */
2389             if (p == annotate_info->text)
2390               {
2391                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2392                   break;
2393                 else
2394                   {
2395                     /*
2396                       Go to end of the previous line of text.
2397                     */
2398                     annotate_info=annotate_info->previous;
2399                     p=annotate_info->text;
2400                     x=annotate_info->x+annotate_info->width;
2401                     y=annotate_info->y;
2402                     if (annotate_info->width != 0)
2403                       p+=strlen(annotate_info->text);
2404                     break;
2405                   }
2406               }
2407             p--;
2408             x-=XTextWidth(font_info,p,1);
2409             text_event.xexpose.x=x;
2410             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2411             XRefreshWindow(display,&windows->image,&text_event);
2412             break;
2413           }
2414           case XK_bracketleft:
2415           {
2416             key_symbol=XK_Escape;
2417             break;
2418           }
2419           case DeleteCommand:
2420           {
2421             /*
2422               Erase the entire line of text.
2423             */
2424             while (p != annotate_info->text)
2425             {
2426               p--;
2427               x-=XTextWidth(font_info,p,1);
2428               text_event.xexpose.x=x;
2429               XRefreshWindow(display,&windows->image,&text_event);
2430             }
2431             break;
2432           }
2433           case XK_Escape:
2434           case XK_F20:
2435           {
2436             /*
2437               Finished annotating.
2438             */
2439             annotate_info->width=(unsigned int) XTextWidth(font_info,
2440               annotate_info->text,(int) strlen(annotate_info->text));
2441             XRefreshWindow(display,&windows->image,&text_event);
2442             state|=ExitState;
2443             break;
2444           }
2445           default:
2446           {
2447             /*
2448               Draw a single character on the Image window.
2449             */
2450             if ((state & ModifierState) != 0)
2451               break;
2452             if (*command == '\0')
2453               break;
2454             *p=(*command);
2455             if (annotate_info->stencil == ForegroundStencil)
2456               (void) XDrawString(display,windows->image.id,annotate_context,
2457                 x,y,p,1);
2458             else
2459               (void) XDrawImageString(display,windows->image.id,
2460                 annotate_context,x,y,p,1);
2461             x+=XTextWidth(font_info,p,1);
2462             p++;
2463             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2464               break;
2465           }
2466           case XK_Return:
2467           case XK_KP_Enter:
2468           {
2469             /*
2470               Advance to the next line of text.
2471             */
2472             *p='\0';
2473             annotate_info->width=(unsigned int) XTextWidth(font_info,
2474               annotate_info->text,(int) strlen(annotate_info->text));
2475             if (annotate_info->next != (XAnnotateInfo *) NULL)
2476               {
2477                 /*
2478                   Line of text already exists.
2479                 */
2480                 annotate_info=annotate_info->next;
2481                 x=annotate_info->x;
2482                 y=annotate_info->y;
2483                 p=annotate_info->text;
2484                 break;
2485               }
2486             annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2487               sizeof(*annotate_info->next));
2488             if (annotate_info->next == (XAnnotateInfo *) NULL)
2489               return(MagickFalse);
2490             *annotate_info->next=(*annotate_info);
2491             annotate_info->next->previous=annotate_info;
2492             annotate_info=annotate_info->next;
2493             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2494               windows->image.width/MagickMax((ssize_t)
2495               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2496             if (annotate_info->text == (char *) NULL)
2497               return(MagickFalse);
2498             annotate_info->y+=annotate_info->height;
2499             if (annotate_info->y > (int) windows->image.height)
2500               annotate_info->y=(int) annotate_info->height;
2501             annotate_info->next=(XAnnotateInfo *) NULL;
2502             x=annotate_info->x;
2503             y=annotate_info->y;
2504             p=annotate_info->text;
2505             break;
2506           }
2507         }
2508         break;
2509       }
2510       case KeyRelease:
2511       {
2512         /*
2513           Respond to a user key release.
2514         */
2515         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2516           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2517         state&=(~ModifierState);
2518         break;
2519       }
2520       case SelectionNotify:
2521       {
2522         Atom
2523           type;
2524 
2525         int
2526           format;
2527 
2528         unsigned char
2529           *data;
2530 
2531         unsigned long
2532           after,
2533           length;
2534 
2535         /*
2536           Obtain response from primary selection.
2537         */
2538         if (event.xselection.property == (Atom) None)
2539           break;
2540         status=XGetWindowProperty(display,event.xselection.requestor,
2541           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2542           &type,&format,&length,&after,&data);
2543         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2544             (length == 0))
2545           break;
2546         /*
2547           Annotate Image window with primary selection.
2548         */
2549         for (i=0; i < (ssize_t) length; i++)
2550         {
2551           if ((char) data[i] != '\n')
2552             {
2553               /*
2554                 Draw a single character on the Image window.
2555               */
2556               *p=(char) data[i];
2557               (void) XDrawString(display,windows->image.id,annotate_context,
2558                 x,y,p,1);
2559               x+=XTextWidth(font_info,p,1);
2560               p++;
2561               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2562                 continue;
2563             }
2564           /*
2565             Advance to the next line of text.
2566           */
2567           *p='\0';
2568           annotate_info->width=(unsigned int) XTextWidth(font_info,
2569             annotate_info->text,(int) strlen(annotate_info->text));
2570           if (annotate_info->next != (XAnnotateInfo *) NULL)
2571             {
2572               /*
2573                 Line of text already exists.
2574               */
2575               annotate_info=annotate_info->next;
2576               x=annotate_info->x;
2577               y=annotate_info->y;
2578               p=annotate_info->text;
2579               continue;
2580             }
2581           annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2582             sizeof(*annotate_info->next));
2583           if (annotate_info->next == (XAnnotateInfo *) NULL)
2584             return(MagickFalse);
2585           *annotate_info->next=(*annotate_info);
2586           annotate_info->next->previous=annotate_info;
2587           annotate_info=annotate_info->next;
2588           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2589             windows->image.width/MagickMax((ssize_t)
2590             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2591           if (annotate_info->text == (char *) NULL)
2592             return(MagickFalse);
2593           annotate_info->y+=annotate_info->height;
2594           if (annotate_info->y > (int) windows->image.height)
2595             annotate_info->y=(int) annotate_info->height;
2596           annotate_info->next=(XAnnotateInfo *) NULL;
2597           x=annotate_info->x;
2598           y=annotate_info->y;
2599           p=annotate_info->text;
2600         }
2601         (void) XFree((void *) data);
2602         break;
2603       }
2604       default:
2605         break;
2606     }
2607   } while ((state & ExitState) == 0);
2608   (void) XFreeCursor(display,cursor);
2609   /*
2610     Annotation is relative to image configuration.
2611   */
2612   width=(unsigned int) image->columns;
2613   height=(unsigned int) image->rows;
2614   x=0;
2615   y=0;
2616   if (windows->image.crop_geometry != (char *) NULL)
2617     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2618   /*
2619     Initialize annotated image.
2620   */
2621   XSetCursorState(display,windows,MagickTrue);
2622   XCheckRefreshWindows(display,windows);
2623   while (annotate_info != (XAnnotateInfo *) NULL)
2624   {
2625     if (annotate_info->width == 0)
2626       {
2627         /*
2628           No text on this line--  go to the next line of text.
2629         */
2630         previous_info=annotate_info->previous;
2631         annotate_info->text=(char *)
2632           RelinquishMagickMemory(annotate_info->text);
2633         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2634         annotate_info=previous_info;
2635         continue;
2636       }
2637     /*
2638       Determine pixel index for box and pen color.
2639     */
2640     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2641     if (windows->pixel_info->colors != 0)
2642       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2643         if (windows->pixel_info->pixels[i] ==
2644             windows->pixel_info->pen_colors[box_id].pixel)
2645           {
2646             windows->pixel_info->box_index=(unsigned short) i;
2647             break;
2648           }
2649     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2650     if (windows->pixel_info->colors != 0)
2651       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2652         if (windows->pixel_info->pixels[i] ==
2653             windows->pixel_info->pen_colors[pen_id].pixel)
2654           {
2655             windows->pixel_info->pen_index=(unsigned short) i;
2656             break;
2657           }
2658     /*
2659       Define the annotate geometry string.
2660     */
2661     annotate_info->x=(int)
2662       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2663     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2664       windows->image.y)/windows->image.ximage->height;
2665     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2666       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2667       height*annotate_info->height/windows->image.ximage->height,
2668       annotate_info->x+x,annotate_info->y+y);
2669     /*
2670       Annotate image with text.
2671     */
2672     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2673     if (status == 0)
2674       return(MagickFalse);
2675     /*
2676       Free up memory.
2677     */
2678     previous_info=annotate_info->previous;
2679     annotate_info->text=DestroyString(annotate_info->text);
2680     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2681     annotate_info=previous_info;
2682   }
2683   (void) XSetForeground(display,annotate_context,
2684     windows->pixel_info->foreground_color.pixel);
2685   (void) XSetBackground(display,annotate_context,
2686     windows->pixel_info->background_color.pixel);
2687   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2688   XSetCursorState(display,windows,MagickFalse);
2689   (void) XFreeFont(display,font_info);
2690   /*
2691     Update image configuration.
2692   */
2693   XConfigureImageColormap(display,resource_info,windows,image);
2694   (void) XConfigureImage(display,resource_info,windows,image);
2695   return(MagickTrue);
2696 }
2697 
2698 /*
2699 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2700 %                                                                             %
2701 %                                                                             %
2702 %                                                                             %
2703 +   X B a c k g r o u n d I m a g e                                           %
2704 %                                                                             %
2705 %                                                                             %
2706 %                                                                             %
2707 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2708 %
2709 %  XBackgroundImage() displays the image in the background of a window.
2710 %
2711 %  The format of the XBackgroundImage method is:
2712 %
2713 %      MagickBooleanType XBackgroundImage(Display *display,
2714 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
2715 %
2716 %  A description of each parameter follows:
2717 %
2718 %    o display: Specifies a connection to an X server; returned from
2719 %      XOpenDisplay.
2720 %
2721 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2722 %
2723 %    o windows: Specifies a pointer to a XWindows structure.
2724 %
2725 %    o image: the image.
2726 %
2727 */
XBackgroundImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image)2728 static MagickBooleanType XBackgroundImage(Display *display,
2729   XResourceInfo *resource_info,XWindows *windows,Image **image)
2730 {
2731 #define BackgroundImageTag  "Background/Image"
2732 
2733   int
2734     status;
2735 
2736   static char
2737     window_id[MaxTextExtent] = "root";
2738 
2739   XResourceInfo
2740     background_resources;
2741 
2742   /*
2743     Put image in background.
2744   */
2745   status=XDialogWidget(display,windows,"Background",
2746     "Enter window id (id 0x00 selects window with pointer):",window_id);
2747   if (*window_id == '\0')
2748     return(MagickFalse);
2749   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2750   XInfoWidget(display,windows,BackgroundImageTag);
2751   XSetCursorState(display,windows,MagickTrue);
2752   XCheckRefreshWindows(display,windows);
2753   background_resources=(*resource_info);
2754   background_resources.window_id=window_id;
2755   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2756   status=XDisplayBackgroundImage(display,&background_resources,*image);
2757   if (status != MagickFalse)
2758     XClientMessage(display,windows->image.id,windows->im_protocols,
2759       windows->im_retain_colors,CurrentTime);
2760   XSetCursorState(display,windows,MagickFalse);
2761   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2762   return(MagickTrue);
2763 }
2764 
2765 /*
2766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2767 %                                                                             %
2768 %                                                                             %
2769 %                                                                             %
2770 +   X C h o p I m a g e                                                       %
2771 %                                                                             %
2772 %                                                                             %
2773 %                                                                             %
2774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2775 %
2776 %  XChopImage() chops the X image.
2777 %
2778 %  The format of the XChopImage method is:
2779 %
2780 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2781 %      XWindows *windows,Image **image)
2782 %
2783 %  A description of each parameter follows:
2784 %
2785 %    o display: Specifies a connection to an X server; returned from
2786 %      XOpenDisplay.
2787 %
2788 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2789 %
2790 %    o windows: Specifies a pointer to a XWindows structure.
2791 %
2792 %    o image: the image.
2793 %
2794 */
XChopImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image)2795 static MagickBooleanType XChopImage(Display *display,
2796   XResourceInfo *resource_info,XWindows *windows,Image **image)
2797 {
2798   const char
2799     *const ChopMenu[] =
2800     {
2801       "Direction",
2802       "Help",
2803       "Dismiss",
2804       (char *) NULL
2805     };
2806 
2807   static ModeType
2808     direction = HorizontalChopCommand;
2809 
2810   static const ModeType
2811     ChopCommands[] =
2812     {
2813       ChopDirectionCommand,
2814       ChopHelpCommand,
2815       ChopDismissCommand
2816     },
2817     DirectionCommands[] =
2818     {
2819       HorizontalChopCommand,
2820       VerticalChopCommand
2821     };
2822 
2823   char
2824     text[MaxTextExtent];
2825 
2826   Image
2827     *chop_image;
2828 
2829   int
2830     id,
2831     x,
2832     y;
2833 
2834   MagickRealType
2835     scale_factor;
2836 
2837   RectangleInfo
2838     chop_info;
2839 
2840   unsigned int
2841     distance,
2842     height,
2843     width;
2844 
2845   size_t
2846     state;
2847 
2848   XEvent
2849     event;
2850 
2851   XSegment
2852     segment_info;
2853 
2854   /*
2855     Map Command widget.
2856   */
2857   (void) CloneString(&windows->command.name,"Chop");
2858   windows->command.data=1;
2859   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2860   (void) XMapRaised(display,windows->command.id);
2861   XClientMessage(display,windows->image.id,windows->im_protocols,
2862     windows->im_update_widget,CurrentTime);
2863   /*
2864     Track pointer until button 1 is pressed.
2865   */
2866   XQueryPosition(display,windows->image.id,&x,&y);
2867   (void) XSelectInput(display,windows->image.id,
2868     windows->image.attributes.event_mask | PointerMotionMask);
2869   state=DefaultState;
2870   (void) memset(&segment_info,0,sizeof(segment_info));
2871   do
2872   {
2873     if (windows->info.mapped != MagickFalse)
2874       {
2875         /*
2876           Display pointer position.
2877         */
2878         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2879           x+windows->image.x,y+windows->image.y);
2880         XInfoWidget(display,windows,text);
2881       }
2882     /*
2883       Wait for next event.
2884     */
2885     XScreenEvent(display,windows,&event);
2886     if (event.xany.window == windows->command.id)
2887       {
2888         /*
2889           Select a command from the Command widget.
2890         */
2891         id=XCommandWidget(display,windows,ChopMenu,&event);
2892         if (id < 0)
2893           continue;
2894         switch (ChopCommands[id])
2895         {
2896           case ChopDirectionCommand:
2897           {
2898             char
2899               command[MaxTextExtent];
2900 
2901             const char
2902               *const Directions[] =
2903               {
2904                 "horizontal",
2905                 "vertical",
2906                 (char *) NULL,
2907               };
2908 
2909             /*
2910               Select a command from the pop-up menu.
2911             */
2912             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2913             if (id >= 0)
2914               direction=DirectionCommands[id];
2915             break;
2916           }
2917           case ChopHelpCommand:
2918           {
2919             XTextViewHelp(display,resource_info,windows,MagickFalse,
2920               "Help Viewer - Image Chop",ImageChopHelp);
2921             break;
2922           }
2923           case ChopDismissCommand:
2924           {
2925             /*
2926               Prematurely exit.
2927             */
2928             state|=EscapeState;
2929             state|=ExitState;
2930             break;
2931           }
2932           default:
2933             break;
2934         }
2935         continue;
2936       }
2937     switch (event.type)
2938     {
2939       case ButtonPress:
2940       {
2941         if (event.xbutton.button != Button1)
2942           break;
2943         if (event.xbutton.window != windows->image.id)
2944           break;
2945         /*
2946           User has committed to start point of chopping line.
2947         */
2948         segment_info.x1=(short int) event.xbutton.x;
2949         segment_info.x2=(short int) event.xbutton.x;
2950         segment_info.y1=(short int) event.xbutton.y;
2951         segment_info.y2=(short int) event.xbutton.y;
2952         state|=ExitState;
2953         break;
2954       }
2955       case ButtonRelease:
2956         break;
2957       case Expose:
2958         break;
2959       case KeyPress:
2960       {
2961         char
2962           command[MaxTextExtent];
2963 
2964         KeySym
2965           key_symbol;
2966 
2967         if (event.xkey.window != windows->image.id)
2968           break;
2969         /*
2970           Respond to a user key press.
2971         */
2972         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2973           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2974         switch ((int) key_symbol)
2975         {
2976           case XK_Escape:
2977           case XK_F20:
2978           {
2979             /*
2980               Prematurely exit.
2981             */
2982             state|=EscapeState;
2983             state|=ExitState;
2984             break;
2985           }
2986           case XK_F1:
2987           case XK_Help:
2988           {
2989             (void) XSetFunction(display,windows->image.highlight_context,
2990               GXcopy);
2991             XTextViewHelp(display,resource_info,windows,MagickFalse,
2992               "Help Viewer - Image Chop",ImageChopHelp);
2993             (void) XSetFunction(display,windows->image.highlight_context,
2994               GXinvert);
2995             break;
2996           }
2997           default:
2998           {
2999             (void) XBell(display,0);
3000             break;
3001           }
3002         }
3003         break;
3004       }
3005       case MotionNotify:
3006       {
3007         /*
3008           Map and unmap Info widget as text cursor crosses its boundaries.
3009         */
3010         x=event.xmotion.x;
3011         y=event.xmotion.y;
3012         if (windows->info.mapped != MagickFalse)
3013           {
3014             if ((x < (int) (windows->info.x+windows->info.width)) &&
3015                 (y < (int) (windows->info.y+windows->info.height)))
3016               (void) XWithdrawWindow(display,windows->info.id,
3017                 windows->info.screen);
3018           }
3019         else
3020           if ((x > (int) (windows->info.x+windows->info.width)) ||
3021               (y > (int) (windows->info.y+windows->info.height)))
3022             (void) XMapWindow(display,windows->info.id);
3023       }
3024     }
3025   } while ((state & ExitState) == 0);
3026   (void) XSelectInput(display,windows->image.id,
3027     windows->image.attributes.event_mask);
3028   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3029   if ((state & EscapeState) != 0)
3030     return(MagickTrue);
3031   /*
3032     Draw line as pointer moves until the mouse button is released.
3033   */
3034   chop_info.width=0;
3035   chop_info.height=0;
3036   chop_info.x=0;
3037   chop_info.y=0;
3038   distance=0;
3039   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3040   state=DefaultState;
3041   do
3042   {
3043     if (distance > 9)
3044       {
3045         /*
3046           Display info and draw chopping line.
3047         */
3048         if (windows->info.mapped == MagickFalse)
3049           (void) XMapWindow(display,windows->info.id);
3050         (void) FormatLocaleString(text,MaxTextExtent,
3051           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3052           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3053         XInfoWidget(display,windows,text);
3054         XHighlightLine(display,windows->image.id,
3055           windows->image.highlight_context,&segment_info);
3056       }
3057     else
3058       if (windows->info.mapped != MagickFalse)
3059         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3060     /*
3061       Wait for next event.
3062     */
3063     XScreenEvent(display,windows,&event);
3064     if (distance > 9)
3065       XHighlightLine(display,windows->image.id,
3066         windows->image.highlight_context,&segment_info);
3067     switch (event.type)
3068     {
3069       case ButtonPress:
3070       {
3071         segment_info.x2=(short int) event.xmotion.x;
3072         segment_info.y2=(short int) event.xmotion.y;
3073         break;
3074       }
3075       case ButtonRelease:
3076       {
3077         /*
3078           User has committed to chopping line.
3079         */
3080         segment_info.x2=(short int) event.xbutton.x;
3081         segment_info.y2=(short int) event.xbutton.y;
3082         state|=ExitState;
3083         break;
3084       }
3085       case Expose:
3086         break;
3087       case MotionNotify:
3088       {
3089         segment_info.x2=(short int) event.xmotion.x;
3090         segment_info.y2=(short int) event.xmotion.y;
3091       }
3092       default:
3093         break;
3094     }
3095     /*
3096       Check boundary conditions.
3097     */
3098     if (segment_info.x2 < 0)
3099       segment_info.x2=0;
3100     else
3101       if (segment_info.x2 > windows->image.ximage->width)
3102         segment_info.x2=windows->image.ximage->width;
3103     if (segment_info.y2 < 0)
3104       segment_info.y2=0;
3105     else
3106       if (segment_info.y2 > windows->image.ximage->height)
3107         segment_info.y2=windows->image.ximage->height;
3108     distance=(unsigned int)
3109       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3110        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3111     /*
3112       Compute chopping geometry.
3113     */
3114     if (direction == HorizontalChopCommand)
3115       {
3116         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3117         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3118         chop_info.height=0;
3119         chop_info.y=0;
3120         if (segment_info.x1 > (int) segment_info.x2)
3121           {
3122             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3123             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3124           }
3125       }
3126     else
3127       {
3128         chop_info.width=0;
3129         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3130         chop_info.x=0;
3131         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3132         if (segment_info.y1 > segment_info.y2)
3133           {
3134             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3135             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3136           }
3137       }
3138   } while ((state & ExitState) == 0);
3139   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3140   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3141   if (distance <= 9)
3142     return(MagickTrue);
3143   /*
3144     Image chopping is relative to image configuration.
3145   */
3146   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3147   XSetCursorState(display,windows,MagickTrue);
3148   XCheckRefreshWindows(display,windows);
3149   windows->image.window_changes.width=windows->image.ximage->width-
3150     (unsigned int) chop_info.width;
3151   windows->image.window_changes.height=windows->image.ximage->height-
3152     (unsigned int) chop_info.height;
3153   width=(unsigned int) (*image)->columns;
3154   height=(unsigned int) (*image)->rows;
3155   x=0;
3156   y=0;
3157   if (windows->image.crop_geometry != (char *) NULL)
3158     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3159   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3160   chop_info.x+=x;
3161   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3162   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3163   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3164   chop_info.y+=y;
3165   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3166   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3167   /*
3168     Chop image.
3169   */
3170   chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3171   XSetCursorState(display,windows,MagickFalse);
3172   if (chop_image == (Image *) NULL)
3173     return(MagickFalse);
3174   *image=DestroyImage(*image);
3175   *image=chop_image;
3176   /*
3177     Update image configuration.
3178   */
3179   XConfigureImageColormap(display,resource_info,windows,*image);
3180   (void) XConfigureImage(display,resource_info,windows,*image);
3181   return(MagickTrue);
3182 }
3183 
3184 /*
3185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3186 %                                                                             %
3187 %                                                                             %
3188 %                                                                             %
3189 +   X C o l o r E d i t I m a g e                                             %
3190 %                                                                             %
3191 %                                                                             %
3192 %                                                                             %
3193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3194 %
3195 %  XColorEditImage() allows the user to interactively change the color of one
3196 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3197 %
3198 %  The format of the XColorEditImage method is:
3199 %
3200 %      MagickBooleanType XColorEditImage(Display *display,
3201 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
3202 %
3203 %  A description of each parameter follows:
3204 %
3205 %    o display: Specifies a connection to an X server;  returned from
3206 %      XOpenDisplay.
3207 %
3208 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3209 %
3210 %    o windows: Specifies a pointer to a XWindows structure.
3211 %
3212 %    o image: the image; returned from ReadImage.
3213 %
3214 */
3215 
3216 
XColorEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image)3217 static MagickBooleanType XColorEditImage(Display *display,
3218   XResourceInfo *resource_info,XWindows *windows,Image **image)
3219 {
3220   const char
3221     *const ColorEditMenu[] =
3222     {
3223       "Method",
3224       "Pixel Color",
3225       "Border Color",
3226       "Fuzz",
3227       "Undo",
3228       "Help",
3229       "Dismiss",
3230       (char *) NULL
3231     };
3232 
3233   static const ModeType
3234     ColorEditCommands[] =
3235     {
3236       ColorEditMethodCommand,
3237       ColorEditColorCommand,
3238       ColorEditBorderCommand,
3239       ColorEditFuzzCommand,
3240       ColorEditUndoCommand,
3241       ColorEditHelpCommand,
3242       ColorEditDismissCommand
3243     };
3244 
3245   static PaintMethod
3246     method = PointMethod;
3247 
3248   static unsigned int
3249     pen_id = 0;
3250 
3251   static XColor
3252     border_color = { 0, 0, 0, 0, 0, 0 };
3253 
3254   char
3255     command[MaxTextExtent],
3256     text[MaxTextExtent];
3257 
3258   Cursor
3259     cursor;
3260 
3261   ExceptionInfo
3262     *exception;
3263 
3264   int
3265     entry,
3266     id,
3267     x,
3268     x_offset,
3269     y,
3270     y_offset;
3271 
3272   PixelPacket
3273     *q;
3274 
3275   ssize_t
3276     i;
3277 
3278   unsigned int
3279     height,
3280     width;
3281 
3282   size_t
3283     state;
3284 
3285   XColor
3286     color;
3287 
3288   XEvent
3289     event;
3290 
3291   /*
3292     Map Command widget.
3293   */
3294   (void) CloneString(&windows->command.name,"Color Edit");
3295   windows->command.data=4;
3296   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3297   (void) XMapRaised(display,windows->command.id);
3298   XClientMessage(display,windows->image.id,windows->im_protocols,
3299     windows->im_update_widget,CurrentTime);
3300   /*
3301     Make cursor.
3302   */
3303   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3304     resource_info->background_color,resource_info->foreground_color);
3305   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3306   /*
3307     Track pointer until button 1 is pressed.
3308   */
3309   XQueryPosition(display,windows->image.id,&x,&y);
3310   (void) XSelectInput(display,windows->image.id,
3311     windows->image.attributes.event_mask | PointerMotionMask);
3312   state=DefaultState;
3313   do
3314   {
3315     if (windows->info.mapped != MagickFalse)
3316       {
3317         /*
3318           Display pointer position.
3319         */
3320         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3321           x+windows->image.x,y+windows->image.y);
3322         XInfoWidget(display,windows,text);
3323       }
3324     /*
3325       Wait for next event.
3326     */
3327     XScreenEvent(display,windows,&event);
3328     if (event.xany.window == windows->command.id)
3329       {
3330         /*
3331           Select a command from the Command widget.
3332         */
3333         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3334         if (id < 0)
3335           {
3336             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3337             continue;
3338           }
3339         switch (ColorEditCommands[id])
3340         {
3341           case ColorEditMethodCommand:
3342           {
3343             char
3344               **methods;
3345 
3346             /*
3347               Select a method from the pop-up menu.
3348             */
3349             methods=(char **) GetCommandOptions(MagickMethodOptions);
3350             if (methods == (char **) NULL)
3351               break;
3352             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3353               (const char **) methods,command);
3354             if (entry >= 0)
3355               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3356                 MagickFalse,methods[entry]);
3357             methods=DestroyStringList(methods);
3358             break;
3359           }
3360           case ColorEditColorCommand:
3361           {
3362             const char
3363               *ColorMenu[MaxNumberPens];
3364 
3365             int
3366               pen_number;
3367 
3368             /*
3369               Initialize menu selections.
3370             */
3371             for (i=0; i < (int) (MaxNumberPens-2); i++)
3372               ColorMenu[i]=resource_info->pen_colors[i];
3373             ColorMenu[MaxNumberPens-2]="Browser...";
3374             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3375             /*
3376               Select a pen color from the pop-up menu.
3377             */
3378             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3379               (const char **) ColorMenu,command);
3380             if (pen_number < 0)
3381               break;
3382             if (pen_number == (MaxNumberPens-2))
3383               {
3384                 static char
3385                   color_name[MaxTextExtent] = "gray";
3386 
3387                 /*
3388                   Select a pen color from a dialog.
3389                 */
3390                 resource_info->pen_colors[pen_number]=color_name;
3391                 XColorBrowserWidget(display,windows,"Select",color_name);
3392                 if (*color_name == '\0')
3393                   break;
3394               }
3395             /*
3396               Set pen color.
3397             */
3398             (void) XParseColor(display,windows->map_info->colormap,
3399               resource_info->pen_colors[pen_number],&color);
3400             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3401               (unsigned int) MaxColors,&color);
3402             windows->pixel_info->pen_colors[pen_number]=color;
3403             pen_id=(unsigned int) pen_number;
3404             break;
3405           }
3406           case ColorEditBorderCommand:
3407           {
3408             const char
3409               *ColorMenu[MaxNumberPens];
3410 
3411             int
3412               pen_number;
3413 
3414             /*
3415               Initialize menu selections.
3416             */
3417             for (i=0; i < (int) (MaxNumberPens-2); i++)
3418               ColorMenu[i]=resource_info->pen_colors[i];
3419             ColorMenu[MaxNumberPens-2]="Browser...";
3420             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3421             /*
3422               Select a pen color from the pop-up menu.
3423             */
3424             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3425               (const char **) ColorMenu,command);
3426             if (pen_number < 0)
3427               break;
3428             if (pen_number == (MaxNumberPens-2))
3429               {
3430                 static char
3431                   color_name[MaxTextExtent] = "gray";
3432 
3433                 /*
3434                   Select a pen color from a dialog.
3435                 */
3436                 resource_info->pen_colors[pen_number]=color_name;
3437                 XColorBrowserWidget(display,windows,"Select",color_name);
3438                 if (*color_name == '\0')
3439                   break;
3440               }
3441             /*
3442               Set border color.
3443             */
3444             (void) XParseColor(display,windows->map_info->colormap,
3445               resource_info->pen_colors[pen_number],&border_color);
3446             break;
3447           }
3448           case ColorEditFuzzCommand:
3449           {
3450             static char
3451               fuzz[MaxTextExtent];
3452 
3453             static const char
3454               *FuzzMenu[] =
3455               {
3456                 "0%",
3457                 "2%",
3458                 "5%",
3459                 "10%",
3460                 "15%",
3461                 "Dialog...",
3462                 (char *) NULL,
3463               };
3464 
3465             /*
3466               Select a command from the pop-up menu.
3467             */
3468             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3469               command);
3470             if (entry < 0)
3471               break;
3472             if (entry != 5)
3473               {
3474                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3475                   QuantumRange+1.0);
3476                 break;
3477               }
3478             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3479             (void) XDialogWidget(display,windows,"Ok",
3480               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3481             if (*fuzz == '\0')
3482               break;
3483             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3484             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3485               1.0);
3486             break;
3487           }
3488           case ColorEditUndoCommand:
3489           {
3490             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3491               image);
3492             break;
3493           }
3494           case ColorEditHelpCommand:
3495           default:
3496           {
3497             XTextViewHelp(display,resource_info,windows,MagickFalse,
3498               "Help Viewer - Image Annotation",ImageColorEditHelp);
3499             break;
3500           }
3501           case ColorEditDismissCommand:
3502           {
3503             /*
3504               Prematurely exit.
3505             */
3506             state|=EscapeState;
3507             state|=ExitState;
3508             break;
3509           }
3510         }
3511         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3512         continue;
3513       }
3514     switch (event.type)
3515     {
3516       case ButtonPress:
3517       {
3518         if (event.xbutton.button != Button1)
3519           break;
3520         if ((event.xbutton.window != windows->image.id) &&
3521             (event.xbutton.window != windows->magnify.id))
3522           break;
3523         /*
3524           exit loop.
3525         */
3526         x=event.xbutton.x;
3527         y=event.xbutton.y;
3528         (void) XMagickCommand(display,resource_info,windows,
3529           SaveToUndoBufferCommand,image);
3530         state|=UpdateConfigurationState;
3531         break;
3532       }
3533       case ButtonRelease:
3534       {
3535         if (event.xbutton.button != Button1)
3536           break;
3537         if ((event.xbutton.window != windows->image.id) &&
3538             (event.xbutton.window != windows->magnify.id))
3539           break;
3540         /*
3541           Update colormap information.
3542         */
3543         x=event.xbutton.x;
3544         y=event.xbutton.y;
3545         XConfigureImageColormap(display,resource_info,windows,*image);
3546         (void) XConfigureImage(display,resource_info,windows,*image);
3547         XInfoWidget(display,windows,text);
3548         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3549         state&=(~UpdateConfigurationState);
3550         break;
3551       }
3552       case Expose:
3553         break;
3554       case KeyPress:
3555       {
3556         KeySym
3557           key_symbol;
3558 
3559         if (event.xkey.window == windows->magnify.id)
3560           {
3561             Window
3562               window;
3563 
3564             window=windows->magnify.id;
3565             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3566           }
3567         if (event.xkey.window != windows->image.id)
3568           break;
3569         /*
3570           Respond to a user key press.
3571         */
3572         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3573           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3574         switch ((int) key_symbol)
3575         {
3576           case XK_Escape:
3577           case XK_F20:
3578           {
3579             /*
3580               Prematurely exit.
3581             */
3582             state|=ExitState;
3583             break;
3584           }
3585           case XK_F1:
3586           case XK_Help:
3587           {
3588             XTextViewHelp(display,resource_info,windows,MagickFalse,
3589               "Help Viewer - Image Annotation",ImageColorEditHelp);
3590             break;
3591           }
3592           default:
3593           {
3594             (void) XBell(display,0);
3595             break;
3596           }
3597         }
3598         break;
3599       }
3600       case MotionNotify:
3601       {
3602         /*
3603           Map and unmap Info widget as cursor crosses its boundaries.
3604         */
3605         x=event.xmotion.x;
3606         y=event.xmotion.y;
3607         if (windows->info.mapped != MagickFalse)
3608           {
3609             if ((x < (int) (windows->info.x+windows->info.width)) &&
3610                 (y < (int) (windows->info.y+windows->info.height)))
3611               (void) XWithdrawWindow(display,windows->info.id,
3612                 windows->info.screen);
3613           }
3614         else
3615           if ((x > (int) (windows->info.x+windows->info.width)) ||
3616               (y > (int) (windows->info.y+windows->info.height)))
3617             (void) XMapWindow(display,windows->info.id);
3618         break;
3619       }
3620       default:
3621         break;
3622     }
3623     if (event.xany.window == windows->magnify.id)
3624       {
3625         x=windows->magnify.x-windows->image.x;
3626         y=windows->magnify.y-windows->image.y;
3627       }
3628     x_offset=x;
3629     y_offset=y;
3630     if ((state & UpdateConfigurationState) != 0)
3631       {
3632         CacheView
3633           *image_view;
3634 
3635         int
3636           x,
3637           y;
3638 
3639         /*
3640           Pixel edit is relative to image configuration.
3641         */
3642         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3643           MagickTrue);
3644         color=windows->pixel_info->pen_colors[pen_id];
3645         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3646         width=(unsigned int) (*image)->columns;
3647         height=(unsigned int) (*image)->rows;
3648         x=0;
3649         y=0;
3650         if (windows->image.crop_geometry != (char *) NULL)
3651           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3652             &width,&height);
3653         x_offset=(int)
3654           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3655         y_offset=(int)
3656           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3657         if ((x_offset < 0) || (y_offset < 0))
3658           continue;
3659         if ((x_offset >= (int) (*image)->columns) ||
3660             (y_offset >= (int) (*image)->rows))
3661           continue;
3662         exception=(&(*image)->exception);
3663         image_view=AcquireAuthenticCacheView(*image,exception);
3664         switch (method)
3665         {
3666           case PointMethod:
3667           default:
3668           {
3669             /*
3670               Update color information using point algorithm.
3671             */
3672             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3673               return(MagickFalse);
3674             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3675               (ssize_t)y_offset,1,1,exception);
3676             if (q == (PixelPacket *) NULL)
3677               break;
3678             q->red=ScaleShortToQuantum(color.red);
3679             q->green=ScaleShortToQuantum(color.green);
3680             q->blue=ScaleShortToQuantum(color.blue);
3681             (void) SyncCacheViewAuthenticPixels(image_view,
3682               &(*image)->exception);
3683             break;
3684           }
3685           case ReplaceMethod:
3686           {
3687             PixelPacket
3688               target;
3689 
3690             /*
3691               Update color information using replace algorithm.
3692             */
3693             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3694               (ssize_t) y_offset,&target,&(*image)->exception);
3695             if ((*image)->storage_class == DirectClass)
3696               {
3697                 for (y=0; y < (int) (*image)->rows; y++)
3698                 {
3699                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3700                     (*image)->columns,1,exception);
3701                   if (q == (PixelPacket *) NULL)
3702                     break;
3703                   for (x=0; x < (int) (*image)->columns; x++)
3704                   {
3705                     if (IsColorSimilar(*image,q,&target) != MagickFalse)
3706                       {
3707                         q->red=ScaleShortToQuantum(color.red);
3708                         q->green=ScaleShortToQuantum(color.green);
3709                         q->blue=ScaleShortToQuantum(color.blue);
3710                       }
3711                     q++;
3712                   }
3713                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3714                     break;
3715                 }
3716               }
3717             else
3718               {
3719                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3720                   if (IsColorSimilar(*image,(*image)->colormap+i,&target) != MagickFalse)
3721                     {
3722                       (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3723                       (*image)->colormap[i].green=ScaleShortToQuantum(
3724                         color.green);
3725                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3726                         color.blue);
3727                     }
3728                 (void) SyncImage(*image);
3729               }
3730             break;
3731           }
3732           case FloodfillMethod:
3733           case FillToBorderMethod:
3734           {
3735             DrawInfo
3736               *draw_info;
3737 
3738             MagickPixelPacket
3739               target;
3740 
3741             /*
3742               Update color information using floodfill algorithm.
3743             */
3744             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3745               (ssize_t) y_offset,&target,exception);
3746             if (method == FillToBorderMethod)
3747               {
3748                 target.red=(MagickRealType)
3749                   ScaleShortToQuantum(border_color.red);
3750                 target.green=(MagickRealType)
3751                   ScaleShortToQuantum(border_color.green);
3752                 target.blue=(MagickRealType)
3753                   ScaleShortToQuantum(border_color.blue);
3754               }
3755             draw_info=CloneDrawInfo(resource_info->image_info,
3756               (DrawInfo *) NULL);
3757             (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3758               &draw_info->fill,exception);
3759             (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3760               (ssize_t) x_offset,(ssize_t) y_offset,
3761               method == FloodfillMethod ? MagickFalse : MagickTrue);
3762             draw_info=DestroyDrawInfo(draw_info);
3763             break;
3764           }
3765           case ResetMethod:
3766           {
3767             /*
3768               Update color information using reset algorithm.
3769             */
3770             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3771               return(MagickFalse);
3772             for (y=0; y < (int) (*image)->rows; y++)
3773             {
3774               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3775                 (*image)->columns,1,exception);
3776               if (q == (PixelPacket *) NULL)
3777                 break;
3778               for (x=0; x < (int) (*image)->columns; x++)
3779               {
3780                 q->red=ScaleShortToQuantum(color.red);
3781                 q->green=ScaleShortToQuantum(color.green);
3782                 q->blue=ScaleShortToQuantum(color.blue);
3783                 q++;
3784               }
3785               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3786                 break;
3787             }
3788             break;
3789           }
3790         }
3791         image_view=DestroyCacheView(image_view);
3792         state&=(~UpdateConfigurationState);
3793       }
3794   } while ((state & ExitState) == 0);
3795   (void) XSelectInput(display,windows->image.id,
3796     windows->image.attributes.event_mask);
3797   XSetCursorState(display,windows,MagickFalse);
3798   (void) XFreeCursor(display,cursor);
3799   return(MagickTrue);
3800 }
3801 
3802 /*
3803 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3804 %                                                                             %
3805 %                                                                             %
3806 %                                                                             %
3807 +   X C o m p o s i t e I m a g e                                             %
3808 %                                                                             %
3809 %                                                                             %
3810 %                                                                             %
3811 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3812 %
3813 %  XCompositeImage() requests an image name from the user, reads the image and
3814 %  composites it with the X window image at a location the user chooses with
3815 %  the pointer.
3816 %
3817 %  The format of the XCompositeImage method is:
3818 %
3819 %      MagickBooleanType XCompositeImage(Display *display,
3820 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
3821 %
3822 %  A description of each parameter follows:
3823 %
3824 %    o display: Specifies a connection to an X server;  returned from
3825 %      XOpenDisplay.
3826 %
3827 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3828 %
3829 %    o windows: Specifies a pointer to a XWindows structure.
3830 %
3831 %    o image: the image; returned from ReadImage.
3832 %
3833 */
XCompositeImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image)3834 static MagickBooleanType XCompositeImage(Display *display,
3835   XResourceInfo *resource_info,XWindows *windows,Image *image)
3836 {
3837   const char
3838     *const CompositeMenu[] =
3839     {
3840       "Operators",
3841       "Dissolve",
3842       "Displace",
3843       "Help",
3844       "Dismiss",
3845       (char *) NULL
3846     };
3847 
3848   static char
3849     displacement_geometry[MaxTextExtent] = "30x30",
3850     filename[MaxTextExtent] = "\0";
3851 
3852   static CompositeOperator
3853     compose = CopyCompositeOp;
3854 
3855   static const ModeType
3856     CompositeCommands[] =
3857     {
3858       CompositeOperatorsCommand,
3859       CompositeDissolveCommand,
3860       CompositeDisplaceCommand,
3861       CompositeHelpCommand,
3862       CompositeDismissCommand
3863     };
3864 
3865   char
3866     text[MaxTextExtent];
3867 
3868   Cursor
3869     cursor;
3870 
3871   Image
3872     *composite_image;
3873 
3874   int
3875     entry,
3876     id,
3877     x,
3878     y;
3879 
3880   MagickRealType
3881     blend,
3882     scale_factor;
3883 
3884   RectangleInfo
3885     highlight_info,
3886     composite_info;
3887 
3888   unsigned int
3889     height,
3890     width;
3891 
3892   size_t
3893     state;
3894 
3895   XEvent
3896     event;
3897 
3898   /*
3899     Request image file name from user.
3900   */
3901   XFileBrowserWidget(display,windows,"Composite",filename);
3902   if (*filename == '\0')
3903     return(MagickTrue);
3904   /*
3905     Read image.
3906   */
3907   XSetCursorState(display,windows,MagickTrue);
3908   XCheckRefreshWindows(display,windows);
3909   (void) CopyMagickString(resource_info->image_info->filename,filename,
3910     MaxTextExtent);
3911   composite_image=ReadImage(resource_info->image_info,&image->exception);
3912   CatchException(&image->exception);
3913   XSetCursorState(display,windows,MagickFalse);
3914   if (composite_image == (Image *) NULL)
3915     return(MagickFalse);
3916   /*
3917     Map Command widget.
3918   */
3919   (void) CloneString(&windows->command.name,"Composite");
3920   windows->command.data=1;
3921   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3922   (void) XMapRaised(display,windows->command.id);
3923   XClientMessage(display,windows->image.id,windows->im_protocols,
3924     windows->im_update_widget,CurrentTime);
3925   /*
3926     Track pointer until button 1 is pressed.
3927   */
3928   XQueryPosition(display,windows->image.id,&x,&y);
3929   (void) XSelectInput(display,windows->image.id,
3930     windows->image.attributes.event_mask | PointerMotionMask);
3931   composite_info.x=(ssize_t) windows->image.x+x;
3932   composite_info.y=(ssize_t) windows->image.y+y;
3933   composite_info.width=0;
3934   composite_info.height=0;
3935   cursor=XCreateFontCursor(display,XC_ul_angle);
3936   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3937   blend=0.0;
3938   state=DefaultState;
3939   do
3940   {
3941     if (windows->info.mapped != MagickFalse)
3942       {
3943         /*
3944           Display pointer position.
3945         */
3946         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
3947           (long) composite_info.x,(long) composite_info.y);
3948         XInfoWidget(display,windows,text);
3949       }
3950     highlight_info=composite_info;
3951     highlight_info.x=composite_info.x-windows->image.x;
3952     highlight_info.y=composite_info.y-windows->image.y;
3953     XHighlightRectangle(display,windows->image.id,
3954       windows->image.highlight_context,&highlight_info);
3955     /*
3956       Wait for next event.
3957     */
3958     XScreenEvent(display,windows,&event);
3959     XHighlightRectangle(display,windows->image.id,
3960       windows->image.highlight_context,&highlight_info);
3961     if (event.xany.window == windows->command.id)
3962       {
3963         /*
3964           Select a command from the Command widget.
3965         */
3966         id=XCommandWidget(display,windows,CompositeMenu,&event);
3967         if (id < 0)
3968           continue;
3969         switch (CompositeCommands[id])
3970         {
3971           case CompositeOperatorsCommand:
3972           {
3973             char
3974               command[MaxTextExtent],
3975               **operators;
3976 
3977             /*
3978               Select a command from the pop-up menu.
3979             */
3980             operators=GetCommandOptions(MagickComposeOptions);
3981             if (operators == (char **) NULL)
3982               break;
3983             entry=XMenuWidget(display,windows,CompositeMenu[id],
3984               (const char **) operators,command);
3985             if (entry >= 0)
3986               compose=(CompositeOperator) ParseCommandOption(
3987                 MagickComposeOptions,MagickFalse,operators[entry]);
3988             operators=DestroyStringList(operators);
3989             break;
3990           }
3991           case CompositeDissolveCommand:
3992           {
3993             static char
3994               factor[MaxTextExtent] = "20.0";
3995 
3996             /*
3997               Dissolve the two images a given percent.
3998             */
3999             (void) XSetFunction(display,windows->image.highlight_context,
4000               GXcopy);
4001             (void) XDialogWidget(display,windows,"Dissolve",
4002               "Enter the blend factor (0.0 - 99.9%):",factor);
4003             (void) XSetFunction(display,windows->image.highlight_context,
4004               GXinvert);
4005             if (*factor == '\0')
4006               break;
4007             blend=StringToDouble(factor,(char **) NULL);
4008             compose=DissolveCompositeOp;
4009             break;
4010           }
4011           case CompositeDisplaceCommand:
4012           {
4013             /*
4014               Get horizontal and vertical scale displacement geometry.
4015             */
4016             (void) XSetFunction(display,windows->image.highlight_context,
4017               GXcopy);
4018             (void) XDialogWidget(display,windows,"Displace",
4019               "Enter the horizontal and vertical scale:",displacement_geometry);
4020             (void) XSetFunction(display,windows->image.highlight_context,
4021               GXinvert);
4022             if (*displacement_geometry == '\0')
4023               break;
4024             compose=DisplaceCompositeOp;
4025             break;
4026           }
4027           case CompositeHelpCommand:
4028           {
4029             (void) XSetFunction(display,windows->image.highlight_context,
4030               GXcopy);
4031             XTextViewHelp(display,resource_info,windows,MagickFalse,
4032               "Help Viewer - Image Composite",ImageCompositeHelp);
4033             (void) XSetFunction(display,windows->image.highlight_context,
4034               GXinvert);
4035             break;
4036           }
4037           case CompositeDismissCommand:
4038           {
4039             /*
4040               Prematurely exit.
4041             */
4042             state|=EscapeState;
4043             state|=ExitState;
4044             break;
4045           }
4046           default:
4047             break;
4048         }
4049         continue;
4050       }
4051     switch (event.type)
4052     {
4053       case ButtonPress:
4054       {
4055         if (image->debug != MagickFalse)
4056           (void) LogMagickEvent(X11Event,GetMagickModule(),
4057             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4058             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4059         if (event.xbutton.button != Button1)
4060           break;
4061         if (event.xbutton.window != windows->image.id)
4062           break;
4063         /*
4064           Change cursor.
4065         */
4066         composite_info.width=composite_image->columns;
4067         composite_info.height=composite_image->rows;
4068         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4069         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4070         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4071         break;
4072       }
4073       case ButtonRelease:
4074       {
4075         if (image->debug != MagickFalse)
4076           (void) LogMagickEvent(X11Event,GetMagickModule(),
4077             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4078             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4079         if (event.xbutton.button != Button1)
4080           break;
4081         if (event.xbutton.window != windows->image.id)
4082           break;
4083         if ((composite_info.width != 0) && (composite_info.height != 0))
4084           {
4085             /*
4086               User has selected the location of the composite image.
4087             */
4088             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4089             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4090             state|=ExitState;
4091           }
4092         break;
4093       }
4094       case Expose:
4095         break;
4096       case KeyPress:
4097       {
4098         char
4099           command[MaxTextExtent];
4100 
4101         KeySym
4102           key_symbol;
4103 
4104         int
4105           length;
4106 
4107         if (event.xkey.window != windows->image.id)
4108           break;
4109         /*
4110           Respond to a user key press.
4111         */
4112         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4113           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4114         *(command+length)='\0';
4115         if (image->debug != MagickFalse)
4116           (void) LogMagickEvent(X11Event,GetMagickModule(),
4117             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4118         switch ((int) key_symbol)
4119         {
4120           case XK_Escape:
4121           case XK_F20:
4122           {
4123             /*
4124               Prematurely exit.
4125             */
4126             composite_image=DestroyImage(composite_image);
4127             state|=EscapeState;
4128             state|=ExitState;
4129             break;
4130           }
4131           case XK_F1:
4132           case XK_Help:
4133           {
4134             (void) XSetFunction(display,windows->image.highlight_context,
4135               GXcopy);
4136             XTextViewHelp(display,resource_info,windows,MagickFalse,
4137               "Help Viewer - Image Composite",ImageCompositeHelp);
4138             (void) XSetFunction(display,windows->image.highlight_context,
4139               GXinvert);
4140             break;
4141           }
4142           default:
4143           {
4144             (void) XBell(display,0);
4145             break;
4146           }
4147         }
4148         break;
4149       }
4150       case MotionNotify:
4151       {
4152         /*
4153           Map and unmap Info widget as text cursor crosses its boundaries.
4154         */
4155         x=event.xmotion.x;
4156         y=event.xmotion.y;
4157         if (windows->info.mapped != MagickFalse)
4158           {
4159             if ((x < (int) (windows->info.x+windows->info.width)) &&
4160                 (y < (int) (windows->info.y+windows->info.height)))
4161               (void) XWithdrawWindow(display,windows->info.id,
4162                 windows->info.screen);
4163           }
4164         else
4165           if ((x > (int) (windows->info.x+windows->info.width)) ||
4166               (y > (int) (windows->info.y+windows->info.height)))
4167             (void) XMapWindow(display,windows->info.id);
4168         composite_info.x=(ssize_t) windows->image.x+x;
4169         composite_info.y=(ssize_t) windows->image.y+y;
4170         break;
4171       }
4172       default:
4173       {
4174         if (image->debug != MagickFalse)
4175           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4176             event.type);
4177         break;
4178       }
4179     }
4180   } while ((state & ExitState) == 0);
4181   (void) XSelectInput(display,windows->image.id,
4182     windows->image.attributes.event_mask);
4183   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4184   XSetCursorState(display,windows,MagickFalse);
4185   (void) XFreeCursor(display,cursor);
4186   if ((state & EscapeState) != 0)
4187     return(MagickTrue);
4188   /*
4189     Image compositing is relative to image configuration.
4190   */
4191   XSetCursorState(display,windows,MagickTrue);
4192   XCheckRefreshWindows(display,windows);
4193   width=(unsigned int) image->columns;
4194   height=(unsigned int) image->rows;
4195   x=0;
4196   y=0;
4197   if (windows->image.crop_geometry != (char *) NULL)
4198     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4199   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4200   composite_info.x+=x;
4201   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4202   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4203   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4204   composite_info.y+=y;
4205   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4206   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4207   if ((composite_info.width != composite_image->columns) ||
4208       (composite_info.height != composite_image->rows))
4209     {
4210       Image
4211         *resize_image;
4212 
4213       /*
4214         Scale composite image.
4215       */
4216       resize_image=ResizeImage(composite_image,composite_info.width,
4217         composite_info.height,composite_image->filter,composite_image->blur,
4218         &image->exception);
4219       composite_image=DestroyImage(composite_image);
4220       if (resize_image == (Image *) NULL)
4221         {
4222           XSetCursorState(display,windows,MagickFalse);
4223           return(MagickFalse);
4224         }
4225       composite_image=resize_image;
4226     }
4227   if (compose == DisplaceCompositeOp)
4228     (void) SetImageArtifact(composite_image,"compose:args",
4229       displacement_geometry);
4230   if (blend != 0.0)
4231     {
4232       CacheView
4233         *image_view;
4234 
4235       ExceptionInfo
4236         *exception;
4237 
4238       int
4239         y;
4240 
4241       Quantum
4242         opacity;
4243 
4244       int
4245         x;
4246 
4247       PixelPacket
4248         *q;
4249 
4250       /*
4251         Create mattes for blending.
4252       */
4253       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4254       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4255         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4256       if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4257         return(MagickFalse);
4258       image->matte=MagickTrue;
4259       exception=(&image->exception);
4260       image_view=AcquireAuthenticCacheView(image,exception);
4261       for (y=0; y < (int) image->rows; y++)
4262       {
4263         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4264           exception);
4265         if (q == (PixelPacket *) NULL)
4266           break;
4267         for (x=0; x < (int) image->columns; x++)
4268         {
4269           q->opacity=opacity;
4270           q++;
4271         }
4272         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4273           break;
4274       }
4275       image_view=DestroyCacheView(image_view);
4276     }
4277   /*
4278     Composite image with X Image window.
4279   */
4280   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4281     composite_info.y);
4282   composite_image=DestroyImage(composite_image);
4283   XSetCursorState(display,windows,MagickFalse);
4284   /*
4285     Update image configuration.
4286   */
4287   XConfigureImageColormap(display,resource_info,windows,image);
4288   (void) XConfigureImage(display,resource_info,windows,image);
4289   return(MagickTrue);
4290 }
4291 
4292 /*
4293 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4294 %                                                                             %
4295 %                                                                             %
4296 %                                                                             %
4297 +   X C o n f i g u r e I m a g e                                             %
4298 %                                                                             %
4299 %                                                                             %
4300 %                                                                             %
4301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4302 %
4303 %  XConfigureImage() creates a new X image.  It also notifies the window
4304 %  manager of the new image size and configures the transient widows.
4305 %
4306 %  The format of the XConfigureImage method is:
4307 %
4308 %      MagickBooleanType XConfigureImage(Display *display,
4309 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
4310 %
4311 %  A description of each parameter follows:
4312 %
4313 %    o display: Specifies a connection to an X server; returned from
4314 %      XOpenDisplay.
4315 %
4316 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4317 %
4318 %    o windows: Specifies a pointer to a XWindows structure.
4319 %
4320 %    o image: the image.
4321 %
4322 %
4323 */
XConfigureImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image)4324 static MagickBooleanType XConfigureImage(Display *display,
4325   XResourceInfo *resource_info,XWindows *windows,Image *image)
4326 {
4327   char
4328     geometry[MaxTextExtent];
4329 
4330   MagickStatusType
4331     status;
4332 
4333   size_t
4334     mask,
4335     height,
4336     width;
4337 
4338   ssize_t
4339     x,
4340     y;
4341 
4342   XSizeHints
4343     *size_hints;
4344 
4345   XWindowChanges
4346     window_changes;
4347 
4348   /*
4349     Dismiss if window dimensions are zero.
4350   */
4351   width=(unsigned int) windows->image.window_changes.width;
4352   height=(unsigned int) windows->image.window_changes.height;
4353   if (image->debug != MagickFalse)
4354     (void) LogMagickEvent(X11Event,GetMagickModule(),
4355       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4356       windows->image.ximage->height,(double) width,(double) height);
4357   if ((width*height) == 0)
4358     return(MagickTrue);
4359   x=0;
4360   y=0;
4361   /*
4362     Resize image to fit Image window dimensions.
4363   */
4364   XSetCursorState(display,windows,MagickTrue);
4365   (void) XFlush(display);
4366   if (((int) width != windows->image.ximage->width) ||
4367       ((int) height != windows->image.ximage->height))
4368     image->taint=MagickTrue;
4369   windows->magnify.x=(int)
4370     width*windows->magnify.x/windows->image.ximage->width;
4371   windows->magnify.y=(int)
4372     height*windows->magnify.y/windows->image.ximage->height;
4373   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4374   windows->image.y=(int)
4375     (height*windows->image.y/windows->image.ximage->height);
4376   status=XMakeImage(display,resource_info,&windows->image,image,
4377     (unsigned int) width,(unsigned int) height);
4378   if (status == MagickFalse)
4379     XNoticeWidget(display,windows,"Unable to configure X image:",
4380       windows->image.name);
4381   /*
4382     Notify window manager of the new configuration.
4383   */
4384   if (resource_info->image_geometry != (char *) NULL)
4385     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4386       resource_info->image_geometry);
4387   else
4388     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4389       XDisplayWidth(display,windows->image.screen),
4390       XDisplayHeight(display,windows->image.screen));
4391   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4392   window_changes.width=(int) width;
4393   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4394     window_changes.width=XDisplayWidth(display,windows->image.screen);
4395   window_changes.height=(int) height;
4396   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4397     window_changes.height=XDisplayHeight(display,windows->image.screen);
4398   mask=(size_t) (CWWidth | CWHeight);
4399   if (resource_info->backdrop)
4400     {
4401       mask|=CWX | CWY;
4402       window_changes.x=(int)
4403         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4404       window_changes.y=(int)
4405         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4406     }
4407   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4408     (unsigned int) mask,&window_changes);
4409   (void) XClearWindow(display,windows->image.id);
4410   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4411   /*
4412     Update Magnify window configuration.
4413   */
4414   if (windows->magnify.mapped != MagickFalse)
4415     XMakeMagnifyImage(display,windows);
4416   windows->pan.crop_geometry=windows->image.crop_geometry;
4417   XBestIconSize(display,&windows->pan,image);
4418   while (((windows->pan.width << 1) < MaxIconSize) &&
4419          ((windows->pan.height << 1) < MaxIconSize))
4420   {
4421     windows->pan.width<<=1;
4422     windows->pan.height<<=1;
4423   }
4424   if (windows->pan.geometry != (char *) NULL)
4425     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4426       &windows->pan.width,&windows->pan.height);
4427   window_changes.width=(int) windows->pan.width;
4428   window_changes.height=(int) windows->pan.height;
4429   size_hints=XAllocSizeHints();
4430   if (size_hints != (XSizeHints *) NULL)
4431     {
4432       /*
4433         Set new size hints.
4434       */
4435       size_hints->flags=PSize | PMinSize | PMaxSize;
4436       size_hints->width=window_changes.width;
4437       size_hints->height=window_changes.height;
4438       size_hints->min_width=size_hints->width;
4439       size_hints->min_height=size_hints->height;
4440       size_hints->max_width=size_hints->width;
4441       size_hints->max_height=size_hints->height;
4442       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4443       (void) XFree((void *) size_hints);
4444     }
4445   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4446     (unsigned int) (CWWidth | CWHeight),&window_changes);
4447   /*
4448     Update icon window configuration.
4449   */
4450   windows->icon.crop_geometry=windows->image.crop_geometry;
4451   XBestIconSize(display,&windows->icon,image);
4452   window_changes.width=(int) windows->icon.width;
4453   window_changes.height=(int) windows->icon.height;
4454   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4455     (unsigned int) (CWWidth | CWHeight),&window_changes);
4456   XSetCursorState(display,windows,MagickFalse);
4457   return(status != 0 ? MagickTrue : MagickFalse);
4458 }
4459 
4460 /*
4461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4462 %                                                                             %
4463 %                                                                             %
4464 %                                                                             %
4465 +   X C r o p I m a g e                                                       %
4466 %                                                                             %
4467 %                                                                             %
4468 %                                                                             %
4469 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4470 %
4471 %  XCropImage() allows the user to select a region of the image and crop, copy,
4472 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4473 %  the image with XPasteImage.
4474 %
4475 %  The format of the XCropImage method is:
4476 %
4477 %      MagickBooleanType XCropImage(Display *display,
4478 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4479 %        const ClipboardMode mode)
4480 %
4481 %  A description of each parameter follows:
4482 %
4483 %    o display: Specifies a connection to an X server; returned from
4484 %      XOpenDisplay.
4485 %
4486 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4487 %
4488 %    o windows: Specifies a pointer to a XWindows structure.
4489 %
4490 %    o image: the image; returned from ReadImage.
4491 %
4492 %    o mode: This unsigned value specified whether the image should be
4493 %      cropped, copied, or cut.
4494 %
4495 */
XCropImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,const ClipboardMode mode)4496 static MagickBooleanType XCropImage(Display *display,
4497   XResourceInfo *resource_info,XWindows *windows,Image *image,
4498   const ClipboardMode mode)
4499 {
4500   static const char
4501     *CropModeMenu[] =
4502     {
4503       "Help",
4504       "Dismiss",
4505       (char *) NULL
4506     },
4507     *RectifyModeMenu[] =
4508     {
4509       "Crop",
4510       "Help",
4511       "Dismiss",
4512       (char *) NULL
4513     };
4514 
4515   static const ModeType
4516     CropCommands[] =
4517     {
4518       CropHelpCommand,
4519       CropDismissCommand
4520     },
4521     RectifyCommands[] =
4522     {
4523       RectifyCopyCommand,
4524       RectifyHelpCommand,
4525       RectifyDismissCommand
4526     };
4527 
4528   CacheView
4529     *image_view;
4530 
4531   char
4532     command[MaxTextExtent],
4533     text[MaxTextExtent];
4534 
4535   Cursor
4536     cursor;
4537 
4538   ExceptionInfo
4539     *exception;
4540 
4541   int
4542     id,
4543     x,
4544     y;
4545 
4546   KeySym
4547     key_symbol;
4548 
4549   Image
4550     *crop_image;
4551 
4552   MagickRealType
4553     scale_factor;
4554 
4555   RectangleInfo
4556     crop_info,
4557     highlight_info;
4558 
4559   PixelPacket
4560     *q;
4561 
4562   unsigned int
4563     height,
4564     width;
4565 
4566   size_t
4567     state;
4568 
4569   XEvent
4570     event;
4571 
4572   /*
4573     Map Command widget.
4574   */
4575   switch (mode)
4576   {
4577     case CopyMode:
4578     {
4579       (void) CloneString(&windows->command.name,"Copy");
4580       break;
4581     }
4582     case CropMode:
4583     {
4584       (void) CloneString(&windows->command.name,"Crop");
4585       break;
4586     }
4587     case CutMode:
4588     {
4589       (void) CloneString(&windows->command.name,"Cut");
4590       break;
4591     }
4592   }
4593   RectifyModeMenu[0]=windows->command.name;
4594   windows->command.data=0;
4595   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4596   (void) XMapRaised(display,windows->command.id);
4597   XClientMessage(display,windows->image.id,windows->im_protocols,
4598     windows->im_update_widget,CurrentTime);
4599   /*
4600     Track pointer until button 1 is pressed.
4601   */
4602   XQueryPosition(display,windows->image.id,&x,&y);
4603   (void) XSelectInput(display,windows->image.id,
4604     windows->image.attributes.event_mask | PointerMotionMask);
4605   crop_info.x=(ssize_t) windows->image.x+x;
4606   crop_info.y=(ssize_t) windows->image.y+y;
4607   crop_info.width=0;
4608   crop_info.height=0;
4609   cursor=XCreateFontCursor(display,XC_fleur);
4610   state=DefaultState;
4611   do
4612   {
4613     if (windows->info.mapped != MagickFalse)
4614       {
4615         /*
4616           Display pointer position.
4617         */
4618         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4619           (long) crop_info.x,(long) crop_info.y);
4620         XInfoWidget(display,windows,text);
4621       }
4622     /*
4623       Wait for next event.
4624     */
4625     XScreenEvent(display,windows,&event);
4626     if (event.xany.window == windows->command.id)
4627       {
4628         /*
4629           Select a command from the Command widget.
4630         */
4631         id=XCommandWidget(display,windows,CropModeMenu,&event);
4632         if (id < 0)
4633           continue;
4634         switch (CropCommands[id])
4635         {
4636           case CropHelpCommand:
4637           {
4638             switch (mode)
4639             {
4640               case CopyMode:
4641               {
4642                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4643                   "Help Viewer - Image Copy",ImageCopyHelp);
4644                 break;
4645               }
4646               case CropMode:
4647               {
4648                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4649                   "Help Viewer - Image Crop",ImageCropHelp);
4650                 break;
4651               }
4652               case CutMode:
4653               {
4654                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4655                   "Help Viewer - Image Cut",ImageCutHelp);
4656                 break;
4657               }
4658             }
4659             break;
4660           }
4661           case CropDismissCommand:
4662           {
4663             /*
4664               Prematurely exit.
4665             */
4666             state|=EscapeState;
4667             state|=ExitState;
4668             break;
4669           }
4670           default:
4671             break;
4672         }
4673         continue;
4674       }
4675     switch (event.type)
4676     {
4677       case ButtonPress:
4678       {
4679         if (event.xbutton.button != Button1)
4680           break;
4681         if (event.xbutton.window != windows->image.id)
4682           break;
4683         /*
4684           Note first corner of cropping rectangle-- exit loop.
4685         */
4686         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4687         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4688         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4689         state|=ExitState;
4690         break;
4691       }
4692       case ButtonRelease:
4693         break;
4694       case Expose:
4695         break;
4696       case KeyPress:
4697       {
4698         if (event.xkey.window != windows->image.id)
4699           break;
4700         /*
4701           Respond to a user key press.
4702         */
4703         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4704           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4705         switch ((int) key_symbol)
4706         {
4707           case XK_Escape:
4708           case XK_F20:
4709           {
4710             /*
4711               Prematurely exit.
4712             */
4713             state|=EscapeState;
4714             state|=ExitState;
4715             break;
4716           }
4717           case XK_F1:
4718           case XK_Help:
4719           {
4720             switch (mode)
4721             {
4722               case CopyMode:
4723               {
4724                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4725                   "Help Viewer - Image Copy",ImageCopyHelp);
4726                 break;
4727               }
4728               case CropMode:
4729               {
4730                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4731                   "Help Viewer - Image Crop",ImageCropHelp);
4732                 break;
4733               }
4734               case CutMode:
4735               {
4736                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4737                   "Help Viewer - Image Cut",ImageCutHelp);
4738                 break;
4739               }
4740             }
4741             break;
4742           }
4743           default:
4744           {
4745             (void) XBell(display,0);
4746             break;
4747           }
4748         }
4749         break;
4750       }
4751       case MotionNotify:
4752       {
4753         if (event.xmotion.window != windows->image.id)
4754           break;
4755         /*
4756           Map and unmap Info widget as text cursor crosses its boundaries.
4757         */
4758         x=event.xmotion.x;
4759         y=event.xmotion.y;
4760         if (windows->info.mapped != MagickFalse)
4761           {
4762             if ((x < (int) (windows->info.x+windows->info.width)) &&
4763                 (y < (int) (windows->info.y+windows->info.height)))
4764               (void) XWithdrawWindow(display,windows->info.id,
4765                 windows->info.screen);
4766           }
4767         else
4768           if ((x > (int) (windows->info.x+windows->info.width)) ||
4769               (y > (int) (windows->info.y+windows->info.height)))
4770             (void) XMapWindow(display,windows->info.id);
4771         crop_info.x=(ssize_t) windows->image.x+x;
4772         crop_info.y=(ssize_t) windows->image.y+y;
4773         break;
4774       }
4775       default:
4776         break;
4777     }
4778   } while ((state & ExitState) == 0);
4779   (void) XSelectInput(display,windows->image.id,
4780     windows->image.attributes.event_mask);
4781   if ((state & EscapeState) != 0)
4782     {
4783       /*
4784         User want to exit without cropping.
4785       */
4786       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4787       (void) XFreeCursor(display,cursor);
4788       return(MagickTrue);
4789     }
4790   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4791   do
4792   {
4793     /*
4794       Size rectangle as pointer moves until the mouse button is released.
4795     */
4796     x=(int) crop_info.x;
4797     y=(int) crop_info.y;
4798     crop_info.width=0;
4799     crop_info.height=0;
4800     state=DefaultState;
4801     do
4802     {
4803       highlight_info=crop_info;
4804       highlight_info.x=crop_info.x-windows->image.x;
4805       highlight_info.y=crop_info.y-windows->image.y;
4806       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4807         {
4808           /*
4809             Display info and draw cropping rectangle.
4810           */
4811           if (windows->info.mapped == MagickFalse)
4812             (void) XMapWindow(display,windows->info.id);
4813           (void) FormatLocaleString(text,MaxTextExtent,
4814             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4815             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4816           XInfoWidget(display,windows,text);
4817           XHighlightRectangle(display,windows->image.id,
4818             windows->image.highlight_context,&highlight_info);
4819         }
4820       else
4821         if (windows->info.mapped != MagickFalse)
4822           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4823       /*
4824         Wait for next event.
4825       */
4826       XScreenEvent(display,windows,&event);
4827       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4828         XHighlightRectangle(display,windows->image.id,
4829           windows->image.highlight_context,&highlight_info);
4830       switch (event.type)
4831       {
4832         case ButtonPress:
4833         {
4834           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4835           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4836           break;
4837         }
4838         case ButtonRelease:
4839         {
4840           /*
4841             User has committed to cropping rectangle.
4842           */
4843           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4844           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4845           XSetCursorState(display,windows,MagickFalse);
4846           state|=ExitState;
4847           windows->command.data=0;
4848           (void) XCommandWidget(display,windows,RectifyModeMenu,
4849             (XEvent *) NULL);
4850           break;
4851         }
4852         case Expose:
4853           break;
4854         case MotionNotify:
4855         {
4856           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4857           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4858         }
4859         default:
4860           break;
4861       }
4862       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4863           ((state & ExitState) != 0))
4864         {
4865           /*
4866             Check boundary conditions.
4867           */
4868           if (crop_info.x < 0)
4869             crop_info.x=0;
4870           else
4871             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4872               crop_info.x=(ssize_t) windows->image.ximage->width;
4873           if ((int) crop_info.x < x)
4874             crop_info.width=(unsigned int) (x-crop_info.x);
4875           else
4876             {
4877               crop_info.width=(unsigned int) (crop_info.x-x);
4878               crop_info.x=(ssize_t) x;
4879             }
4880           if (crop_info.y < 0)
4881             crop_info.y=0;
4882           else
4883             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4884               crop_info.y=(ssize_t) windows->image.ximage->height;
4885           if ((int) crop_info.y < y)
4886             crop_info.height=(unsigned int) (y-crop_info.y);
4887           else
4888             {
4889               crop_info.height=(unsigned int) (crop_info.y-y);
4890               crop_info.y=(ssize_t) y;
4891             }
4892         }
4893     } while ((state & ExitState) == 0);
4894     /*
4895       Wait for user to grab a corner of the rectangle or press return.
4896     */
4897     state=DefaultState;
4898     (void) XMapWindow(display,windows->info.id);
4899     do
4900     {
4901       if (windows->info.mapped != MagickFalse)
4902         {
4903           /*
4904             Display pointer position.
4905           */
4906           (void) FormatLocaleString(text,MaxTextExtent,
4907             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4908             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4909           XInfoWidget(display,windows,text);
4910         }
4911       highlight_info=crop_info;
4912       highlight_info.x=crop_info.x-windows->image.x;
4913       highlight_info.y=crop_info.y-windows->image.y;
4914       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4915         {
4916           state|=EscapeState;
4917           state|=ExitState;
4918           break;
4919         }
4920       XHighlightRectangle(display,windows->image.id,
4921         windows->image.highlight_context,&highlight_info);
4922       XScreenEvent(display,windows,&event);
4923       if (event.xany.window == windows->command.id)
4924         {
4925           /*
4926             Select a command from the Command widget.
4927           */
4928           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4929           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4930           (void) XSetFunction(display,windows->image.highlight_context,
4931             GXinvert);
4932           XHighlightRectangle(display,windows->image.id,
4933             windows->image.highlight_context,&highlight_info);
4934           if (id >= 0)
4935             switch (RectifyCommands[id])
4936             {
4937               case RectifyCopyCommand:
4938               {
4939                 state|=ExitState;
4940                 break;
4941               }
4942               case RectifyHelpCommand:
4943               {
4944                 (void) XSetFunction(display,windows->image.highlight_context,
4945                   GXcopy);
4946                 switch (mode)
4947                 {
4948                   case CopyMode:
4949                   {
4950                     XTextViewHelp(display,resource_info,windows,MagickFalse,
4951                       "Help Viewer - Image Copy",ImageCopyHelp);
4952                     break;
4953                   }
4954                   case CropMode:
4955                   {
4956                     XTextViewHelp(display,resource_info,windows,MagickFalse,
4957                       "Help Viewer - Image Crop",ImageCropHelp);
4958                     break;
4959                   }
4960                   case CutMode:
4961                   {
4962                     XTextViewHelp(display,resource_info,windows,MagickFalse,
4963                       "Help Viewer - Image Cut",ImageCutHelp);
4964                     break;
4965                   }
4966                 }
4967                 (void) XSetFunction(display,windows->image.highlight_context,
4968                   GXinvert);
4969                 break;
4970               }
4971               case RectifyDismissCommand:
4972               {
4973                 /*
4974                   Prematurely exit.
4975                 */
4976                 state|=EscapeState;
4977                 state|=ExitState;
4978                 break;
4979               }
4980               default:
4981                 break;
4982             }
4983           continue;
4984         }
4985       XHighlightRectangle(display,windows->image.id,
4986         windows->image.highlight_context,&highlight_info);
4987       switch (event.type)
4988       {
4989         case ButtonPress:
4990         {
4991           if (event.xbutton.button != Button1)
4992             break;
4993           if (event.xbutton.window != windows->image.id)
4994             break;
4995           x=windows->image.x+event.xbutton.x;
4996           y=windows->image.y+event.xbutton.y;
4997           if ((x < (int) (crop_info.x+RoiDelta)) &&
4998               (x > (int) (crop_info.x-RoiDelta)) &&
4999               (y < (int) (crop_info.y+RoiDelta)) &&
5000               (y > (int) (crop_info.y-RoiDelta)))
5001             {
5002               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5003               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5004               state|=UpdateConfigurationState;
5005               break;
5006             }
5007           if ((x < (int) (crop_info.x+RoiDelta)) &&
5008               (x > (int) (crop_info.x-RoiDelta)) &&
5009               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5010               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5011             {
5012               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5013               state|=UpdateConfigurationState;
5014               break;
5015             }
5016           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5017               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5018               (y < (int) (crop_info.y+RoiDelta)) &&
5019               (y > (int) (crop_info.y-RoiDelta)))
5020             {
5021               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5022               state|=UpdateConfigurationState;
5023               break;
5024             }
5025           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5026               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5027               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5028               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5029             {
5030               state|=UpdateConfigurationState;
5031               break;
5032             }
5033         }
5034         case ButtonRelease:
5035         {
5036           if (event.xbutton.window == windows->pan.id)
5037             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5038                 (highlight_info.y != crop_info.y-windows->image.y))
5039               XHighlightRectangle(display,windows->image.id,
5040                 windows->image.highlight_context,&highlight_info);
5041           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5042             event.xbutton.time);
5043           break;
5044         }
5045         case Expose:
5046         {
5047           if (event.xexpose.window == windows->image.id)
5048             if (event.xexpose.count == 0)
5049               {
5050                 event.xexpose.x=(int) highlight_info.x;
5051                 event.xexpose.y=(int) highlight_info.y;
5052                 event.xexpose.width=(int) highlight_info.width;
5053                 event.xexpose.height=(int) highlight_info.height;
5054                 XRefreshWindow(display,&windows->image,&event);
5055               }
5056           if (event.xexpose.window == windows->info.id)
5057             if (event.xexpose.count == 0)
5058               XInfoWidget(display,windows,text);
5059           break;
5060         }
5061         case KeyPress:
5062         {
5063           if (event.xkey.window != windows->image.id)
5064             break;
5065           /*
5066             Respond to a user key press.
5067           */
5068           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5069             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5070           switch ((int) key_symbol)
5071           {
5072             case XK_Escape:
5073             case XK_F20:
5074               state|=EscapeState;
5075             case XK_Return:
5076             {
5077               state|=ExitState;
5078               break;
5079             }
5080             case XK_Home:
5081             case XK_KP_Home:
5082             {
5083               crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5084                 2L);
5085               crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5086                 2L);
5087               break;
5088             }
5089             case XK_Left:
5090             case XK_KP_Left:
5091             {
5092               crop_info.x--;
5093               break;
5094             }
5095             case XK_Up:
5096             case XK_KP_Up:
5097             case XK_Next:
5098             {
5099               crop_info.y--;
5100               break;
5101             }
5102             case XK_Right:
5103             case XK_KP_Right:
5104             {
5105               crop_info.x++;
5106               break;
5107             }
5108             case XK_Prior:
5109             case XK_Down:
5110             case XK_KP_Down:
5111             {
5112               crop_info.y++;
5113               break;
5114             }
5115             case XK_F1:
5116             case XK_Help:
5117             {
5118               (void) XSetFunction(display,windows->image.highlight_context,
5119                 GXcopy);
5120               switch (mode)
5121               {
5122                 case CopyMode:
5123                 {
5124                   XTextViewHelp(display,resource_info,windows,MagickFalse,
5125                     "Help Viewer - Image Copy",ImageCopyHelp);
5126                   break;
5127                 }
5128                 case CropMode:
5129                 {
5130                   XTextViewHelp(display,resource_info,windows,MagickFalse,
5131                     "Help Viewer - Image Cropg",ImageCropHelp);
5132                   break;
5133                 }
5134                 case CutMode:
5135                 {
5136                   XTextViewHelp(display,resource_info,windows,MagickFalse,
5137                     "Help Viewer - Image Cutg",ImageCutHelp);
5138                   break;
5139                 }
5140               }
5141               (void) XSetFunction(display,windows->image.highlight_context,
5142                 GXinvert);
5143               break;
5144             }
5145             default:
5146             {
5147               (void) XBell(display,0);
5148               break;
5149             }
5150           }
5151           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5152             event.xkey.time);
5153           break;
5154         }
5155         case KeyRelease:
5156           break;
5157         case MotionNotify:
5158         {
5159           if (event.xmotion.window != windows->image.id)
5160             break;
5161           /*
5162             Map and unmap Info widget as text cursor crosses its boundaries.
5163           */
5164           x=event.xmotion.x;
5165           y=event.xmotion.y;
5166           if (windows->info.mapped != MagickFalse)
5167             {
5168               if ((x < (int) (windows->info.x+windows->info.width)) &&
5169                   (y < (int) (windows->info.y+windows->info.height)))
5170                 (void) XWithdrawWindow(display,windows->info.id,
5171                   windows->info.screen);
5172             }
5173           else
5174             if ((x > (int) (windows->info.x+windows->info.width)) ||
5175                 (y > (int) (windows->info.y+windows->info.height)))
5176               (void) XMapWindow(display,windows->info.id);
5177           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5178           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5179           break;
5180         }
5181         case SelectionRequest:
5182         {
5183           XSelectionEvent
5184             notify;
5185 
5186           XSelectionRequestEvent
5187             *request;
5188 
5189           /*
5190             Set primary selection.
5191           */
5192           (void) FormatLocaleString(text,MaxTextExtent,
5193             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5194             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5195           request=(&(event.xselectionrequest));
5196           (void) XChangeProperty(request->display,request->requestor,
5197             request->property,request->target,8,PropModeReplace,
5198             (unsigned char *) text,(int) strlen(text));
5199           notify.type=SelectionNotify;
5200           notify.display=request->display;
5201           notify.requestor=request->requestor;
5202           notify.selection=request->selection;
5203           notify.target=request->target;
5204           notify.time=request->time;
5205           if (request->property == None)
5206             notify.property=request->target;
5207           else
5208             notify.property=request->property;
5209           (void) XSendEvent(request->display,request->requestor,False,0,
5210             (XEvent *) &notify);
5211         }
5212         default:
5213           break;
5214       }
5215       if ((state & UpdateConfigurationState) != 0)
5216         {
5217           (void) XPutBackEvent(display,&event);
5218           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5219           break;
5220         }
5221     } while ((state & ExitState) == 0);
5222   } while ((state & ExitState) == 0);
5223   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5224   XSetCursorState(display,windows,MagickFalse);
5225   if ((state & EscapeState) != 0)
5226     return(MagickTrue);
5227   if (mode == CropMode)
5228     if (((int) crop_info.width != windows->image.ximage->width) ||
5229         ((int) crop_info.height != windows->image.ximage->height))
5230       {
5231         /*
5232           Reconfigure Image window as defined by cropping rectangle.
5233         */
5234         XSetCropGeometry(display,windows,&crop_info,image);
5235         windows->image.window_changes.width=(int) crop_info.width;
5236         windows->image.window_changes.height=(int) crop_info.height;
5237         (void) XConfigureImage(display,resource_info,windows,image);
5238         return(MagickTrue);
5239       }
5240   /*
5241     Copy image before applying image transforms.
5242   */
5243   XSetCursorState(display,windows,MagickTrue);
5244   XCheckRefreshWindows(display,windows);
5245   width=(unsigned int) image->columns;
5246   height=(unsigned int) image->rows;
5247   x=0;
5248   y=0;
5249   if (windows->image.crop_geometry != (char *) NULL)
5250     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5251   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5252   crop_info.x+=x;
5253   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5254   crop_info.x+=image->page.x;
5255   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5256   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5257   crop_info.y+=y;
5258   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5259   crop_info.y+=image->page.y;
5260   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5261   crop_image=CropImage(image,&crop_info,&image->exception);
5262   XSetCursorState(display,windows,MagickFalse);
5263   if (crop_image == (Image *) NULL)
5264     return(MagickFalse);
5265   if (resource_info->copy_image != (Image *) NULL)
5266     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5267   resource_info->copy_image=crop_image;
5268   if (mode == CopyMode)
5269     {
5270       (void) XConfigureImage(display,resource_info,windows,image);
5271       return(MagickTrue);
5272     }
5273   /*
5274     Cut image.
5275   */
5276   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5277     return(MagickFalse);
5278   image->matte=MagickTrue;
5279   exception=(&image->exception);
5280   image_view=AcquireAuthenticCacheView(image,exception);
5281   for (y=0; y < (int) crop_info.height; y++)
5282   {
5283     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5284       crop_info.width,1,exception);
5285     if (q == (PixelPacket *) NULL)
5286       break;
5287     for (x=0; x < (int) crop_info.width; x++)
5288     {
5289       q->opacity=(Quantum) TransparentOpacity;
5290       q++;
5291     }
5292     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5293       break;
5294   }
5295   image_view=DestroyCacheView(image_view);
5296   /*
5297     Update image configuration.
5298   */
5299   XConfigureImageColormap(display,resource_info,windows,image);
5300   (void) XConfigureImage(display,resource_info,windows,image);
5301   return(MagickTrue);
5302 }
5303 
5304 /*
5305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5306 %                                                                             %
5307 %                                                                             %
5308 %                                                                             %
5309 +   X D r a w I m a g e                                                       %
5310 %                                                                             %
5311 %                                                                             %
5312 %                                                                             %
5313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5314 %
5315 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5316 %  the image.
5317 %
5318 %  The format of the XDrawEditImage method is:
5319 %
5320 %      MagickBooleanType XDrawEditImage(Display *display,
5321 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
5322 %
5323 %  A description of each parameter follows:
5324 %
5325 %    o display: Specifies a connection to an X server; returned from
5326 %      XOpenDisplay.
5327 %
5328 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5329 %
5330 %    o windows: Specifies a pointer to a XWindows structure.
5331 %
5332 %    o image: the image.
5333 %
5334 */
XDrawEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image)5335 static MagickBooleanType XDrawEditImage(Display *display,
5336   XResourceInfo *resource_info,XWindows *windows,Image **image)
5337 {
5338   const char
5339     *const DrawMenu[] =
5340     {
5341       "Element",
5342       "Color",
5343       "Stipple",
5344       "Width",
5345       "Undo",
5346       "Help",
5347       "Dismiss",
5348       (char *) NULL
5349     };
5350 
5351   static ElementType
5352     element = PointElement;
5353 
5354   static const ModeType
5355     DrawCommands[] =
5356     {
5357       DrawElementCommand,
5358       DrawColorCommand,
5359       DrawStippleCommand,
5360       DrawWidthCommand,
5361       DrawUndoCommand,
5362       DrawHelpCommand,
5363       DrawDismissCommand
5364     };
5365 
5366   static Pixmap
5367     stipple = (Pixmap) NULL;
5368 
5369   static unsigned int
5370     pen_id = 0,
5371     line_width = 1;
5372 
5373   char
5374     command[MaxTextExtent],
5375     text[MaxTextExtent];
5376 
5377   Cursor
5378     cursor;
5379 
5380   int
5381     entry,
5382     id,
5383     number_coordinates,
5384     x,
5385     y;
5386 
5387   MagickRealType
5388     degrees;
5389 
5390   MagickStatusType
5391     status;
5392 
5393   RectangleInfo
5394     rectangle_info;
5395 
5396   int
5397     i;
5398 
5399   unsigned int
5400     distance,
5401     height,
5402     max_coordinates,
5403     width;
5404 
5405   size_t
5406     state;
5407 
5408   Window
5409     root_window;
5410 
5411   XDrawInfo
5412     draw_info;
5413 
5414   XEvent
5415     event;
5416 
5417   XPoint
5418     *coordinate_info;
5419 
5420   XSegment
5421     line_info;
5422 
5423   /*
5424     Allocate polygon info.
5425   */
5426   max_coordinates=2048;
5427   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5428     sizeof(*coordinate_info));
5429   if (coordinate_info == (XPoint *) NULL)
5430     {
5431       (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5432         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5433       return(MagickFalse);
5434     }
5435   /*
5436     Map Command widget.
5437   */
5438   (void) CloneString(&windows->command.name,"Draw");
5439   windows->command.data=4;
5440   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5441   (void) XMapRaised(display,windows->command.id);
5442   XClientMessage(display,windows->image.id,windows->im_protocols,
5443     windows->im_update_widget,CurrentTime);
5444   /*
5445     Wait for first button press.
5446   */
5447   root_window=XRootWindow(display,XDefaultScreen(display));
5448   draw_info.stencil=OpaqueStencil;
5449   status=MagickTrue;
5450   cursor=XCreateFontCursor(display,XC_tcross);
5451   for ( ; ; )
5452   {
5453     XQueryPosition(display,windows->image.id,&x,&y);
5454     (void) XSelectInput(display,windows->image.id,
5455       windows->image.attributes.event_mask | PointerMotionMask);
5456     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5457     state=DefaultState;
5458     do
5459     {
5460       if (windows->info.mapped != MagickFalse)
5461         {
5462           /*
5463             Display pointer position.
5464           */
5465           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5466             x+windows->image.x,y+windows->image.y);
5467           XInfoWidget(display,windows,text);
5468         }
5469       /*
5470         Wait for next event.
5471       */
5472       XScreenEvent(display,windows,&event);
5473       if (event.xany.window == windows->command.id)
5474         {
5475           /*
5476             Select a command from the Command widget.
5477           */
5478           id=XCommandWidget(display,windows,DrawMenu,&event);
5479           if (id < 0)
5480             continue;
5481           switch (DrawCommands[id])
5482           {
5483             case DrawElementCommand:
5484             {
5485               const char
5486                 *const Elements[] =
5487                 {
5488                   "point",
5489                   "line",
5490                   "rectangle",
5491                   "fill rectangle",
5492                   "circle",
5493                   "fill circle",
5494                   "ellipse",
5495                   "fill ellipse",
5496                   "polygon",
5497                   "fill polygon",
5498                   (char *) NULL,
5499                 };
5500 
5501               /*
5502                 Select a command from the pop-up menu.
5503               */
5504               element=(ElementType) (XMenuWidget(display,windows,
5505                 DrawMenu[id],Elements,command)+1);
5506               break;
5507             }
5508             case DrawColorCommand:
5509             {
5510               const char
5511                 *ColorMenu[MaxNumberPens+1];
5512 
5513               int
5514                 pen_number;
5515 
5516               MagickBooleanType
5517                 transparent;
5518 
5519               XColor
5520                 color;
5521 
5522               /*
5523                 Initialize menu selections.
5524               */
5525               for (i=0; i < (int) (MaxNumberPens-2); i++)
5526                 ColorMenu[i]=resource_info->pen_colors[i];
5527               ColorMenu[MaxNumberPens-2]="transparent";
5528               ColorMenu[MaxNumberPens-1]="Browser...";
5529               ColorMenu[MaxNumberPens]=(char *) NULL;
5530               /*
5531                 Select a pen color from the pop-up menu.
5532               */
5533               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5534                 (const char **) ColorMenu,command);
5535               if (pen_number < 0)
5536                 break;
5537               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5538                 MagickFalse;
5539               if (transparent != MagickFalse)
5540                 {
5541                   draw_info.stencil=TransparentStencil;
5542                   break;
5543                 }
5544               if (pen_number == (MaxNumberPens-1))
5545                 {
5546                   static char
5547                     color_name[MaxTextExtent] = "gray";
5548 
5549                   /*
5550                     Select a pen color from a dialog.
5551                   */
5552                   resource_info->pen_colors[pen_number]=color_name;
5553                   XColorBrowserWidget(display,windows,"Select",color_name);
5554                   if (*color_name == '\0')
5555                     break;
5556                 }
5557               /*
5558                 Set pen color.
5559               */
5560               (void) XParseColor(display,windows->map_info->colormap,
5561                 resource_info->pen_colors[pen_number],&color);
5562               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5563                 (unsigned int) MaxColors,&color);
5564               windows->pixel_info->pen_colors[pen_number]=color;
5565               pen_id=(unsigned int) pen_number;
5566               draw_info.stencil=OpaqueStencil;
5567               break;
5568             }
5569             case DrawStippleCommand:
5570             {
5571               const char
5572                 *StipplesMenu[] =
5573                 {
5574                   "Brick",
5575                   "Diagonal",
5576                   "Scales",
5577                   "Vertical",
5578                   "Wavy",
5579                   "Translucent",
5580                   "Opaque",
5581                   (char *) NULL,
5582                   (char *) NULL,
5583                 };
5584 
5585               Image
5586                 *stipple_image;
5587 
5588               ImageInfo
5589                 *image_info;
5590 
5591               int
5592                 status;
5593 
5594               static char
5595                 filename[MaxTextExtent] = "\0";
5596 
5597               /*
5598                 Select a command from the pop-up menu.
5599               */
5600               StipplesMenu[7]="Open...";
5601               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5602                 command);
5603               if (entry < 0)
5604                 break;
5605               if (stipple != (Pixmap) NULL)
5606                 (void) XFreePixmap(display,stipple);
5607               stipple=(Pixmap) NULL;
5608               if (entry != 7)
5609                 {
5610                   switch (entry)
5611                   {
5612                     case 0:
5613                     {
5614                       stipple=XCreateBitmapFromData(display,root_window,
5615                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5616                       break;
5617                     }
5618                     case 1:
5619                     {
5620                       stipple=XCreateBitmapFromData(display,root_window,
5621                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5622                       break;
5623                     }
5624                     case 2:
5625                     {
5626                       stipple=XCreateBitmapFromData(display,root_window,
5627                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5628                       break;
5629                     }
5630                     case 3:
5631                     {
5632                       stipple=XCreateBitmapFromData(display,root_window,
5633                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5634                       break;
5635                     }
5636                     case 4:
5637                     {
5638                       stipple=XCreateBitmapFromData(display,root_window,
5639                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5640                       break;
5641                     }
5642                     case 5:
5643                     {
5644                       stipple=XCreateBitmapFromData(display,root_window,
5645                         (char *) HighlightBitmap,HighlightWidth,
5646                         HighlightHeight);
5647                       break;
5648                     }
5649                     case 6:
5650                     default:
5651                     {
5652                       stipple=XCreateBitmapFromData(display,root_window,
5653                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5654                       break;
5655                     }
5656                   }
5657                   break;
5658                 }
5659               XFileBrowserWidget(display,windows,"Stipple",filename);
5660               if (*filename == '\0')
5661                 break;
5662               /*
5663                 Read image.
5664               */
5665               XSetCursorState(display,windows,MagickTrue);
5666               XCheckRefreshWindows(display,windows);
5667               image_info=AcquireImageInfo();
5668               (void) CopyMagickString(image_info->filename,filename,
5669                 MaxTextExtent);
5670               stipple_image=ReadImage(image_info,&(*image)->exception);
5671               CatchException(&(*image)->exception);
5672               XSetCursorState(display,windows,MagickFalse);
5673               if (stipple_image == (Image *) NULL)
5674                 break;
5675               (void) AcquireUniqueFileResource(filename);
5676               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5677                 "xbm:%s",filename);
5678               (void) WriteImage(image_info,stipple_image);
5679               stipple_image=DestroyImage(stipple_image);
5680               image_info=DestroyImageInfo(image_info);
5681               status=XReadBitmapFile(display,root_window,filename,&width,
5682                 &height,&stipple,&x,&y);
5683               (void) RelinquishUniqueFileResource(filename);
5684               if ((status != BitmapSuccess) != 0)
5685                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5686                   filename);
5687               break;
5688             }
5689             case DrawWidthCommand:
5690             {
5691               const char
5692                 *const WidthsMenu[] =
5693                 {
5694                   "1",
5695                   "2",
5696                   "4",
5697                   "8",
5698                   "16",
5699                   "Dialog...",
5700                   (char *) NULL,
5701                 };
5702 
5703               static char
5704                 width[MaxTextExtent] = "0";
5705 
5706               /*
5707                 Select a command from the pop-up menu.
5708               */
5709               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5710                 command);
5711               if (entry < 0)
5712                 break;
5713               if (entry != 5)
5714                 {
5715                   line_width=(unsigned int) StringToUnsignedLong(
5716                     WidthsMenu[entry]);
5717                   break;
5718                 }
5719               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5720                 width);
5721               if (*width == '\0')
5722                 break;
5723               line_width=(unsigned int) StringToUnsignedLong(width);
5724               break;
5725             }
5726             case DrawUndoCommand:
5727             {
5728               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5729                 image);
5730               break;
5731             }
5732             case DrawHelpCommand:
5733             {
5734               XTextViewHelp(display,resource_info,windows,MagickFalse,
5735                 "Help Viewer - Image Rotation",ImageDrawHelp);
5736               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5737               break;
5738             }
5739             case DrawDismissCommand:
5740             {
5741               /*
5742                 Prematurely exit.
5743               */
5744               state|=EscapeState;
5745               state|=ExitState;
5746               break;
5747             }
5748             default:
5749               break;
5750           }
5751           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5752           continue;
5753         }
5754       switch (event.type)
5755       {
5756         case ButtonPress:
5757         {
5758           if (event.xbutton.button != Button1)
5759             break;
5760           if (event.xbutton.window != windows->image.id)
5761             break;
5762           /*
5763             exit loop.
5764           */
5765           x=event.xbutton.x;
5766           y=event.xbutton.y;
5767           state|=ExitState;
5768           break;
5769         }
5770         case ButtonRelease:
5771           break;
5772         case Expose:
5773           break;
5774         case KeyPress:
5775         {
5776           KeySym
5777             key_symbol;
5778 
5779           if (event.xkey.window != windows->image.id)
5780             break;
5781           /*
5782             Respond to a user key press.
5783           */
5784           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5785             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5786           switch ((int) key_symbol)
5787           {
5788             case XK_Escape:
5789             case XK_F20:
5790             {
5791               /*
5792                 Prematurely exit.
5793               */
5794               state|=EscapeState;
5795               state|=ExitState;
5796               break;
5797             }
5798             case XK_F1:
5799             case XK_Help:
5800             {
5801               XTextViewHelp(display,resource_info,windows,MagickFalse,
5802                 "Help Viewer - Image Rotation",ImageDrawHelp);
5803               break;
5804             }
5805             default:
5806             {
5807               (void) XBell(display,0);
5808               break;
5809             }
5810           }
5811           break;
5812         }
5813         case MotionNotify:
5814         {
5815           /*
5816             Map and unmap Info widget as text cursor crosses its boundaries.
5817           */
5818           x=event.xmotion.x;
5819           y=event.xmotion.y;
5820           if (windows->info.mapped != MagickFalse)
5821             {
5822               if ((x < (int) (windows->info.x+windows->info.width)) &&
5823                   (y < (int) (windows->info.y+windows->info.height)))
5824                 (void) XWithdrawWindow(display,windows->info.id,
5825                   windows->info.screen);
5826             }
5827           else
5828             if ((x > (int) (windows->info.x+windows->info.width)) ||
5829                 (y > (int) (windows->info.y+windows->info.height)))
5830               (void) XMapWindow(display,windows->info.id);
5831           break;
5832         }
5833       }
5834     } while ((state & ExitState) == 0);
5835     (void) XSelectInput(display,windows->image.id,
5836       windows->image.attributes.event_mask);
5837     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5838     if ((state & EscapeState) != 0)
5839       break;
5840     /*
5841       Draw element as pointer moves until the button is released.
5842     */
5843     distance=0;
5844     degrees=0.0;
5845     line_info.x1=x;
5846     line_info.y1=y;
5847     line_info.x2=x;
5848     line_info.y2=y;
5849     rectangle_info.x=(ssize_t) x;
5850     rectangle_info.y=(ssize_t) y;
5851     rectangle_info.width=0;
5852     rectangle_info.height=0;
5853     number_coordinates=1;
5854     coordinate_info->x=x;
5855     coordinate_info->y=y;
5856     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5857     state=DefaultState;
5858     do
5859     {
5860       switch (element)
5861       {
5862         case PointElement:
5863         default:
5864         {
5865           if (number_coordinates > 1)
5866             {
5867               (void) XDrawLines(display,windows->image.id,
5868                 windows->image.highlight_context,coordinate_info,
5869                 number_coordinates,CoordModeOrigin);
5870               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5871                 coordinate_info[number_coordinates-1].x,
5872                 coordinate_info[number_coordinates-1].y);
5873               XInfoWidget(display,windows,text);
5874             }
5875           break;
5876         }
5877         case LineElement:
5878         {
5879           if (distance > 9)
5880             {
5881               /*
5882                 Display angle of the line.
5883               */
5884               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5885                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5886               (void) FormatLocaleString(text,MaxTextExtent," %g",
5887                 (double) degrees);
5888               XInfoWidget(display,windows,text);
5889               XHighlightLine(display,windows->image.id,
5890                 windows->image.highlight_context,&line_info);
5891             }
5892           else
5893             if (windows->info.mapped != MagickFalse)
5894               (void) XWithdrawWindow(display,windows->info.id,
5895                 windows->info.screen);
5896           break;
5897         }
5898         case RectangleElement:
5899         case FillRectangleElement:
5900         {
5901           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5902             {
5903               /*
5904                 Display info and draw drawing rectangle.
5905               */
5906               (void) FormatLocaleString(text,MaxTextExtent,
5907                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5908                 (double) rectangle_info.height,(double) rectangle_info.x,
5909                 (double) rectangle_info.y);
5910               XInfoWidget(display,windows,text);
5911               XHighlightRectangle(display,windows->image.id,
5912                 windows->image.highlight_context,&rectangle_info);
5913             }
5914           else
5915             if (windows->info.mapped != MagickFalse)
5916               (void) XWithdrawWindow(display,windows->info.id,
5917                 windows->info.screen);
5918           break;
5919         }
5920         case CircleElement:
5921         case FillCircleElement:
5922         case EllipseElement:
5923         case FillEllipseElement:
5924         {
5925           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5926             {
5927               /*
5928                 Display info and draw drawing rectangle.
5929               */
5930               (void) FormatLocaleString(text,MaxTextExtent,
5931                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5932                 (double) rectangle_info.height,(double) rectangle_info.x,
5933                 (double) rectangle_info.y);
5934               XInfoWidget(display,windows,text);
5935               XHighlightEllipse(display,windows->image.id,
5936                 windows->image.highlight_context,&rectangle_info);
5937             }
5938           else
5939             if (windows->info.mapped != MagickFalse)
5940               (void) XWithdrawWindow(display,windows->info.id,
5941                 windows->info.screen);
5942           break;
5943         }
5944         case PolygonElement:
5945         case FillPolygonElement:
5946         {
5947           if (number_coordinates > 1)
5948             (void) XDrawLines(display,windows->image.id,
5949               windows->image.highlight_context,coordinate_info,
5950               number_coordinates,CoordModeOrigin);
5951           if (distance > 9)
5952             {
5953               /*
5954                 Display angle of the line.
5955               */
5956               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5957                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5958               (void) FormatLocaleString(text,MaxTextExtent," %g",
5959                 (double) degrees);
5960               XInfoWidget(display,windows,text);
5961               XHighlightLine(display,windows->image.id,
5962                 windows->image.highlight_context,&line_info);
5963             }
5964           else
5965             if (windows->info.mapped != MagickFalse)
5966               (void) XWithdrawWindow(display,windows->info.id,
5967                 windows->info.screen);
5968           break;
5969         }
5970       }
5971       /*
5972         Wait for next event.
5973       */
5974       XScreenEvent(display,windows,&event);
5975       switch (element)
5976       {
5977         case PointElement:
5978         default:
5979         {
5980           if (number_coordinates > 1)
5981             (void) XDrawLines(display,windows->image.id,
5982               windows->image.highlight_context,coordinate_info,
5983               number_coordinates,CoordModeOrigin);
5984           break;
5985         }
5986         case LineElement:
5987         {
5988           if (distance > 9)
5989             XHighlightLine(display,windows->image.id,
5990               windows->image.highlight_context,&line_info);
5991           break;
5992         }
5993         case RectangleElement:
5994         case FillRectangleElement:
5995         {
5996           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5997             XHighlightRectangle(display,windows->image.id,
5998               windows->image.highlight_context,&rectangle_info);
5999           break;
6000         }
6001         case CircleElement:
6002         case FillCircleElement:
6003         case EllipseElement:
6004         case FillEllipseElement:
6005         {
6006           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6007             XHighlightEllipse(display,windows->image.id,
6008               windows->image.highlight_context,&rectangle_info);
6009           break;
6010         }
6011         case PolygonElement:
6012         case FillPolygonElement:
6013         {
6014           if (number_coordinates > 1)
6015             (void) XDrawLines(display,windows->image.id,
6016               windows->image.highlight_context,coordinate_info,
6017               number_coordinates,CoordModeOrigin);
6018           if (distance > 9)
6019             XHighlightLine(display,windows->image.id,
6020               windows->image.highlight_context,&line_info);
6021           break;
6022         }
6023       }
6024       switch (event.type)
6025       {
6026         case ButtonPress:
6027           break;
6028         case ButtonRelease:
6029         {
6030           /*
6031             User has committed to element.
6032           */
6033           line_info.x2=event.xbutton.x;
6034           line_info.y2=event.xbutton.y;
6035           rectangle_info.x=(ssize_t) event.xbutton.x;
6036           rectangle_info.y=(ssize_t) event.xbutton.y;
6037           coordinate_info[number_coordinates].x=event.xbutton.x;
6038           coordinate_info[number_coordinates].y=event.xbutton.y;
6039           if (((element != PolygonElement) &&
6040                (element != FillPolygonElement)) || (distance <= 9))
6041             {
6042               state|=ExitState;
6043               break;
6044             }
6045           number_coordinates++;
6046           if (number_coordinates < (int) max_coordinates)
6047             {
6048               line_info.x1=event.xbutton.x;
6049               line_info.y1=event.xbutton.y;
6050               break;
6051             }
6052           max_coordinates<<=1;
6053           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6054             max_coordinates,sizeof(*coordinate_info));
6055           if (coordinate_info == (XPoint *) NULL)
6056             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6057               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6058           break;
6059         }
6060         case Expose:
6061           break;
6062         case MotionNotify:
6063         {
6064           if (event.xmotion.window != windows->image.id)
6065             break;
6066           if (element != PointElement)
6067             {
6068               line_info.x2=event.xmotion.x;
6069               line_info.y2=event.xmotion.y;
6070               rectangle_info.x=(ssize_t) event.xmotion.x;
6071               rectangle_info.y=(ssize_t) event.xmotion.y;
6072               break;
6073             }
6074           coordinate_info[number_coordinates].x=event.xbutton.x;
6075           coordinate_info[number_coordinates].y=event.xbutton.y;
6076           number_coordinates++;
6077           if (number_coordinates < (int) max_coordinates)
6078             break;
6079           max_coordinates<<=1;
6080           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6081             max_coordinates,sizeof(*coordinate_info));
6082           if (coordinate_info == (XPoint *) NULL)
6083             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6084               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6085           break;
6086         }
6087         default:
6088           break;
6089       }
6090       /*
6091         Check boundary conditions.
6092       */
6093       if (line_info.x2 < 0)
6094         line_info.x2=0;
6095       else
6096         if (line_info.x2 > (int) windows->image.width)
6097           line_info.x2=(short) windows->image.width;
6098       if (line_info.y2 < 0)
6099         line_info.y2=0;
6100       else
6101         if (line_info.y2 > (int) windows->image.height)
6102           line_info.y2=(short) windows->image.height;
6103       distance=(unsigned int)
6104         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6105          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6106       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6107           ((state & ExitState) != 0))
6108         {
6109           if (rectangle_info.x < 0)
6110             rectangle_info.x=0;
6111           else
6112             if (rectangle_info.x > (ssize_t) windows->image.width)
6113               rectangle_info.x=(ssize_t) windows->image.width;
6114           if ((int) rectangle_info.x < x)
6115             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6116           else
6117             {
6118               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6119               rectangle_info.x=(ssize_t) x;
6120             }
6121           if (rectangle_info.y < 0)
6122             rectangle_info.y=0;
6123           else
6124             if (rectangle_info.y > (ssize_t) windows->image.height)
6125               rectangle_info.y=(ssize_t) windows->image.height;
6126           if ((int) rectangle_info.y < y)
6127             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6128           else
6129             {
6130               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6131               rectangle_info.y=(ssize_t) y;
6132             }
6133         }
6134     } while ((state & ExitState) == 0);
6135     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6136     if ((element == PointElement) || (element == PolygonElement) ||
6137         (element == FillPolygonElement))
6138       {
6139         /*
6140           Determine polygon bounding box.
6141         */
6142         rectangle_info.x=(ssize_t) coordinate_info->x;
6143         rectangle_info.y=(ssize_t) coordinate_info->y;
6144         x=coordinate_info->x;
6145         y=coordinate_info->y;
6146         for (i=1; i < number_coordinates; i++)
6147         {
6148           if (coordinate_info[i].x > x)
6149             x=coordinate_info[i].x;
6150           if (coordinate_info[i].y > y)
6151             y=coordinate_info[i].y;
6152           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6153             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6154           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6155             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6156         }
6157         rectangle_info.width=(size_t) (x-rectangle_info.x);
6158         rectangle_info.height=(size_t) (y-rectangle_info.y);
6159         for (i=0; i < number_coordinates; i++)
6160         {
6161           coordinate_info[i].x-=rectangle_info.x;
6162           coordinate_info[i].y-=rectangle_info.y;
6163         }
6164       }
6165     else
6166       if (distance <= 9)
6167         continue;
6168       else
6169         if ((element == RectangleElement) ||
6170             (element == CircleElement) || (element == EllipseElement))
6171           {
6172             rectangle_info.width--;
6173             rectangle_info.height--;
6174           }
6175     /*
6176       Drawing is relative to image configuration.
6177     */
6178     draw_info.x=(int) rectangle_info.x;
6179     draw_info.y=(int) rectangle_info.y;
6180     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6181       image);
6182     width=(unsigned int) (*image)->columns;
6183     height=(unsigned int) (*image)->rows;
6184     x=0;
6185     y=0;
6186     if (windows->image.crop_geometry != (char *) NULL)
6187       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6188     draw_info.x+=windows->image.x-(line_width/2);
6189     if (draw_info.x < 0)
6190       draw_info.x=0;
6191     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6192     draw_info.y+=windows->image.y-(line_width/2);
6193     if (draw_info.y < 0)
6194       draw_info.y=0;
6195     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6196     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6197     if (draw_info.width > (unsigned int) (*image)->columns)
6198       draw_info.width=(unsigned int) (*image)->columns;
6199     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6200     if (draw_info.height > (unsigned int) (*image)->rows)
6201       draw_info.height=(unsigned int) (*image)->rows;
6202     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6203       width*draw_info.width/windows->image.ximage->width,
6204       height*draw_info.height/windows->image.ximage->height,
6205       draw_info.x+x,draw_info.y+y);
6206     /*
6207       Initialize drawing attributes.
6208     */
6209     draw_info.degrees=0.0;
6210     draw_info.element=element;
6211     draw_info.stipple=stipple;
6212     draw_info.line_width=line_width;
6213     draw_info.line_info=line_info;
6214     if (line_info.x1 > (int) (line_width/2))
6215       draw_info.line_info.x1=(short) line_width/2;
6216     if (line_info.y1 > (int) (line_width/2))
6217       draw_info.line_info.y1=(short) line_width/2;
6218     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6219     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6220     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6221       {
6222         draw_info.line_info.x2=(-draw_info.line_info.x2);
6223         draw_info.line_info.y2=(-draw_info.line_info.y2);
6224       }
6225     if (draw_info.line_info.x2 < 0)
6226       {
6227         draw_info.line_info.x2=(-draw_info.line_info.x2);
6228         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6229       }
6230     if (draw_info.line_info.y2 < 0)
6231       {
6232         draw_info.line_info.y2=(-draw_info.line_info.y2);
6233         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6234       }
6235     draw_info.rectangle_info=rectangle_info;
6236     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6237       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6238     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6239       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6240     draw_info.number_coordinates=(unsigned int) number_coordinates;
6241     draw_info.coordinate_info=coordinate_info;
6242     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6243     /*
6244       Draw element on image.
6245     */
6246     XSetCursorState(display,windows,MagickTrue);
6247     XCheckRefreshWindows(display,windows);
6248     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6249     XSetCursorState(display,windows,MagickFalse);
6250     /*
6251       Update image colormap and return to image drawing.
6252     */
6253     XConfigureImageColormap(display,resource_info,windows,*image);
6254     (void) XConfigureImage(display,resource_info,windows,*image);
6255   }
6256   XSetCursorState(display,windows,MagickFalse);
6257   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6258   return(status != 0 ? MagickTrue : MagickFalse);
6259 }
6260 
6261 /*
6262 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6263 %                                                                             %
6264 %                                                                             %
6265 %                                                                             %
6266 +   X D r a w P a n R e c t a n g l e                                         %
6267 %                                                                             %
6268 %                                                                             %
6269 %                                                                             %
6270 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6271 %
6272 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6273 %  displays a zoom image and the rectangle shows which portion of the image is
6274 %  displayed in the Image window.
6275 %
6276 %  The format of the XDrawPanRectangle method is:
6277 %
6278 %      XDrawPanRectangle(Display *display,XWindows *windows)
6279 %
6280 %  A description of each parameter follows:
6281 %
6282 %    o display: Specifies a connection to an X server;  returned from
6283 %      XOpenDisplay.
6284 %
6285 %    o windows: Specifies a pointer to a XWindows structure.
6286 %
6287 */
XDrawPanRectangle(Display * display,XWindows * windows)6288 static void XDrawPanRectangle(Display *display,XWindows *windows)
6289 {
6290   MagickRealType
6291     scale_factor;
6292 
6293   RectangleInfo
6294     highlight_info;
6295 
6296   /*
6297     Determine dimensions of the panning rectangle.
6298   */
6299   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6300   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6301   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6302   scale_factor=(MagickRealType)
6303     windows->pan.height/windows->image.ximage->height;
6304   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6305   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6306   /*
6307     Display the panning rectangle.
6308   */
6309   (void) XClearWindow(display,windows->pan.id);
6310   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6311     &highlight_info);
6312 }
6313 
6314 /*
6315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6316 %                                                                             %
6317 %                                                                             %
6318 %                                                                             %
6319 +   X I m a g e C a c h e                                                     %
6320 %                                                                             %
6321 %                                                                             %
6322 %                                                                             %
6323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6324 %
6325 %  XImageCache() handles the creation, manipulation, and destruction of the
6326 %  image cache (undo and redo buffers).
6327 %
6328 %  The format of the XImageCache method is:
6329 %
6330 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6331 %        XWindows *windows,const CommandType command,Image **image)
6332 %
6333 %  A description of each parameter follows:
6334 %
6335 %    o display: Specifies a connection to an X server; returned from
6336 %      XOpenDisplay.
6337 %
6338 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6339 %
6340 %    o windows: Specifies a pointer to a XWindows structure.
6341 %
6342 %    o command: Specifies a command to perform.
6343 %
6344 %    o image: the image;  XImageCache may transform the image and return a new
6345 %      image pointer.
6346 %
6347 */
XImageCache(Display * display,XResourceInfo * resource_info,XWindows * windows,const CommandType command,Image ** image)6348 static void XImageCache(Display *display,XResourceInfo *resource_info,
6349   XWindows *windows,const CommandType command,Image **image)
6350 {
6351   Image
6352     *cache_image;
6353 
6354   static Image
6355     *redo_image = (Image *) NULL,
6356     *undo_image = (Image *) NULL;
6357 
6358   switch (command)
6359   {
6360     case FreeBuffersCommand:
6361     {
6362       /*
6363         Free memory from the undo and redo cache.
6364       */
6365       while (undo_image != (Image *) NULL)
6366       {
6367         cache_image=undo_image;
6368         undo_image=GetPreviousImageInList(undo_image);
6369         cache_image->list=DestroyImage(cache_image->list);
6370         cache_image=DestroyImage(cache_image);
6371       }
6372       undo_image=NewImageList();
6373       if (redo_image != (Image *) NULL)
6374         redo_image=DestroyImage(redo_image);
6375       redo_image=NewImageList();
6376       return;
6377     }
6378     case UndoCommand:
6379     {
6380       char
6381         image_geometry[MaxTextExtent];
6382 
6383       /*
6384         Undo the last image transformation.
6385       */
6386       if (undo_image == (Image *) NULL)
6387         {
6388           (void) XBell(display,0);
6389           return;
6390         }
6391       cache_image=undo_image;
6392       undo_image=GetPreviousImageInList(undo_image);
6393       windows->image.window_changes.width=(int) cache_image->columns;
6394       windows->image.window_changes.height=(int) cache_image->rows;
6395       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6396         windows->image.ximage->width,windows->image.ximage->height);
6397       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6398       if (windows->image.crop_geometry != (char *) NULL)
6399         windows->image.crop_geometry=(char *)
6400           RelinquishMagickMemory(windows->image.crop_geometry);
6401       windows->image.crop_geometry=cache_image->geometry;
6402       if (redo_image != (Image *) NULL)
6403         redo_image=DestroyImage(redo_image);
6404       redo_image=(*image);
6405       *image=cache_image->list;
6406       cache_image=DestroyImage(cache_image);
6407       if (windows->image.orphan != MagickFalse)
6408         return;
6409       XConfigureImageColormap(display,resource_info,windows,*image);
6410       (void) XConfigureImage(display,resource_info,windows,*image);
6411       return;
6412     }
6413     case CutCommand:
6414     case PasteCommand:
6415     case ApplyCommand:
6416     case HalfSizeCommand:
6417     case OriginalSizeCommand:
6418     case DoubleSizeCommand:
6419     case ResizeCommand:
6420     case TrimCommand:
6421     case CropCommand:
6422     case ChopCommand:
6423     case FlipCommand:
6424     case FlopCommand:
6425     case RotateRightCommand:
6426     case RotateLeftCommand:
6427     case RotateCommand:
6428     case ShearCommand:
6429     case RollCommand:
6430     case NegateCommand:
6431     case ContrastStretchCommand:
6432     case SigmoidalContrastCommand:
6433     case NormalizeCommand:
6434     case EqualizeCommand:
6435     case HueCommand:
6436     case SaturationCommand:
6437     case BrightnessCommand:
6438     case GammaCommand:
6439     case SpiffCommand:
6440     case DullCommand:
6441     case GrayscaleCommand:
6442     case MapCommand:
6443     case QuantizeCommand:
6444     case DespeckleCommand:
6445     case EmbossCommand:
6446     case ReduceNoiseCommand:
6447     case AddNoiseCommand:
6448     case SharpenCommand:
6449     case BlurCommand:
6450     case ThresholdCommand:
6451     case EdgeDetectCommand:
6452     case SpreadCommand:
6453     case ShadeCommand:
6454     case RaiseCommand:
6455     case SegmentCommand:
6456     case SolarizeCommand:
6457     case SepiaToneCommand:
6458     case SwirlCommand:
6459     case ImplodeCommand:
6460     case VignetteCommand:
6461     case WaveCommand:
6462     case OilPaintCommand:
6463     case CharcoalDrawCommand:
6464     case AnnotateCommand:
6465     case AddBorderCommand:
6466     case AddFrameCommand:
6467     case CompositeCommand:
6468     case CommentCommand:
6469     case LaunchCommand:
6470     case RegionofInterestCommand:
6471     case SaveToUndoBufferCommand:
6472     case RedoCommand:
6473     {
6474       Image
6475         *previous_image;
6476 
6477       ssize_t
6478         bytes;
6479 
6480       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6481       if (undo_image != (Image *) NULL)
6482         {
6483           /*
6484             Ensure the undo cache has enough memory available.
6485           */
6486           previous_image=undo_image;
6487           while (previous_image != (Image *) NULL)
6488           {
6489             bytes+=previous_image->list->columns*previous_image->list->rows*
6490               sizeof(PixelPacket);
6491             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6492               {
6493                 previous_image=GetPreviousImageInList(previous_image);
6494                 continue;
6495               }
6496             bytes-=previous_image->list->columns*previous_image->list->rows*
6497               sizeof(PixelPacket);
6498             if (previous_image == undo_image)
6499               undo_image=NewImageList();
6500             else
6501               previous_image->next->previous=NewImageList();
6502             break;
6503           }
6504           while (previous_image != (Image *) NULL)
6505           {
6506             /*
6507               Delete any excess memory from undo cache.
6508             */
6509             cache_image=previous_image;
6510             previous_image=GetPreviousImageInList(previous_image);
6511             cache_image->list=DestroyImage(cache_image->list);
6512             cache_image=DestroyImage(cache_image);
6513           }
6514         }
6515       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6516         break;
6517       /*
6518         Save image before transformations are applied.
6519       */
6520       cache_image=AcquireImage((ImageInfo *) NULL);
6521       if (cache_image == (Image *) NULL)
6522         break;
6523       XSetCursorState(display,windows,MagickTrue);
6524       XCheckRefreshWindows(display,windows);
6525       cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6526       XSetCursorState(display,windows,MagickFalse);
6527       if (cache_image->list == (Image *) NULL)
6528         {
6529           cache_image=DestroyImage(cache_image);
6530           break;
6531         }
6532       cache_image->columns=(size_t) windows->image.ximage->width;
6533       cache_image->rows=(size_t) windows->image.ximage->height;
6534       cache_image->geometry=windows->image.crop_geometry;
6535       if (windows->image.crop_geometry != (char *) NULL)
6536         {
6537           cache_image->geometry=AcquireString((char *) NULL);
6538           (void) CopyMagickString(cache_image->geometry,
6539             windows->image.crop_geometry,MaxTextExtent);
6540         }
6541       if (undo_image == (Image *) NULL)
6542         {
6543           undo_image=cache_image;
6544           break;
6545         }
6546       undo_image->next=cache_image;
6547       undo_image->next->previous=undo_image;
6548       undo_image=undo_image->next;
6549       break;
6550     }
6551     default:
6552       break;
6553   }
6554   if (command == RedoCommand)
6555     {
6556       /*
6557         Redo the last image transformation.
6558       */
6559       if (redo_image == (Image *) NULL)
6560         {
6561           (void) XBell(display,0);
6562           return;
6563         }
6564       windows->image.window_changes.width=(int) redo_image->columns;
6565       windows->image.window_changes.height=(int) redo_image->rows;
6566       if (windows->image.crop_geometry != (char *) NULL)
6567         windows->image.crop_geometry=(char *)
6568           RelinquishMagickMemory(windows->image.crop_geometry);
6569       windows->image.crop_geometry=redo_image->geometry;
6570       *image=DestroyImage(*image);
6571       *image=redo_image;
6572       redo_image=NewImageList();
6573       if (windows->image.orphan != MagickFalse)
6574         return;
6575       XConfigureImageColormap(display,resource_info,windows,*image);
6576       (void) XConfigureImage(display,resource_info,windows,*image);
6577       return;
6578     }
6579   if (command != InfoCommand)
6580     return;
6581   /*
6582     Display image info.
6583   */
6584   XSetCursorState(display,windows,MagickTrue);
6585   XCheckRefreshWindows(display,windows);
6586   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6587   XSetCursorState(display,windows,MagickFalse);
6588 }
6589 
6590 /*
6591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6592 %                                                                             %
6593 %                                                                             %
6594 %                                                                             %
6595 +   X I m a g e W i n d o w C o m m a n d                                     %
6596 %                                                                             %
6597 %                                                                             %
6598 %                                                                             %
6599 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6600 %
6601 %  XImageWindowCommand() makes a transform to the image or Image window as
6602 %  specified by a user menu button or keyboard command.
6603 %
6604 %  The format of the XMagickCommand method is:
6605 %
6606 %      CommandType XImageWindowCommand(Display *display,
6607 %        XResourceInfo *resource_info,XWindows *windows,
6608 %        const MagickStatusType state,KeySym key_symbol,Image **image)
6609 %
6610 %  A description of each parameter follows:
6611 %
6612 %    o nexus:  Method XImageWindowCommand returns an image when the
6613 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6614 %      image is returned.
6615 %
6616 %    o display: Specifies a connection to an X server; returned from
6617 %      XOpenDisplay.
6618 %
6619 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6620 %
6621 %    o windows: Specifies a pointer to a XWindows structure.
6622 %
6623 %    o state: key mask.
6624 %
6625 %    o key_symbol: Specifies a command to perform.
6626 %
6627 %    o image: the image;  XImageWIndowCommand
6628 %      may transform the image and return a new image pointer.
6629 %
6630 */
XImageWindowCommand(Display * display,XResourceInfo * resource_info,XWindows * windows,const MagickStatusType state,KeySym key_symbol,Image ** image)6631 static CommandType XImageWindowCommand(Display *display,
6632   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6633   KeySym key_symbol,Image **image)
6634 {
6635   static char
6636     delta[MaxTextExtent] = "";
6637 
6638   static const char
6639     Digits[] = "01234567890";
6640 
6641   static KeySym
6642     last_symbol = XK_0;
6643 
6644   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6645     {
6646       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6647         {
6648           *delta='\0';
6649           resource_info->quantum=1;
6650         }
6651       last_symbol=key_symbol;
6652       delta[strlen(delta)+1]='\0';
6653       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6654       resource_info->quantum=StringToLong(delta);
6655       return(NullCommand);
6656     }
6657   last_symbol=key_symbol;
6658   if (resource_info->immutable)
6659     {
6660       /*
6661         Virtual image window has a restricted command set.
6662       */
6663       switch (key_symbol)
6664       {
6665         case XK_question:
6666           return(InfoCommand);
6667         case XK_p:
6668         case XK_Print:
6669           return(PrintCommand);
6670         case XK_space:
6671           return(NextCommand);
6672         case XK_q:
6673         case XK_Escape:
6674           return(QuitCommand);
6675         default:
6676           break;
6677       }
6678       return(NullCommand);
6679     }
6680   switch ((int) key_symbol)
6681   {
6682     case XK_o:
6683     {
6684       if ((state & ControlMask) == 0)
6685         break;
6686       return(OpenCommand);
6687     }
6688     case XK_space:
6689       return(NextCommand);
6690     case XK_BackSpace:
6691       return(FormerCommand);
6692     case XK_s:
6693     {
6694       if ((state & Mod1Mask) != 0)
6695         return(SwirlCommand);
6696       if ((state & ControlMask) == 0)
6697         return(ShearCommand);
6698       return(SaveCommand);
6699     }
6700     case XK_p:
6701     case XK_Print:
6702     {
6703       if ((state & Mod1Mask) != 0)
6704         return(OilPaintCommand);
6705       if ((state & Mod4Mask) != 0)
6706         return(ColorCommand);
6707       if ((state & ControlMask) == 0)
6708         return(NullCommand);
6709       return(PrintCommand);
6710     }
6711     case XK_d:
6712     {
6713       if ((state & Mod4Mask) != 0)
6714         return(DrawCommand);
6715       if ((state & ControlMask) == 0)
6716         return(NullCommand);
6717       return(DeleteCommand);
6718     }
6719     case XK_Select:
6720     {
6721       if ((state & ControlMask) == 0)
6722         return(NullCommand);
6723       return(SelectCommand);
6724     }
6725     case XK_n:
6726     {
6727       if ((state & ControlMask) == 0)
6728         return(NullCommand);
6729       return(NewCommand);
6730     }
6731     case XK_q:
6732     case XK_Escape:
6733       return(QuitCommand);
6734     case XK_z:
6735     case XK_Undo:
6736     {
6737       if ((state & ControlMask) == 0)
6738         return(NullCommand);
6739       return(UndoCommand);
6740     }
6741     case XK_r:
6742     case XK_Redo:
6743     {
6744       if ((state & ControlMask) == 0)
6745         return(RollCommand);
6746       return(RedoCommand);
6747     }
6748     case XK_x:
6749     {
6750       if ((state & ControlMask) == 0)
6751         return(NullCommand);
6752       return(CutCommand);
6753     }
6754     case XK_c:
6755     {
6756       if ((state & Mod1Mask) != 0)
6757         return(CharcoalDrawCommand);
6758       if ((state & ControlMask) == 0)
6759         return(CropCommand);
6760       return(CopyCommand);
6761     }
6762     case XK_v:
6763     case XK_Insert:
6764     {
6765       if ((state & Mod4Mask) != 0)
6766         return(CompositeCommand);
6767       if ((state & ControlMask) == 0)
6768         return(FlipCommand);
6769       return(PasteCommand);
6770     }
6771     case XK_less:
6772       return(HalfSizeCommand);
6773     case XK_minus:
6774       return(OriginalSizeCommand);
6775     case XK_greater:
6776       return(DoubleSizeCommand);
6777     case XK_percent:
6778       return(ResizeCommand);
6779     case XK_at:
6780       return(RefreshCommand);
6781     case XK_bracketleft:
6782       return(ChopCommand);
6783     case XK_h:
6784       return(FlopCommand);
6785     case XK_slash:
6786       return(RotateRightCommand);
6787     case XK_backslash:
6788       return(RotateLeftCommand);
6789     case XK_asterisk:
6790       return(RotateCommand);
6791     case XK_t:
6792       return(TrimCommand);
6793     case XK_H:
6794       return(HueCommand);
6795     case XK_S:
6796       return(SaturationCommand);
6797     case XK_L:
6798       return(BrightnessCommand);
6799     case XK_G:
6800       return(GammaCommand);
6801     case XK_C:
6802       return(SpiffCommand);
6803     case XK_Z:
6804       return(DullCommand);
6805     case XK_N:
6806       return(NormalizeCommand);
6807     case XK_equal:
6808       return(EqualizeCommand);
6809     case XK_asciitilde:
6810       return(NegateCommand);
6811     case XK_period:
6812       return(GrayscaleCommand);
6813     case XK_numbersign:
6814       return(QuantizeCommand);
6815     case XK_F2:
6816       return(DespeckleCommand);
6817     case XK_F3:
6818       return(EmbossCommand);
6819     case XK_F4:
6820       return(ReduceNoiseCommand);
6821     case XK_F5:
6822       return(AddNoiseCommand);
6823     case XK_F6:
6824       return(SharpenCommand);
6825     case XK_F7:
6826       return(BlurCommand);
6827     case XK_F8:
6828       return(ThresholdCommand);
6829     case XK_F9:
6830       return(EdgeDetectCommand);
6831     case XK_F10:
6832       return(SpreadCommand);
6833     case XK_F11:
6834       return(ShadeCommand);
6835     case XK_F12:
6836       return(RaiseCommand);
6837     case XK_F13:
6838       return(SegmentCommand);
6839     case XK_i:
6840     {
6841       if ((state & Mod1Mask) == 0)
6842         return(NullCommand);
6843       return(ImplodeCommand);
6844     }
6845     case XK_w:
6846     {
6847       if ((state & Mod1Mask) == 0)
6848         return(NullCommand);
6849       return(WaveCommand);
6850     }
6851     case XK_m:
6852     {
6853       if ((state & Mod4Mask) == 0)
6854         return(NullCommand);
6855       return(MatteCommand);
6856     }
6857     case XK_b:
6858     {
6859       if ((state & Mod4Mask) == 0)
6860         return(NullCommand);
6861       return(AddBorderCommand);
6862     }
6863     case XK_f:
6864     {
6865       if ((state & Mod4Mask) == 0)
6866         return(NullCommand);
6867       return(AddFrameCommand);
6868     }
6869     case XK_exclam:
6870     {
6871       if ((state & Mod4Mask) == 0)
6872         return(NullCommand);
6873       return(CommentCommand);
6874     }
6875     case XK_a:
6876     {
6877       if ((state & Mod1Mask) != 0)
6878         return(ApplyCommand);
6879       if ((state & Mod4Mask) != 0)
6880         return(AnnotateCommand);
6881       if ((state & ControlMask) == 0)
6882         return(NullCommand);
6883       return(RegionofInterestCommand);
6884     }
6885     case XK_question:
6886       return(InfoCommand);
6887     case XK_plus:
6888       return(ZoomCommand);
6889     case XK_P:
6890     {
6891       if ((state & ShiftMask) == 0)
6892         return(NullCommand);
6893       return(ShowPreviewCommand);
6894     }
6895     case XK_Execute:
6896       return(LaunchCommand);
6897     case XK_F1:
6898       return(HelpCommand);
6899     case XK_Find:
6900       return(BrowseDocumentationCommand);
6901     case XK_Menu:
6902     {
6903       (void) XMapRaised(display,windows->command.id);
6904       return(NullCommand);
6905     }
6906     case XK_Next:
6907     case XK_Prior:
6908     case XK_Home:
6909     case XK_KP_Home:
6910     {
6911       XTranslateImage(display,windows,*image,key_symbol);
6912       return(NullCommand);
6913     }
6914     case XK_Up:
6915     case XK_KP_Up:
6916     case XK_Down:
6917     case XK_KP_Down:
6918     case XK_Left:
6919     case XK_KP_Left:
6920     case XK_Right:
6921     case XK_KP_Right:
6922     {
6923       if ((state & Mod1Mask) != 0)
6924         {
6925           RectangleInfo
6926             crop_info;
6927 
6928           /*
6929             Trim one pixel from edge of image.
6930           */
6931           crop_info.x=0;
6932           crop_info.y=0;
6933           crop_info.width=(size_t) windows->image.ximage->width;
6934           crop_info.height=(size_t) windows->image.ximage->height;
6935           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6936             {
6937               if (resource_info->quantum >= (int) crop_info.height)
6938                 resource_info->quantum=(int) crop_info.height-1;
6939               crop_info.height-=resource_info->quantum;
6940             }
6941           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6942             {
6943               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6944                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6945               crop_info.y+=resource_info->quantum;
6946               crop_info.height-=resource_info->quantum;
6947             }
6948           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6949             {
6950               if (resource_info->quantum >= (int) crop_info.width)
6951                 resource_info->quantum=(int) crop_info.width-1;
6952               crop_info.width-=resource_info->quantum;
6953             }
6954           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6955             {
6956               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6957                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6958               crop_info.x+=resource_info->quantum;
6959               crop_info.width-=resource_info->quantum;
6960             }
6961           if ((int) (windows->image.x+windows->image.width) >
6962               (int) crop_info.width)
6963             windows->image.x=(int) (crop_info.width-windows->image.width);
6964           if ((int) (windows->image.y+windows->image.height) >
6965               (int) crop_info.height)
6966             windows->image.y=(int) (crop_info.height-windows->image.height);
6967           XSetCropGeometry(display,windows,&crop_info,*image);
6968           windows->image.window_changes.width=(int) crop_info.width;
6969           windows->image.window_changes.height=(int) crop_info.height;
6970           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6971           (void) XConfigureImage(display,resource_info,windows,*image);
6972           return(NullCommand);
6973         }
6974       XTranslateImage(display,windows,*image,key_symbol);
6975       return(NullCommand);
6976     }
6977     default:
6978       return(NullCommand);
6979   }
6980   return(NullCommand);
6981 }
6982 
6983 /*
6984 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6985 %                                                                             %
6986 %                                                                             %
6987 %                                                                             %
6988 +   X M a g i c k C o m m a n d                                               %
6989 %                                                                             %
6990 %                                                                             %
6991 %                                                                             %
6992 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6993 %
6994 %  XMagickCommand() makes a transform to the image or Image window as
6995 %  specified by a user menu button or keyboard command.
6996 %
6997 %  The format of the XMagickCommand method is:
6998 %
6999 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7000 %        XWindows *windows,const CommandType command,Image **image)
7001 %
7002 %  A description of each parameter follows:
7003 %
7004 %    o nexus:  Method XMagickCommand returns an image when the
7005 %      user chooses 'Load Image' from the command menu.  Otherwise a null
7006 %      image is returned.
7007 %
7008 %    o display: Specifies a connection to an X server; returned from
7009 %      XOpenDisplay.
7010 %
7011 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7012 %
7013 %    o windows: Specifies a pointer to a XWindows structure.
7014 %
7015 %    o command: Specifies a command to perform.
7016 %
7017 %    o image: the image;  XMagickCommand
7018 %      may transform the image and return a new image pointer.
7019 %
7020 */
XMagickCommand(Display * display,XResourceInfo * resource_info,XWindows * windows,const CommandType command,Image ** image)7021 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7022   XWindows *windows,const CommandType command,Image **image)
7023 {
7024   char
7025     filename[MaxTextExtent],
7026     geometry[MaxTextExtent],
7027     modulate_factors[MaxTextExtent];
7028 
7029   GeometryInfo
7030     geometry_info;
7031 
7032   Image
7033     *nexus;
7034 
7035   ImageInfo
7036     *image_info;
7037 
7038   int
7039     x,
7040     y;
7041 
7042   MagickStatusType
7043     flags,
7044     status;
7045 
7046   QuantizeInfo
7047     quantize_info;
7048 
7049   RectangleInfo
7050     page_geometry;
7051 
7052   int
7053     i;
7054 
7055   static char
7056     color[MaxTextExtent] = "gray";
7057 
7058   unsigned int
7059     height,
7060     width;
7061 
7062   /*
7063     Process user command.
7064   */
7065   XCheckRefreshWindows(display,windows);
7066   XImageCache(display,resource_info,windows,command,image);
7067   nexus=NewImageList();
7068   windows->image.window_changes.width=windows->image.ximage->width;
7069   windows->image.window_changes.height=windows->image.ximage->height;
7070   image_info=CloneImageInfo(resource_info->image_info);
7071   SetGeometryInfo(&geometry_info);
7072   GetQuantizeInfo(&quantize_info);
7073   switch (command)
7074   {
7075     case OpenCommand:
7076     {
7077       /*
7078         Load image.
7079       */
7080       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7081       break;
7082     }
7083     case NextCommand:
7084     {
7085       /*
7086         Display next image.
7087       */
7088       for (i=0; i < resource_info->quantum; i++)
7089         XClientMessage(display,windows->image.id,windows->im_protocols,
7090           windows->im_next_image,CurrentTime);
7091       break;
7092     }
7093     case FormerCommand:
7094     {
7095       /*
7096         Display former image.
7097       */
7098       for (i=0; i < resource_info->quantum; i++)
7099         XClientMessage(display,windows->image.id,windows->im_protocols,
7100           windows->im_former_image,CurrentTime);
7101       break;
7102     }
7103     case SelectCommand:
7104     {
7105       int
7106         status;
7107 
7108       /*
7109         Select image.
7110       */
7111       if (*resource_info->home_directory == '\0')
7112         (void) CopyMagickString(resource_info->home_directory,".",
7113           MaxTextExtent);
7114       status=chdir(resource_info->home_directory);
7115       if (status == -1)
7116         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7117           FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7118       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7119       break;
7120     }
7121     case SaveCommand:
7122     {
7123       /*
7124         Save image.
7125       */
7126       status=XSaveImage(display,resource_info,windows,*image);
7127       if (status == MagickFalse)
7128         {
7129           char
7130             message[MaxTextExtent];
7131 
7132           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7133             (*image)->exception.reason != (char *) NULL ?
7134             (*image)->exception.reason : "",
7135             (*image)->exception.description != (char *) NULL ?
7136             (*image)->exception.description : "");
7137           XNoticeWidget(display,windows,"Unable to save file:",message);
7138           break;
7139         }
7140       break;
7141     }
7142     case PrintCommand:
7143     {
7144       /*
7145         Print image.
7146       */
7147       status=XPrintImage(display,resource_info,windows,*image);
7148       if (status == MagickFalse)
7149         {
7150           char
7151             message[MaxTextExtent];
7152 
7153           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7154             (*image)->exception.reason != (char *) NULL ?
7155             (*image)->exception.reason : "",
7156             (*image)->exception.description != (char *) NULL ?
7157             (*image)->exception.description : "");
7158           XNoticeWidget(display,windows,"Unable to print file:",message);
7159           break;
7160         }
7161       break;
7162     }
7163     case DeleteCommand:
7164     {
7165       static char
7166         filename[MaxTextExtent] = "\0";
7167 
7168       /*
7169         Delete image file.
7170       */
7171       XFileBrowserWidget(display,windows,"Delete",filename);
7172       if (*filename == '\0')
7173         break;
7174       status=ShredFile(filename);
7175       if (status != MagickFalse)
7176         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7177       break;
7178     }
7179     case NewCommand:
7180     {
7181       int
7182         status;
7183 
7184       static char
7185         color[MaxTextExtent] = "gray",
7186         geometry[MaxTextExtent] = "640x480";
7187 
7188       static const char
7189         *format = "gradient";
7190 
7191       /*
7192         Query user for canvas geometry.
7193       */
7194       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7195         geometry);
7196       if (*geometry == '\0')
7197         break;
7198       if (status == 0)
7199         format="xc";
7200       XColorBrowserWidget(display,windows,"Select",color);
7201       if (*color == '\0')
7202         break;
7203       /*
7204         Create canvas.
7205       */
7206       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7207         "%s:%s",format,color);
7208       (void) CloneString(&image_info->size,geometry);
7209       nexus=ReadImage(image_info,&(*image)->exception);
7210       CatchException(&(*image)->exception);
7211       XClientMessage(display,windows->image.id,windows->im_protocols,
7212         windows->im_next_image,CurrentTime);
7213       break;
7214     }
7215     case VisualDirectoryCommand:
7216     {
7217       /*
7218         Visual Image directory.
7219       */
7220       nexus=XVisualDirectoryImage(display,resource_info,windows);
7221       break;
7222     }
7223     case QuitCommand:
7224     {
7225       /*
7226         exit program.
7227       */
7228       if (resource_info->confirm_exit == MagickFalse)
7229         XClientMessage(display,windows->image.id,windows->im_protocols,
7230           windows->im_exit,CurrentTime);
7231       else
7232         {
7233           int
7234             status;
7235 
7236           /*
7237             Confirm program exit.
7238           */
7239           status=XConfirmWidget(display,windows,"Do you really want to exit",
7240             resource_info->client_name);
7241           if (status > 0)
7242             XClientMessage(display,windows->image.id,windows->im_protocols,
7243               windows->im_exit,CurrentTime);
7244         }
7245       break;
7246     }
7247     case CutCommand:
7248     {
7249       /*
7250         Cut image.
7251       */
7252       (void) XCropImage(display,resource_info,windows,*image,CutMode);
7253       break;
7254     }
7255     case CopyCommand:
7256     {
7257       /*
7258         Copy image.
7259       */
7260       (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7261       break;
7262     }
7263     case PasteCommand:
7264     {
7265       /*
7266         Paste image.
7267       */
7268       status=XPasteImage(display,resource_info,windows,*image);
7269       if (status == MagickFalse)
7270         {
7271           XNoticeWidget(display,windows,"Unable to paste X image",
7272             (*image)->filename);
7273           break;
7274         }
7275       break;
7276     }
7277     case HalfSizeCommand:
7278     {
7279       /*
7280         Half image size.
7281       */
7282       windows->image.window_changes.width=windows->image.ximage->width/2;
7283       windows->image.window_changes.height=windows->image.ximage->height/2;
7284       (void) XConfigureImage(display,resource_info,windows,*image);
7285       break;
7286     }
7287     case OriginalSizeCommand:
7288     {
7289       /*
7290         Original image size.
7291       */
7292       windows->image.window_changes.width=(int) (*image)->columns;
7293       windows->image.window_changes.height=(int) (*image)->rows;
7294       (void) XConfigureImage(display,resource_info,windows,*image);
7295       break;
7296     }
7297     case DoubleSizeCommand:
7298     {
7299       /*
7300         Double the image size.
7301       */
7302       windows->image.window_changes.width=windows->image.ximage->width << 1;
7303       windows->image.window_changes.height=windows->image.ximage->height << 1;
7304       (void) XConfigureImage(display,resource_info,windows,*image);
7305       break;
7306     }
7307     case ResizeCommand:
7308     {
7309       int
7310         status;
7311 
7312       size_t
7313         height,
7314         width;
7315 
7316       ssize_t
7317         x,
7318         y;
7319 
7320       /*
7321         Resize image.
7322       */
7323       width=(size_t) windows->image.ximage->width;
7324       height=(size_t) windows->image.ximage->height;
7325       x=0;
7326       y=0;
7327       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7328         (double) width,(double) height);
7329       status=XDialogWidget(display,windows,"Resize",
7330         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7331       if (*geometry == '\0')
7332         break;
7333       if (status == 0)
7334         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7335       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7336       windows->image.window_changes.width=(int) width;
7337       windows->image.window_changes.height=(int) height;
7338       (void) XConfigureImage(display,resource_info,windows,*image);
7339       break;
7340     }
7341     case ApplyCommand:
7342     {
7343       char
7344         image_geometry[MaxTextExtent];
7345 
7346       if ((windows->image.crop_geometry == (char *) NULL) &&
7347           ((int) (*image)->columns == windows->image.ximage->width) &&
7348           ((int) (*image)->rows == windows->image.ximage->height))
7349         break;
7350       /*
7351         Apply size transforms to image.
7352       */
7353       XSetCursorState(display,windows,MagickTrue);
7354       XCheckRefreshWindows(display,windows);
7355       /*
7356         Crop and/or scale displayed image.
7357       */
7358       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7359         windows->image.ximage->width,windows->image.ximage->height);
7360       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7361       if (windows->image.crop_geometry != (char *) NULL)
7362         windows->image.crop_geometry=(char *)
7363           RelinquishMagickMemory(windows->image.crop_geometry);
7364       windows->image.x=0;
7365       windows->image.y=0;
7366       XConfigureImageColormap(display,resource_info,windows,*image);
7367       (void) XConfigureImage(display,resource_info,windows,*image);
7368       break;
7369     }
7370     case RefreshCommand:
7371     {
7372       (void) XConfigureImage(display,resource_info,windows,*image);
7373       break;
7374     }
7375     case RestoreCommand:
7376     {
7377       /*
7378         Restore Image window to its original size.
7379       */
7380       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7381           (windows->image.height == (unsigned int) (*image)->rows) &&
7382           (windows->image.crop_geometry == (char *) NULL))
7383         {
7384           (void) XBell(display,0);
7385           break;
7386         }
7387       windows->image.window_changes.width=(int) (*image)->columns;
7388       windows->image.window_changes.height=(int) (*image)->rows;
7389       if (windows->image.crop_geometry != (char *) NULL)
7390         {
7391           windows->image.crop_geometry=(char *)
7392             RelinquishMagickMemory(windows->image.crop_geometry);
7393           windows->image.crop_geometry=(char *) NULL;
7394           windows->image.x=0;
7395           windows->image.y=0;
7396         }
7397       XConfigureImageColormap(display,resource_info,windows,*image);
7398       (void) XConfigureImage(display,resource_info,windows,*image);
7399       break;
7400     }
7401     case CropCommand:
7402     {
7403       /*
7404         Crop image.
7405       */
7406       (void) XCropImage(display,resource_info,windows,*image,CropMode);
7407       break;
7408     }
7409     case ChopCommand:
7410     {
7411       /*
7412         Chop image.
7413       */
7414       status=XChopImage(display,resource_info,windows,image);
7415       if (status == MagickFalse)
7416         {
7417           XNoticeWidget(display,windows,"Unable to cut X image",
7418             (*image)->filename);
7419           break;
7420         }
7421       break;
7422     }
7423     case FlopCommand:
7424     {
7425       Image
7426         *flop_image;
7427 
7428       /*
7429         Flop image scanlines.
7430       */
7431       XSetCursorState(display,windows,MagickTrue);
7432       XCheckRefreshWindows(display,windows);
7433       flop_image=FlopImage(*image,&(*image)->exception);
7434       if (flop_image != (Image *) NULL)
7435         {
7436           *image=DestroyImage(*image);
7437           *image=flop_image;
7438         }
7439       CatchException(&(*image)->exception);
7440       XSetCursorState(display,windows,MagickFalse);
7441       if (windows->image.crop_geometry != (char *) NULL)
7442         {
7443           /*
7444             Flop crop geometry.
7445           */
7446           width=(unsigned int) (*image)->columns;
7447           height=(unsigned int) (*image)->rows;
7448           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7449             &width,&height);
7450           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7451             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7452         }
7453       if (windows->image.orphan != MagickFalse)
7454         break;
7455       (void) XConfigureImage(display,resource_info,windows,*image);
7456       break;
7457     }
7458     case FlipCommand:
7459     {
7460       Image
7461         *flip_image;
7462 
7463       /*
7464         Flip image scanlines.
7465       */
7466       XSetCursorState(display,windows,MagickTrue);
7467       XCheckRefreshWindows(display,windows);
7468       flip_image=FlipImage(*image,&(*image)->exception);
7469       if (flip_image != (Image *) NULL)
7470         {
7471           *image=DestroyImage(*image);
7472           *image=flip_image;
7473         }
7474       CatchException(&(*image)->exception);
7475       XSetCursorState(display,windows,MagickFalse);
7476       if (windows->image.crop_geometry != (char *) NULL)
7477         {
7478           /*
7479             Flip crop geometry.
7480           */
7481           width=(unsigned int) (*image)->columns;
7482           height=(unsigned int) (*image)->rows;
7483           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7484             &width,&height);
7485           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7486             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7487         }
7488       if (windows->image.orphan != MagickFalse)
7489         break;
7490       (void) XConfigureImage(display,resource_info,windows,*image);
7491       break;
7492     }
7493     case RotateRightCommand:
7494     {
7495       /*
7496         Rotate image 90 degrees clockwise.
7497       */
7498       status=XRotateImage(display,resource_info,windows,90.0,image);
7499       if (status == MagickFalse)
7500         {
7501           XNoticeWidget(display,windows,"Unable to rotate X image",
7502             (*image)->filename);
7503           break;
7504         }
7505       break;
7506     }
7507     case RotateLeftCommand:
7508     {
7509       /*
7510         Rotate image 90 degrees counter-clockwise.
7511       */
7512       status=XRotateImage(display,resource_info,windows,-90.0,image);
7513       if (status == MagickFalse)
7514         {
7515           XNoticeWidget(display,windows,"Unable to rotate X image",
7516             (*image)->filename);
7517           break;
7518         }
7519       break;
7520     }
7521     case RotateCommand:
7522     {
7523       /*
7524         Rotate image.
7525       */
7526       status=XRotateImage(display,resource_info,windows,0.0,image);
7527       if (status == MagickFalse)
7528         {
7529           XNoticeWidget(display,windows,"Unable to rotate X image",
7530             (*image)->filename);
7531           break;
7532         }
7533       break;
7534     }
7535     case ShearCommand:
7536     {
7537       Image
7538         *shear_image;
7539 
7540       static char
7541         geometry[MaxTextExtent] = "45.0x45.0";
7542 
7543       /*
7544         Query user for shear color and geometry.
7545       */
7546       XColorBrowserWidget(display,windows,"Select",color);
7547       if (*color == '\0')
7548         break;
7549       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7550         geometry);
7551       if (*geometry == '\0')
7552         break;
7553       /*
7554         Shear image.
7555       */
7556       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7557       XSetCursorState(display,windows,MagickTrue);
7558       XCheckRefreshWindows(display,windows);
7559       (void) QueryColorDatabase(color,&(*image)->background_color,
7560         &(*image)->exception);
7561       flags=ParseGeometry(geometry,&geometry_info);
7562       if ((flags & SigmaValue) == 0)
7563         geometry_info.sigma=geometry_info.rho;
7564       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7565         &(*image)->exception);
7566       if (shear_image != (Image *) NULL)
7567         {
7568           *image=DestroyImage(*image);
7569           *image=shear_image;
7570         }
7571       CatchException(&(*image)->exception);
7572       XSetCursorState(display,windows,MagickFalse);
7573       if (windows->image.orphan != MagickFalse)
7574         break;
7575       windows->image.window_changes.width=(int) (*image)->columns;
7576       windows->image.window_changes.height=(int) (*image)->rows;
7577       XConfigureImageColormap(display,resource_info,windows,*image);
7578       (void) XConfigureImage(display,resource_info,windows,*image);
7579       break;
7580     }
7581     case RollCommand:
7582     {
7583       Image
7584         *roll_image;
7585 
7586       static char
7587         geometry[MaxTextExtent] = "+2+2";
7588 
7589       /*
7590         Query user for the roll geometry.
7591       */
7592       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7593         geometry);
7594       if (*geometry == '\0')
7595         break;
7596       /*
7597         Roll image.
7598       */
7599       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7600       XSetCursorState(display,windows,MagickTrue);
7601       XCheckRefreshWindows(display,windows);
7602       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7603         &(*image)->exception);
7604       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7605         &(*image)->exception);
7606       if (roll_image != (Image *) NULL)
7607         {
7608           *image=DestroyImage(*image);
7609           *image=roll_image;
7610         }
7611       CatchException(&(*image)->exception);
7612       XSetCursorState(display,windows,MagickFalse);
7613       if (windows->image.orphan != MagickFalse)
7614         break;
7615       windows->image.window_changes.width=(int) (*image)->columns;
7616       windows->image.window_changes.height=(int) (*image)->rows;
7617       XConfigureImageColormap(display,resource_info,windows,*image);
7618       (void) XConfigureImage(display,resource_info,windows,*image);
7619       break;
7620     }
7621     case TrimCommand:
7622     {
7623       static char
7624         fuzz[MaxTextExtent];
7625 
7626       /*
7627         Query user for the fuzz factor.
7628       */
7629       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7630         (*image)->fuzz/(QuantumRange+1.0));
7631       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7632       if (*fuzz == '\0')
7633         break;
7634       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7635       /*
7636         Trim image.
7637       */
7638       status=XTrimImage(display,resource_info,windows,*image);
7639       if (status == MagickFalse)
7640         {
7641           XNoticeWidget(display,windows,"Unable to trim X image",
7642             (*image)->filename);
7643           break;
7644         }
7645       break;
7646     }
7647     case HueCommand:
7648     {
7649       static char
7650         hue_percent[MaxTextExtent] = "110";
7651 
7652       /*
7653         Query user for percent hue change.
7654       */
7655       (void) XDialogWidget(display,windows,"Apply",
7656         "Enter percent change in image hue (0-200):",hue_percent);
7657       if (*hue_percent == '\0')
7658         break;
7659       /*
7660         Vary the image hue.
7661       */
7662       XSetCursorState(display,windows,MagickTrue);
7663       XCheckRefreshWindows(display,windows);
7664       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7665       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7666         MaxTextExtent);
7667       (void) ModulateImage(*image,modulate_factors);
7668       XSetCursorState(display,windows,MagickFalse);
7669       if (windows->image.orphan != MagickFalse)
7670         break;
7671       XConfigureImageColormap(display,resource_info,windows,*image);
7672       (void) XConfigureImage(display,resource_info,windows,*image);
7673       break;
7674     }
7675     case SaturationCommand:
7676     {
7677       static char
7678         saturation_percent[MaxTextExtent] = "110";
7679 
7680       /*
7681         Query user for percent saturation change.
7682       */
7683       (void) XDialogWidget(display,windows,"Apply",
7684         "Enter percent change in color saturation (0-200):",saturation_percent);
7685       if (*saturation_percent == '\0')
7686         break;
7687       /*
7688         Vary color saturation.
7689       */
7690       XSetCursorState(display,windows,MagickTrue);
7691       XCheckRefreshWindows(display,windows);
7692       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7693       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7694         MaxTextExtent);
7695       (void) ModulateImage(*image,modulate_factors);
7696       XSetCursorState(display,windows,MagickFalse);
7697       if (windows->image.orphan != MagickFalse)
7698         break;
7699       XConfigureImageColormap(display,resource_info,windows,*image);
7700       (void) XConfigureImage(display,resource_info,windows,*image);
7701       break;
7702     }
7703     case BrightnessCommand:
7704     {
7705       static char
7706         brightness_percent[MaxTextExtent] = "110";
7707 
7708       /*
7709         Query user for percent brightness change.
7710       */
7711       (void) XDialogWidget(display,windows,"Apply",
7712         "Enter percent change in color brightness (0-200):",brightness_percent);
7713       if (*brightness_percent == '\0')
7714         break;
7715       /*
7716         Vary the color brightness.
7717       */
7718       XSetCursorState(display,windows,MagickTrue);
7719       XCheckRefreshWindows(display,windows);
7720       (void) CopyMagickString(modulate_factors,brightness_percent,
7721         MaxTextExtent);
7722       (void) ModulateImage(*image,modulate_factors);
7723       XSetCursorState(display,windows,MagickFalse);
7724       if (windows->image.orphan != MagickFalse)
7725         break;
7726       XConfigureImageColormap(display,resource_info,windows,*image);
7727       (void) XConfigureImage(display,resource_info,windows,*image);
7728       break;
7729     }
7730     case GammaCommand:
7731     {
7732       static char
7733         factor[MaxTextExtent] = "1.6";
7734 
7735       /*
7736         Query user for gamma value.
7737       */
7738       (void) XDialogWidget(display,windows,"Gamma",
7739         "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7740       if (*factor == '\0')
7741         break;
7742       /*
7743         Gamma correct image.
7744       */
7745       XSetCursorState(display,windows,MagickTrue);
7746       XCheckRefreshWindows(display,windows);
7747       (void) GammaImage(*image,factor);
7748       XSetCursorState(display,windows,MagickFalse);
7749       if (windows->image.orphan != MagickFalse)
7750         break;
7751       XConfigureImageColormap(display,resource_info,windows,*image);
7752       (void) XConfigureImage(display,resource_info,windows,*image);
7753       break;
7754     }
7755     case SpiffCommand:
7756     {
7757       /*
7758         Sharpen the image contrast.
7759       */
7760       XSetCursorState(display,windows,MagickTrue);
7761       XCheckRefreshWindows(display,windows);
7762       (void) ContrastImage(*image,MagickTrue);
7763       XSetCursorState(display,windows,MagickFalse);
7764       if (windows->image.orphan != MagickFalse)
7765         break;
7766       XConfigureImageColormap(display,resource_info,windows,*image);
7767       (void) XConfigureImage(display,resource_info,windows,*image);
7768       break;
7769     }
7770     case DullCommand:
7771     {
7772       /*
7773         Dull the image contrast.
7774       */
7775       XSetCursorState(display,windows,MagickTrue);
7776       XCheckRefreshWindows(display,windows);
7777       (void) ContrastImage(*image,MagickFalse);
7778       XSetCursorState(display,windows,MagickFalse);
7779       if (windows->image.orphan != MagickFalse)
7780         break;
7781       XConfigureImageColormap(display,resource_info,windows,*image);
7782       (void) XConfigureImage(display,resource_info,windows,*image);
7783       break;
7784     }
7785     case ContrastStretchCommand:
7786     {
7787       double
7788         black_point,
7789         white_point;
7790 
7791       static char
7792         levels[MaxTextExtent] = "1%";
7793 
7794       /*
7795         Query user for gamma value.
7796       */
7797       (void) XDialogWidget(display,windows,"Contrast Stretch",
7798         "Enter black and white points:",levels);
7799       if (*levels == '\0')
7800         break;
7801       /*
7802         Contrast stretch image.
7803       */
7804       XSetCursorState(display,windows,MagickTrue);
7805       XCheckRefreshWindows(display,windows);
7806       flags=ParseGeometry(levels,&geometry_info);
7807       black_point=geometry_info.rho;
7808       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7809       if ((flags & PercentValue) != 0)
7810         {
7811           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7812           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7813         }
7814       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7815       (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7816         white_point);
7817       XSetCursorState(display,windows,MagickFalse);
7818       if (windows->image.orphan != MagickFalse)
7819         break;
7820       XConfigureImageColormap(display,resource_info,windows,*image);
7821       (void) XConfigureImage(display,resource_info,windows,*image);
7822       break;
7823     }
7824     case SigmoidalContrastCommand:
7825     {
7826       static char
7827         levels[MaxTextExtent] = "3x50%";
7828 
7829       /*
7830         Query user for gamma value.
7831       */
7832       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7833         "Enter contrast and midpoint:",levels);
7834       if (*levels == '\0')
7835         break;
7836       /*
7837         Contrast stretch image.
7838       */
7839       XSetCursorState(display,windows,MagickTrue);
7840       XCheckRefreshWindows(display,windows);
7841       (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7842       XSetCursorState(display,windows,MagickFalse);
7843       if (windows->image.orphan != MagickFalse)
7844         break;
7845       XConfigureImageColormap(display,resource_info,windows,*image);
7846       (void) XConfigureImage(display,resource_info,windows,*image);
7847       break;
7848     }
7849     case NormalizeCommand:
7850     {
7851       /*
7852         Perform histogram normalization on the image.
7853       */
7854       XSetCursorState(display,windows,MagickTrue);
7855       XCheckRefreshWindows(display,windows);
7856       (void) NormalizeImage(*image);
7857       XSetCursorState(display,windows,MagickFalse);
7858       if (windows->image.orphan != MagickFalse)
7859         break;
7860       XConfigureImageColormap(display,resource_info,windows,*image);
7861       (void) XConfigureImage(display,resource_info,windows,*image);
7862       break;
7863     }
7864     case EqualizeCommand:
7865     {
7866       /*
7867         Perform histogram equalization on the image.
7868       */
7869       XSetCursorState(display,windows,MagickTrue);
7870       XCheckRefreshWindows(display,windows);
7871       (void) EqualizeImage(*image);
7872       XSetCursorState(display,windows,MagickFalse);
7873       if (windows->image.orphan != MagickFalse)
7874         break;
7875       XConfigureImageColormap(display,resource_info,windows,*image);
7876       (void) XConfigureImage(display,resource_info,windows,*image);
7877       break;
7878     }
7879     case NegateCommand:
7880     {
7881       /*
7882         Negate colors in image.
7883       */
7884       XSetCursorState(display,windows,MagickTrue);
7885       XCheckRefreshWindows(display,windows);
7886       (void) NegateImage(*image,MagickFalse);
7887       XSetCursorState(display,windows,MagickFalse);
7888       if (windows->image.orphan != MagickFalse)
7889         break;
7890       XConfigureImageColormap(display,resource_info,windows,*image);
7891       (void) XConfigureImage(display,resource_info,windows,*image);
7892       break;
7893     }
7894     case GrayscaleCommand:
7895     {
7896       /*
7897         Convert image to grayscale.
7898       */
7899       XSetCursorState(display,windows,MagickTrue);
7900       XCheckRefreshWindows(display,windows);
7901       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7902         GrayscaleType : GrayscaleMatteType);
7903       XSetCursorState(display,windows,MagickFalse);
7904       if (windows->image.orphan != MagickFalse)
7905         break;
7906       XConfigureImageColormap(display,resource_info,windows,*image);
7907       (void) XConfigureImage(display,resource_info,windows,*image);
7908       break;
7909     }
7910     case MapCommand:
7911     {
7912       Image
7913         *affinity_image;
7914 
7915       static char
7916         filename[MaxTextExtent] = "\0";
7917 
7918       /*
7919         Request image file name from user.
7920       */
7921       XFileBrowserWidget(display,windows,"Map",filename);
7922       if (*filename == '\0')
7923         break;
7924       /*
7925         Map image.
7926       */
7927       XSetCursorState(display,windows,MagickTrue);
7928       XCheckRefreshWindows(display,windows);
7929       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7930       affinity_image=ReadImage(image_info,&(*image)->exception);
7931       if (affinity_image != (Image *) NULL)
7932         {
7933           (void) RemapImage(&quantize_info,*image,affinity_image);
7934           affinity_image=DestroyImage(affinity_image);
7935         }
7936       CatchException(&(*image)->exception);
7937       XSetCursorState(display,windows,MagickFalse);
7938       if (windows->image.orphan != MagickFalse)
7939         break;
7940       XConfigureImageColormap(display,resource_info,windows,*image);
7941       (void) XConfigureImage(display,resource_info,windows,*image);
7942       break;
7943     }
7944     case QuantizeCommand:
7945     {
7946       int
7947         status;
7948 
7949       static char
7950         colors[MaxTextExtent] = "256";
7951 
7952       /*
7953         Query user for maximum number of colors.
7954       */
7955       status=XDialogWidget(display,windows,"Quantize",
7956         "Maximum number of colors:",colors);
7957       if (*colors == '\0')
7958         break;
7959       /*
7960         Color reduce the image.
7961       */
7962       XSetCursorState(display,windows,MagickTrue);
7963       XCheckRefreshWindows(display,windows);
7964       quantize_info.number_colors=StringToUnsignedLong(colors);
7965       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7966       (void) QuantizeImage(&quantize_info,*image);
7967       XSetCursorState(display,windows,MagickFalse);
7968       if (windows->image.orphan != MagickFalse)
7969         break;
7970       XConfigureImageColormap(display,resource_info,windows,*image);
7971       (void) XConfigureImage(display,resource_info,windows,*image);
7972       break;
7973     }
7974     case DespeckleCommand:
7975     {
7976       Image
7977         *despeckle_image;
7978 
7979       /*
7980         Despeckle image.
7981       */
7982       XSetCursorState(display,windows,MagickTrue);
7983       XCheckRefreshWindows(display,windows);
7984       despeckle_image=DespeckleImage(*image,&(*image)->exception);
7985       if (despeckle_image != (Image *) NULL)
7986         {
7987           *image=DestroyImage(*image);
7988           *image=despeckle_image;
7989         }
7990       CatchException(&(*image)->exception);
7991       XSetCursorState(display,windows,MagickFalse);
7992       if (windows->image.orphan != MagickFalse)
7993         break;
7994       XConfigureImageColormap(display,resource_info,windows,*image);
7995       (void) XConfigureImage(display,resource_info,windows,*image);
7996       break;
7997     }
7998     case EmbossCommand:
7999     {
8000       Image
8001         *emboss_image;
8002 
8003       static char
8004         radius[MaxTextExtent] = "0.0x1.0";
8005 
8006       /*
8007         Query user for emboss radius.
8008       */
8009       (void) XDialogWidget(display,windows,"Emboss",
8010         "Enter the emboss radius and standard deviation:",radius);
8011       if (*radius == '\0')
8012         break;
8013       /*
8014         Reduce noise in the image.
8015       */
8016       XSetCursorState(display,windows,MagickTrue);
8017       XCheckRefreshWindows(display,windows);
8018       flags=ParseGeometry(radius,&geometry_info);
8019       if ((flags & SigmaValue) == 0)
8020         geometry_info.sigma=1.0;
8021       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8022         &(*image)->exception);
8023       if (emboss_image != (Image *) NULL)
8024         {
8025           *image=DestroyImage(*image);
8026           *image=emboss_image;
8027         }
8028       CatchException(&(*image)->exception);
8029       XSetCursorState(display,windows,MagickFalse);
8030       if (windows->image.orphan != MagickFalse)
8031         break;
8032       XConfigureImageColormap(display,resource_info,windows,*image);
8033       (void) XConfigureImage(display,resource_info,windows,*image);
8034       break;
8035     }
8036     case ReduceNoiseCommand:
8037     {
8038       Image
8039         *noise_image;
8040 
8041       static char
8042         radius[MaxTextExtent] = "0";
8043 
8044       /*
8045         Query user for noise radius.
8046       */
8047       (void) XDialogWidget(display,windows,"Reduce Noise",
8048         "Enter the noise radius:",radius);
8049       if (*radius == '\0')
8050         break;
8051       /*
8052         Reduce noise in the image.
8053       */
8054       XSetCursorState(display,windows,MagickTrue);
8055       XCheckRefreshWindows(display,windows);
8056       flags=ParseGeometry(radius,&geometry_info);
8057       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8058         geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8059       if (noise_image != (Image *) NULL)
8060         {
8061           *image=DestroyImage(*image);
8062           *image=noise_image;
8063         }
8064       CatchException(&(*image)->exception);
8065       XSetCursorState(display,windows,MagickFalse);
8066       if (windows->image.orphan != MagickFalse)
8067         break;
8068       XConfigureImageColormap(display,resource_info,windows,*image);
8069       (void) XConfigureImage(display,resource_info,windows,*image);
8070       break;
8071     }
8072     case AddNoiseCommand:
8073     {
8074       char
8075         **noises;
8076 
8077       Image
8078         *noise_image;
8079 
8080       static char
8081         noise_type[MaxTextExtent] = "Gaussian";
8082 
8083       /*
8084         Add noise to the image.
8085       */
8086       noises=GetCommandOptions(MagickNoiseOptions);
8087       if (noises == (char **) NULL)
8088         break;
8089       XListBrowserWidget(display,windows,&windows->widget,
8090         (const char **) noises,"Add Noise",
8091         "Select a type of noise to add to your image:",noise_type);
8092       noises=DestroyStringList(noises);
8093       if (*noise_type == '\0')
8094         break;
8095       XSetCursorState(display,windows,MagickTrue);
8096       XCheckRefreshWindows(display,windows);
8097       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8098         MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8099       if (noise_image != (Image *) NULL)
8100         {
8101           *image=DestroyImage(*image);
8102           *image=noise_image;
8103         }
8104       CatchException(&(*image)->exception);
8105       XSetCursorState(display,windows,MagickFalse);
8106       if (windows->image.orphan != MagickFalse)
8107         break;
8108       XConfigureImageColormap(display,resource_info,windows,*image);
8109       (void) XConfigureImage(display,resource_info,windows,*image);
8110       break;
8111     }
8112     case SharpenCommand:
8113     {
8114       Image
8115         *sharp_image;
8116 
8117       static char
8118         radius[MaxTextExtent] = "0.0x1.0";
8119 
8120       /*
8121         Query user for sharpen radius.
8122       */
8123       (void) XDialogWidget(display,windows,"Sharpen",
8124         "Enter the sharpen radius and standard deviation:",radius);
8125       if (*radius == '\0')
8126         break;
8127       /*
8128         Sharpen image scanlines.
8129       */
8130       XSetCursorState(display,windows,MagickTrue);
8131       XCheckRefreshWindows(display,windows);
8132       flags=ParseGeometry(radius,&geometry_info);
8133       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8134         &(*image)->exception);
8135       if (sharp_image != (Image *) NULL)
8136         {
8137           *image=DestroyImage(*image);
8138           *image=sharp_image;
8139         }
8140       CatchException(&(*image)->exception);
8141       XSetCursorState(display,windows,MagickFalse);
8142       if (windows->image.orphan != MagickFalse)
8143         break;
8144       XConfigureImageColormap(display,resource_info,windows,*image);
8145       (void) XConfigureImage(display,resource_info,windows,*image);
8146       break;
8147     }
8148     case BlurCommand:
8149     {
8150       Image
8151         *blur_image;
8152 
8153       static char
8154         radius[MaxTextExtent] = "0.0x1.0";
8155 
8156       /*
8157         Query user for blur radius.
8158       */
8159       (void) XDialogWidget(display,windows,"Blur",
8160         "Enter the blur radius and standard deviation:",radius);
8161       if (*radius == '\0')
8162         break;
8163       /*
8164         Blur an image.
8165       */
8166       XSetCursorState(display,windows,MagickTrue);
8167       XCheckRefreshWindows(display,windows);
8168       flags=ParseGeometry(radius,&geometry_info);
8169       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8170         &(*image)->exception);
8171       if (blur_image != (Image *) NULL)
8172         {
8173           *image=DestroyImage(*image);
8174           *image=blur_image;
8175         }
8176       CatchException(&(*image)->exception);
8177       XSetCursorState(display,windows,MagickFalse);
8178       if (windows->image.orphan != MagickFalse)
8179         break;
8180       XConfigureImageColormap(display,resource_info,windows,*image);
8181       (void) XConfigureImage(display,resource_info,windows,*image);
8182       break;
8183     }
8184     case ThresholdCommand:
8185     {
8186       double
8187         threshold;
8188 
8189       static char
8190         factor[MaxTextExtent] = "128";
8191 
8192       /*
8193         Query user for threshold value.
8194       */
8195       (void) XDialogWidget(display,windows,"Threshold",
8196         "Enter threshold value:",factor);
8197       if (*factor == '\0')
8198         break;
8199       /*
8200         Gamma correct image.
8201       */
8202       XSetCursorState(display,windows,MagickTrue);
8203       XCheckRefreshWindows(display,windows);
8204       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8205       (void) BilevelImage(*image,threshold);
8206       XSetCursorState(display,windows,MagickFalse);
8207       if (windows->image.orphan != MagickFalse)
8208         break;
8209       XConfigureImageColormap(display,resource_info,windows,*image);
8210       (void) XConfigureImage(display,resource_info,windows,*image);
8211       break;
8212     }
8213     case EdgeDetectCommand:
8214     {
8215       Image
8216         *edge_image;
8217 
8218       static char
8219         radius[MaxTextExtent] = "0";
8220 
8221       /*
8222         Query user for edge factor.
8223       */
8224       (void) XDialogWidget(display,windows,"Detect Edges",
8225         "Enter the edge detect radius:",radius);
8226       if (*radius == '\0')
8227         break;
8228       /*
8229         Detect edge in image.
8230       */
8231       XSetCursorState(display,windows,MagickTrue);
8232       XCheckRefreshWindows(display,windows);
8233       flags=ParseGeometry(radius,&geometry_info);
8234       edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8235       if (edge_image != (Image *) NULL)
8236         {
8237           *image=DestroyImage(*image);
8238           *image=edge_image;
8239         }
8240       CatchException(&(*image)->exception);
8241       XSetCursorState(display,windows,MagickFalse);
8242       if (windows->image.orphan != MagickFalse)
8243         break;
8244       XConfigureImageColormap(display,resource_info,windows,*image);
8245       (void) XConfigureImage(display,resource_info,windows,*image);
8246       break;
8247     }
8248     case SpreadCommand:
8249     {
8250       Image
8251         *spread_image;
8252 
8253       static char
8254         amount[MaxTextExtent] = "2";
8255 
8256       /*
8257         Query user for spread amount.
8258       */
8259       (void) XDialogWidget(display,windows,"Spread",
8260         "Enter the displacement amount:",amount);
8261       if (*amount == '\0')
8262         break;
8263       /*
8264         Displace image pixels by a random amount.
8265       */
8266       XSetCursorState(display,windows,MagickTrue);
8267       XCheckRefreshWindows(display,windows);
8268       flags=ParseGeometry(amount,&geometry_info);
8269       spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8270       if (spread_image != (Image *) NULL)
8271         {
8272           *image=DestroyImage(*image);
8273           *image=spread_image;
8274         }
8275       CatchException(&(*image)->exception);
8276       XSetCursorState(display,windows,MagickFalse);
8277       if (windows->image.orphan != MagickFalse)
8278         break;
8279       XConfigureImageColormap(display,resource_info,windows,*image);
8280       (void) XConfigureImage(display,resource_info,windows,*image);
8281       break;
8282     }
8283     case ShadeCommand:
8284     {
8285       Image
8286         *shade_image;
8287 
8288       int
8289         status;
8290 
8291       static char
8292         geometry[MaxTextExtent] = "30x30";
8293 
8294       /*
8295         Query user for the shade geometry.
8296       */
8297       status=XDialogWidget(display,windows,"Shade",
8298         "Enter the azimuth and elevation of the light source:",geometry);
8299       if (*geometry == '\0')
8300         break;
8301       /*
8302         Shade image pixels.
8303       */
8304       XSetCursorState(display,windows,MagickTrue);
8305       XCheckRefreshWindows(display,windows);
8306       flags=ParseGeometry(geometry,&geometry_info);
8307       if ((flags & SigmaValue) == 0)
8308         geometry_info.sigma=1.0;
8309       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8310         geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8311       if (shade_image != (Image *) NULL)
8312         {
8313           *image=DestroyImage(*image);
8314           *image=shade_image;
8315         }
8316       CatchException(&(*image)->exception);
8317       XSetCursorState(display,windows,MagickFalse);
8318       if (windows->image.orphan != MagickFalse)
8319         break;
8320       XConfigureImageColormap(display,resource_info,windows,*image);
8321       (void) XConfigureImage(display,resource_info,windows,*image);
8322       break;
8323     }
8324     case RaiseCommand:
8325     {
8326       static char
8327         bevel_width[MaxTextExtent] = "10";
8328 
8329       /*
8330         Query user for bevel width.
8331       */
8332       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8333       if (*bevel_width == '\0')
8334         break;
8335       /*
8336         Raise an image.
8337       */
8338       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8339       XSetCursorState(display,windows,MagickTrue);
8340       XCheckRefreshWindows(display,windows);
8341       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8342         &(*image)->exception);
8343       (void) RaiseImage(*image,&page_geometry,MagickTrue);
8344       XSetCursorState(display,windows,MagickFalse);
8345       if (windows->image.orphan != MagickFalse)
8346         break;
8347       XConfigureImageColormap(display,resource_info,windows,*image);
8348       (void) XConfigureImage(display,resource_info,windows,*image);
8349       break;
8350     }
8351     case SegmentCommand:
8352     {
8353       static char
8354         threshold[MaxTextExtent] = "1.0x1.5";
8355 
8356       /*
8357         Query user for smoothing threshold.
8358       */
8359       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8360         threshold);
8361       if (*threshold == '\0')
8362         break;
8363       /*
8364         Segment an image.
8365       */
8366       XSetCursorState(display,windows,MagickTrue);
8367       XCheckRefreshWindows(display,windows);
8368       flags=ParseGeometry(threshold,&geometry_info);
8369       if ((flags & SigmaValue) == 0)
8370         geometry_info.sigma=1.0;
8371       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8372         geometry_info.sigma);
8373       XSetCursorState(display,windows,MagickFalse);
8374       if (windows->image.orphan != MagickFalse)
8375         break;
8376       XConfigureImageColormap(display,resource_info,windows,*image);
8377       (void) XConfigureImage(display,resource_info,windows,*image);
8378       break;
8379     }
8380     case SepiaToneCommand:
8381     {
8382       double
8383         threshold;
8384 
8385       Image
8386         *sepia_image;
8387 
8388       static char
8389         factor[MaxTextExtent] = "80%";
8390 
8391       /*
8392         Query user for sepia-tone factor.
8393       */
8394       (void) XDialogWidget(display,windows,"Sepia Tone",
8395         "Enter the sepia tone factor (0 - 99.9%):",factor);
8396       if (*factor == '\0')
8397         break;
8398       /*
8399         Sepia tone image pixels.
8400       */
8401       XSetCursorState(display,windows,MagickTrue);
8402       XCheckRefreshWindows(display,windows);
8403       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8404       sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8405       if (sepia_image != (Image *) NULL)
8406         {
8407           *image=DestroyImage(*image);
8408           *image=sepia_image;
8409         }
8410       CatchException(&(*image)->exception);
8411       XSetCursorState(display,windows,MagickFalse);
8412       if (windows->image.orphan != MagickFalse)
8413         break;
8414       XConfigureImageColormap(display,resource_info,windows,*image);
8415       (void) XConfigureImage(display,resource_info,windows,*image);
8416       break;
8417     }
8418     case SolarizeCommand:
8419     {
8420       double
8421         threshold;
8422 
8423       static char
8424         factor[MaxTextExtent] = "60%";
8425 
8426       /*
8427         Query user for solarize factor.
8428       */
8429       (void) XDialogWidget(display,windows,"Solarize",
8430         "Enter the solarize factor (0 - 99.9%):",factor);
8431       if (*factor == '\0')
8432         break;
8433       /*
8434         Solarize image pixels.
8435       */
8436       XSetCursorState(display,windows,MagickTrue);
8437       XCheckRefreshWindows(display,windows);
8438       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8439       (void) SolarizeImage(*image,threshold);
8440       XSetCursorState(display,windows,MagickFalse);
8441       if (windows->image.orphan != MagickFalse)
8442         break;
8443       XConfigureImageColormap(display,resource_info,windows,*image);
8444       (void) XConfigureImage(display,resource_info,windows,*image);
8445       break;
8446     }
8447     case SwirlCommand:
8448     {
8449       Image
8450         *swirl_image;
8451 
8452       static char
8453         degrees[MaxTextExtent] = "60";
8454 
8455       /*
8456         Query user for swirl angle.
8457       */
8458       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8459         degrees);
8460       if (*degrees == '\0')
8461         break;
8462       /*
8463         Swirl image pixels about the center.
8464       */
8465       XSetCursorState(display,windows,MagickTrue);
8466       XCheckRefreshWindows(display,windows);
8467       flags=ParseGeometry(degrees,&geometry_info);
8468       swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8469       if (swirl_image != (Image *) NULL)
8470         {
8471           *image=DestroyImage(*image);
8472           *image=swirl_image;
8473         }
8474       CatchException(&(*image)->exception);
8475       XSetCursorState(display,windows,MagickFalse);
8476       if (windows->image.orphan != MagickFalse)
8477         break;
8478       XConfigureImageColormap(display,resource_info,windows,*image);
8479       (void) XConfigureImage(display,resource_info,windows,*image);
8480       break;
8481     }
8482     case ImplodeCommand:
8483     {
8484       Image
8485         *implode_image;
8486 
8487       static char
8488         factor[MaxTextExtent] = "0.3";
8489 
8490       /*
8491         Query user for implode factor.
8492       */
8493       (void) XDialogWidget(display,windows,"Implode",
8494         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8495       if (*factor == '\0')
8496         break;
8497       /*
8498         Implode image pixels about the center.
8499       */
8500       XSetCursorState(display,windows,MagickTrue);
8501       XCheckRefreshWindows(display,windows);
8502       flags=ParseGeometry(factor,&geometry_info);
8503       implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8504       if (implode_image != (Image *) NULL)
8505         {
8506           *image=DestroyImage(*image);
8507           *image=implode_image;
8508         }
8509       CatchException(&(*image)->exception);
8510       XSetCursorState(display,windows,MagickFalse);
8511       if (windows->image.orphan != MagickFalse)
8512         break;
8513       XConfigureImageColormap(display,resource_info,windows,*image);
8514       (void) XConfigureImage(display,resource_info,windows,*image);
8515       break;
8516     }
8517     case VignetteCommand:
8518     {
8519       Image
8520         *vignette_image;
8521 
8522       static char
8523         geometry[MaxTextExtent] = "0x20";
8524 
8525       /*
8526         Query user for the vignette geometry.
8527       */
8528       (void) XDialogWidget(display,windows,"Vignette",
8529         "Enter the radius, sigma, and x and y offsets:",geometry);
8530       if (*geometry == '\0')
8531         break;
8532       /*
8533         Soften the edges of the image in vignette style
8534       */
8535       XSetCursorState(display,windows,MagickTrue);
8536       XCheckRefreshWindows(display,windows);
8537       flags=ParseGeometry(geometry,&geometry_info);
8538       if ((flags & SigmaValue) == 0)
8539         geometry_info.sigma=1.0;
8540       if ((flags & XiValue) == 0)
8541         geometry_info.xi=0.1*(*image)->columns;
8542       if ((flags & PsiValue) == 0)
8543         geometry_info.psi=0.1*(*image)->rows;
8544       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8545         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8546         0.5),&(*image)->exception);
8547       if (vignette_image != (Image *) NULL)
8548         {
8549           *image=DestroyImage(*image);
8550           *image=vignette_image;
8551         }
8552       CatchException(&(*image)->exception);
8553       XSetCursorState(display,windows,MagickFalse);
8554       if (windows->image.orphan != MagickFalse)
8555         break;
8556       XConfigureImageColormap(display,resource_info,windows,*image);
8557       (void) XConfigureImage(display,resource_info,windows,*image);
8558       break;
8559     }
8560     case WaveCommand:
8561     {
8562       Image
8563         *wave_image;
8564 
8565       static char
8566         geometry[MaxTextExtent] = "25x150";
8567 
8568       /*
8569         Query user for the wave geometry.
8570       */
8571       (void) XDialogWidget(display,windows,"Wave",
8572         "Enter the amplitude and length of the wave:",geometry);
8573       if (*geometry == '\0')
8574         break;
8575       /*
8576         Alter an image along a sine wave.
8577       */
8578       XSetCursorState(display,windows,MagickTrue);
8579       XCheckRefreshWindows(display,windows);
8580       flags=ParseGeometry(geometry,&geometry_info);
8581       if ((flags & SigmaValue) == 0)
8582         geometry_info.sigma=1.0;
8583       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8584         &(*image)->exception);
8585       if (wave_image != (Image *) NULL)
8586         {
8587           *image=DestroyImage(*image);
8588           *image=wave_image;
8589         }
8590       CatchException(&(*image)->exception);
8591       XSetCursorState(display,windows,MagickFalse);
8592       if (windows->image.orphan != MagickFalse)
8593         break;
8594       XConfigureImageColormap(display,resource_info,windows,*image);
8595       (void) XConfigureImage(display,resource_info,windows,*image);
8596       break;
8597     }
8598     case OilPaintCommand:
8599     {
8600       Image
8601         *paint_image;
8602 
8603       static char
8604         radius[MaxTextExtent] = "0";
8605 
8606       /*
8607         Query user for circular neighborhood radius.
8608       */
8609       (void) XDialogWidget(display,windows,"Oil Paint",
8610         "Enter the mask radius:",radius);
8611       if (*radius == '\0')
8612         break;
8613       /*
8614         OilPaint image scanlines.
8615       */
8616       XSetCursorState(display,windows,MagickTrue);
8617       XCheckRefreshWindows(display,windows);
8618       flags=ParseGeometry(radius,&geometry_info);
8619       paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8620       if (paint_image != (Image *) NULL)
8621         {
8622           *image=DestroyImage(*image);
8623           *image=paint_image;
8624         }
8625       CatchException(&(*image)->exception);
8626       XSetCursorState(display,windows,MagickFalse);
8627       if (windows->image.orphan != MagickFalse)
8628         break;
8629       XConfigureImageColormap(display,resource_info,windows,*image);
8630       (void) XConfigureImage(display,resource_info,windows,*image);
8631       break;
8632     }
8633     case CharcoalDrawCommand:
8634     {
8635       Image
8636         *charcoal_image;
8637 
8638       static char
8639         radius[MaxTextExtent] = "0x1";
8640 
8641       /*
8642         Query user for charcoal radius.
8643       */
8644       (void) XDialogWidget(display,windows,"Charcoal Draw",
8645         "Enter the charcoal radius and sigma:",radius);
8646       if (*radius == '\0')
8647         break;
8648       /*
8649         Charcoal the image.
8650       */
8651       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8652       XSetCursorState(display,windows,MagickTrue);
8653       XCheckRefreshWindows(display,windows);
8654       flags=ParseGeometry(radius,&geometry_info);
8655       if ((flags & SigmaValue) == 0)
8656         geometry_info.sigma=geometry_info.rho;
8657       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8658         &(*image)->exception);
8659       if (charcoal_image != (Image *) NULL)
8660         {
8661           *image=DestroyImage(*image);
8662           *image=charcoal_image;
8663         }
8664       CatchException(&(*image)->exception);
8665       XSetCursorState(display,windows,MagickFalse);
8666       if (windows->image.orphan != MagickFalse)
8667         break;
8668       XConfigureImageColormap(display,resource_info,windows,*image);
8669       (void) XConfigureImage(display,resource_info,windows,*image);
8670       break;
8671     }
8672     case AnnotateCommand:
8673     {
8674       /*
8675         Annotate the image with text.
8676       */
8677       status=XAnnotateEditImage(display,resource_info,windows,*image);
8678       if (status == MagickFalse)
8679         {
8680           XNoticeWidget(display,windows,"Unable to annotate X image",
8681             (*image)->filename);
8682           break;
8683         }
8684       break;
8685     }
8686     case DrawCommand:
8687     {
8688       /*
8689         Draw image.
8690       */
8691       status=XDrawEditImage(display,resource_info,windows,image);
8692       if (status == MagickFalse)
8693         {
8694           XNoticeWidget(display,windows,"Unable to draw on the X image",
8695             (*image)->filename);
8696           break;
8697         }
8698       break;
8699     }
8700     case ColorCommand:
8701     {
8702       /*
8703         Color edit.
8704       */
8705       status=XColorEditImage(display,resource_info,windows,image);
8706       if (status == MagickFalse)
8707         {
8708           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8709             (*image)->filename);
8710           break;
8711         }
8712       break;
8713     }
8714     case MatteCommand:
8715     {
8716       /*
8717         Matte edit.
8718       */
8719       status=XMatteEditImage(display,resource_info,windows,image);
8720       if (status == MagickFalse)
8721         {
8722           XNoticeWidget(display,windows,"Unable to matte edit X image",
8723             (*image)->filename);
8724           break;
8725         }
8726       break;
8727     }
8728     case CompositeCommand:
8729     {
8730       /*
8731         Composite image.
8732       */
8733       status=XCompositeImage(display,resource_info,windows,*image);
8734       if (status == MagickFalse)
8735         {
8736           XNoticeWidget(display,windows,"Unable to composite X image",
8737             (*image)->filename);
8738           break;
8739         }
8740       break;
8741     }
8742     case AddBorderCommand:
8743     {
8744       Image
8745         *border_image;
8746 
8747       static char
8748         geometry[MaxTextExtent] = "6x6";
8749 
8750       /*
8751         Query user for border color and geometry.
8752       */
8753       XColorBrowserWidget(display,windows,"Select",color);
8754       if (*color == '\0')
8755         break;
8756       (void) XDialogWidget(display,windows,"Add Border",
8757         "Enter border geometry:",geometry);
8758       if (*geometry == '\0')
8759         break;
8760       /*
8761         Add a border to the image.
8762       */
8763       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8764       XSetCursorState(display,windows,MagickTrue);
8765       XCheckRefreshWindows(display,windows);
8766       (void) QueryColorDatabase(color,&(*image)->border_color,
8767         &(*image)->exception);
8768       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8769         &(*image)->exception);
8770       border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8771       if (border_image != (Image *) NULL)
8772         {
8773           *image=DestroyImage(*image);
8774           *image=border_image;
8775         }
8776       CatchException(&(*image)->exception);
8777       XSetCursorState(display,windows,MagickFalse);
8778       if (windows->image.orphan != MagickFalse)
8779         break;
8780       windows->image.window_changes.width=(int) (*image)->columns;
8781       windows->image.window_changes.height=(int) (*image)->rows;
8782       XConfigureImageColormap(display,resource_info,windows,*image);
8783       (void) XConfigureImage(display,resource_info,windows,*image);
8784       break;
8785     }
8786     case AddFrameCommand:
8787     {
8788       FrameInfo
8789         frame_info;
8790 
8791       Image
8792         *frame_image;
8793 
8794       static char
8795         geometry[MaxTextExtent] = "6x6";
8796 
8797       /*
8798         Query user for frame color and geometry.
8799       */
8800       XColorBrowserWidget(display,windows,"Select",color);
8801       if (*color == '\0')
8802         break;
8803       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8804         geometry);
8805       if (*geometry == '\0')
8806         break;
8807       /*
8808         Surround image with an ornamental border.
8809       */
8810       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8811       XSetCursorState(display,windows,MagickTrue);
8812       XCheckRefreshWindows(display,windows);
8813       (void) QueryColorDatabase(color,&(*image)->matte_color,
8814         &(*image)->exception);
8815       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8816         &(*image)->exception);
8817       frame_info.width=page_geometry.width;
8818       frame_info.height=page_geometry.height;
8819       frame_info.outer_bevel=page_geometry.x;
8820       frame_info.inner_bevel=page_geometry.y;
8821       frame_info.x=(ssize_t) frame_info.width;
8822       frame_info.y=(ssize_t) frame_info.height;
8823       frame_info.width=(*image)->columns+2*frame_info.width;
8824       frame_info.height=(*image)->rows+2*frame_info.height;
8825       frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8826       if (frame_image != (Image *) NULL)
8827         {
8828           *image=DestroyImage(*image);
8829           *image=frame_image;
8830         }
8831       CatchException(&(*image)->exception);
8832       XSetCursorState(display,windows,MagickFalse);
8833       if (windows->image.orphan != MagickFalse)
8834         break;
8835       windows->image.window_changes.width=(int) (*image)->columns;
8836       windows->image.window_changes.height=(int) (*image)->rows;
8837       XConfigureImageColormap(display,resource_info,windows,*image);
8838       (void) XConfigureImage(display,resource_info,windows,*image);
8839       break;
8840     }
8841     case CommentCommand:
8842     {
8843       const char
8844         *value;
8845 
8846       FILE
8847         *file;
8848 
8849       int
8850         unique_file;
8851 
8852       /*
8853         Edit image comment.
8854       */
8855       unique_file=AcquireUniqueFileResource(image_info->filename);
8856       if (unique_file == -1)
8857         XNoticeWidget(display,windows,"Unable to edit image comment",
8858           image_info->filename);
8859       value=GetImageProperty(*image,"comment");
8860       if (value == (char *) NULL)
8861         unique_file=close(unique_file)-1;
8862       else
8863         {
8864           const char
8865             *p;
8866 
8867           file=fdopen(unique_file,"w");
8868           if (file == (FILE *) NULL)
8869             {
8870               XNoticeWidget(display,windows,"Unable to edit image comment",
8871                 image_info->filename);
8872               break;
8873             }
8874           for (p=value; *p != '\0'; p++)
8875             (void) fputc((int) *p,file);
8876           (void) fputc('\n',file);
8877           (void) fclose(file);
8878         }
8879       XSetCursorState(display,windows,MagickTrue);
8880       XCheckRefreshWindows(display,windows);
8881       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8882         &(*image)->exception);
8883       if (status == MagickFalse)
8884         XNoticeWidget(display,windows,"Unable to edit image comment",
8885           (char *) NULL);
8886       else
8887         {
8888           char
8889             *comment;
8890 
8891           comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8892           if (comment != (char *) NULL)
8893             {
8894               (void) SetImageProperty(*image,"comment",comment);
8895               (*image)->taint=MagickTrue;
8896             }
8897         }
8898       (void) RelinquishUniqueFileResource(image_info->filename);
8899       XSetCursorState(display,windows,MagickFalse);
8900       break;
8901     }
8902     case LaunchCommand:
8903     {
8904       /*
8905         Launch program.
8906       */
8907       XSetCursorState(display,windows,MagickTrue);
8908       XCheckRefreshWindows(display,windows);
8909       (void) AcquireUniqueFilename(filename);
8910       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8911         filename);
8912       status=WriteImage(image_info,*image);
8913       if (status == MagickFalse)
8914         XNoticeWidget(display,windows,"Unable to launch image editor",
8915           (char *) NULL);
8916       else
8917         {
8918           nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8919           CatchException(&(*image)->exception);
8920           XClientMessage(display,windows->image.id,windows->im_protocols,
8921             windows->im_next_image,CurrentTime);
8922         }
8923       (void) RelinquishUniqueFileResource(filename);
8924       XSetCursorState(display,windows,MagickFalse);
8925       break;
8926     }
8927     case RegionofInterestCommand:
8928     {
8929       /*
8930         Apply an image processing technique to a region of interest.
8931       */
8932       (void) XROIImage(display,resource_info,windows,image);
8933       break;
8934     }
8935     case InfoCommand:
8936       break;
8937     case ZoomCommand:
8938     {
8939       /*
8940         Zoom image.
8941       */
8942       if (windows->magnify.mapped != MagickFalse)
8943         (void) XRaiseWindow(display,windows->magnify.id);
8944       else
8945         {
8946           /*
8947             Make magnify image.
8948           */
8949           XSetCursorState(display,windows,MagickTrue);
8950           (void) XMapRaised(display,windows->magnify.id);
8951           XSetCursorState(display,windows,MagickFalse);
8952         }
8953       break;
8954     }
8955     case ShowPreviewCommand:
8956     {
8957       char
8958         **previews;
8959 
8960       Image
8961         *preview_image;
8962 
8963       static char
8964         preview_type[MaxTextExtent] = "Gamma";
8965 
8966       /*
8967         Select preview type from menu.
8968       */
8969       previews=GetCommandOptions(MagickPreviewOptions);
8970       if (previews == (char **) NULL)
8971         break;
8972       XListBrowserWidget(display,windows,&windows->widget,
8973         (const char **) previews,"Preview",
8974         "Select an enhancement, effect, or F/X:",preview_type);
8975       previews=DestroyStringList(previews);
8976       if (*preview_type == '\0')
8977         break;
8978       /*
8979         Show image preview.
8980       */
8981       XSetCursorState(display,windows,MagickTrue);
8982       XCheckRefreshWindows(display,windows);
8983       image_info->preview_type=(PreviewType)
8984         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
8985       image_info->group=(ssize_t) windows->image.id;
8986       (void) DeleteImageProperty(*image,"label");
8987       (void) SetImageProperty(*image,"label","Preview");
8988       (void) AcquireUniqueFilename(filename);
8989       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
8990         filename);
8991       status=WriteImage(image_info,*image);
8992       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8993       preview_image=ReadImage(image_info,&(*image)->exception);
8994       (void) RelinquishUniqueFileResource(filename);
8995       if (preview_image == (Image *) NULL)
8996         break;
8997       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
8998         filename);
8999       status=WriteImage(image_info,preview_image);
9000       preview_image=DestroyImage(preview_image);
9001       if (status == MagickFalse)
9002         XNoticeWidget(display,windows,"Unable to show image preview",
9003           (*image)->filename);
9004       XDelay(display,1500);
9005       XSetCursorState(display,windows,MagickFalse);
9006       break;
9007     }
9008     case ShowHistogramCommand:
9009     {
9010       Image
9011         *histogram_image;
9012 
9013       /*
9014         Show image histogram.
9015       */
9016       XSetCursorState(display,windows,MagickTrue);
9017       XCheckRefreshWindows(display,windows);
9018       image_info->group=(ssize_t) windows->image.id;
9019       (void) DeleteImageProperty(*image,"label");
9020       (void) SetImageProperty(*image,"label","Histogram");
9021       (void) AcquireUniqueFilename(filename);
9022       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9023         filename);
9024       status=WriteImage(image_info,*image);
9025       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9026       histogram_image=ReadImage(image_info,&(*image)->exception);
9027       (void) RelinquishUniqueFileResource(filename);
9028       if (histogram_image == (Image *) NULL)
9029         break;
9030       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9031         "show:%s",filename);
9032       status=WriteImage(image_info,histogram_image);
9033       histogram_image=DestroyImage(histogram_image);
9034       if (status == MagickFalse)
9035         XNoticeWidget(display,windows,"Unable to show histogram",
9036           (*image)->filename);
9037       XDelay(display,1500);
9038       XSetCursorState(display,windows,MagickFalse);
9039       break;
9040     }
9041     case ShowMatteCommand:
9042     {
9043       Image
9044         *matte_image;
9045 
9046       if ((*image)->matte == MagickFalse)
9047         {
9048           XNoticeWidget(display,windows,
9049             "Image does not have any matte information",(*image)->filename);
9050           break;
9051         }
9052       /*
9053         Show image matte.
9054       */
9055       XSetCursorState(display,windows,MagickTrue);
9056       XCheckRefreshWindows(display,windows);
9057       image_info->group=(ssize_t) windows->image.id;
9058       (void) DeleteImageProperty(*image,"label");
9059       (void) SetImageProperty(*image,"label","Matte");
9060       (void) AcquireUniqueFilename(filename);
9061       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9062         filename);
9063       status=WriteImage(image_info,*image);
9064       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9065       matte_image=ReadImage(image_info,&(*image)->exception);
9066       (void) RelinquishUniqueFileResource(filename);
9067       if (matte_image == (Image *) NULL)
9068         break;
9069       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9070         filename);
9071       status=WriteImage(image_info,matte_image);
9072       matte_image=DestroyImage(matte_image);
9073       if (status == MagickFalse)
9074         XNoticeWidget(display,windows,"Unable to show matte",
9075           (*image)->filename);
9076       XDelay(display,1500);
9077       XSetCursorState(display,windows,MagickFalse);
9078       break;
9079     }
9080     case BackgroundCommand:
9081     {
9082       /*
9083         Background image.
9084       */
9085       status=XBackgroundImage(display,resource_info,windows,image);
9086       if (status == MagickFalse)
9087         break;
9088       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9089       if (nexus != (Image *) NULL)
9090         XClientMessage(display,windows->image.id,windows->im_protocols,
9091           windows->im_next_image,CurrentTime);
9092       break;
9093     }
9094     case SlideShowCommand:
9095     {
9096       static char
9097         delay[MaxTextExtent] = "5";
9098 
9099       /*
9100         Display next image after pausing.
9101       */
9102       (void) XDialogWidget(display,windows,"Slide Show",
9103         "Pause how many 1/100ths of a second between images:",delay);
9104       if (*delay == '\0')
9105         break;
9106       resource_info->delay=StringToUnsignedLong(delay);
9107       XClientMessage(display,windows->image.id,windows->im_protocols,
9108         windows->im_next_image,CurrentTime);
9109       break;
9110     }
9111     case PreferencesCommand:
9112     {
9113       /*
9114         Set user preferences.
9115       */
9116       status=XPreferencesWidget(display,resource_info,windows);
9117       if (status == MagickFalse)
9118         break;
9119       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9120       if (nexus != (Image *) NULL)
9121         XClientMessage(display,windows->image.id,windows->im_protocols,
9122           windows->im_next_image,CurrentTime);
9123       break;
9124     }
9125     case HelpCommand:
9126     {
9127       /*
9128         User requested help.
9129       */
9130       XTextViewHelp(display,resource_info,windows,MagickFalse,
9131         "Help Viewer - Display",DisplayHelp);
9132       break;
9133     }
9134     case BrowseDocumentationCommand:
9135     {
9136       Atom
9137         mozilla_atom;
9138 
9139       Window
9140         mozilla_window,
9141         root_window;
9142 
9143       /*
9144         Browse the ImageMagick documentation.
9145       */
9146       root_window=XRootWindow(display,XDefaultScreen(display));
9147       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9148       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9149       if (mozilla_window != (Window) NULL)
9150         {
9151           char
9152             command[MaxTextExtent];
9153 
9154           /*
9155             Display documentation using Netscape remote control.
9156           */
9157           (void) FormatLocaleString(command,MaxTextExtent,
9158             "openurl(%s,new-tab)",MagickAuthoritativeURL);
9159           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9160           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9161             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9162           XSetCursorState(display,windows,MagickFalse);
9163           break;
9164         }
9165       XSetCursorState(display,windows,MagickTrue);
9166       XCheckRefreshWindows(display,windows);
9167       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9168         &(*image)->exception);
9169       if (status == MagickFalse)
9170         XNoticeWidget(display,windows,"Unable to browse documentation",
9171           (char *) NULL);
9172       XDelay(display,1500);
9173       XSetCursorState(display,windows,MagickFalse);
9174       break;
9175     }
9176     case VersionCommand:
9177     {
9178       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9179         GetMagickCopyright());
9180       break;
9181     }
9182     case SaveToUndoBufferCommand:
9183       break;
9184     default:
9185     {
9186       (void) XBell(display,0);
9187       break;
9188     }
9189   }
9190   image_info=DestroyImageInfo(image_info);
9191   return(nexus);
9192 }
9193 
9194 /*
9195 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9196 %                                                                             %
9197 %                                                                             %
9198 %                                                                             %
9199 +   X M a g n i f y I m a g e                                                 %
9200 %                                                                             %
9201 %                                                                             %
9202 %                                                                             %
9203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9204 %
9205 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9206 %  The magnified portion is displayed in a separate window.
9207 %
9208 %  The format of the XMagnifyImage method is:
9209 %
9210 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9211 %
9212 %  A description of each parameter follows:
9213 %
9214 %    o display: Specifies a connection to an X server;  returned from
9215 %      XOpenDisplay.
9216 %
9217 %    o windows: Specifies a pointer to a XWindows structure.
9218 %
9219 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9220 %      the entire image is refreshed.
9221 %
9222 */
XMagnifyImage(Display * display,XWindows * windows,XEvent * event)9223 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9224 {
9225   char
9226     text[MaxTextExtent];
9227 
9228   int
9229     x,
9230     y;
9231 
9232   size_t
9233     state;
9234 
9235   /*
9236     Update magnified image until the mouse button is released.
9237   */
9238   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9239   state=DefaultState;
9240   x=event->xbutton.x;
9241   y=event->xbutton.y;
9242   windows->magnify.x=(int) windows->image.x+x;
9243   windows->magnify.y=(int) windows->image.y+y;
9244   do
9245   {
9246     /*
9247       Map and unmap Info widget as text cursor crosses its boundaries.
9248     */
9249     if (windows->info.mapped != MagickFalse)
9250       {
9251         if ((x < (int) (windows->info.x+windows->info.width)) &&
9252             (y < (int) (windows->info.y+windows->info.height)))
9253           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9254       }
9255     else
9256       if ((x > (int) (windows->info.x+windows->info.width)) ||
9257           (y > (int) (windows->info.y+windows->info.height)))
9258         (void) XMapWindow(display,windows->info.id);
9259     if (windows->info.mapped != MagickFalse)
9260       {
9261         /*
9262           Display pointer position.
9263         */
9264         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9265           windows->magnify.x,windows->magnify.y);
9266         XInfoWidget(display,windows,text);
9267       }
9268     /*
9269       Wait for next event.
9270     */
9271     XScreenEvent(display,windows,event);
9272     switch (event->type)
9273     {
9274       case ButtonPress:
9275         break;
9276       case ButtonRelease:
9277       {
9278         /*
9279           User has finished magnifying image.
9280         */
9281         x=event->xbutton.x;
9282         y=event->xbutton.y;
9283         state|=ExitState;
9284         break;
9285       }
9286       case Expose:
9287         break;
9288       case MotionNotify:
9289       {
9290         x=event->xmotion.x;
9291         y=event->xmotion.y;
9292         break;
9293       }
9294       default:
9295         break;
9296     }
9297     /*
9298       Check boundary conditions.
9299     */
9300     if (x < 0)
9301       x=0;
9302     else
9303       if (x >= (int) windows->image.width)
9304         x=(int) windows->image.width-1;
9305     if (y < 0)
9306       y=0;
9307     else
9308      if (y >= (int) windows->image.height)
9309        y=(int) windows->image.height-1;
9310   } while ((state & ExitState) == 0);
9311   /*
9312     Display magnified image.
9313   */
9314   XSetCursorState(display,windows,MagickFalse);
9315 }
9316 
9317 /*
9318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9319 %                                                                             %
9320 %                                                                             %
9321 %                                                                             %
9322 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9323 %                                                                             %
9324 %                                                                             %
9325 %                                                                             %
9326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9327 %
9328 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9329 %  pixel as specified by the key symbol.
9330 %
9331 %  The format of the XMagnifyWindowCommand method is:
9332 %
9333 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9334 %        const MagickStatusType state,const KeySym key_symbol)
9335 %
9336 %  A description of each parameter follows:
9337 %
9338 %    o display: Specifies a connection to an X server; returned from
9339 %      XOpenDisplay.
9340 %
9341 %    o windows: Specifies a pointer to a XWindows structure.
9342 %
9343 %    o state: key mask.
9344 %
9345 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9346 %      to trim.
9347 %
9348 */
XMagnifyWindowCommand(Display * display,XWindows * windows,const MagickStatusType state,const KeySym key_symbol)9349 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9350   const MagickStatusType state,const KeySym key_symbol)
9351 {
9352   unsigned int
9353     quantum;
9354 
9355   /*
9356     User specified a magnify factor or position.
9357   */
9358   quantum=1;
9359   if ((state & Mod1Mask) != 0)
9360     quantum=10;
9361   switch ((int) key_symbol)
9362   {
9363     case QuitCommand:
9364     {
9365       (void) XWithdrawWindow(display,windows->magnify.id,
9366         windows->magnify.screen);
9367       break;
9368     }
9369     case XK_Home:
9370     case XK_KP_Home:
9371     {
9372       windows->magnify.x=(int) windows->image.width/2;
9373       windows->magnify.y=(int) windows->image.height/2;
9374       break;
9375     }
9376     case XK_Left:
9377     case XK_KP_Left:
9378     {
9379       if (windows->magnify.x > 0)
9380         windows->magnify.x-=quantum;
9381       break;
9382     }
9383     case XK_Up:
9384     case XK_KP_Up:
9385     {
9386       if (windows->magnify.y > 0)
9387         windows->magnify.y-=quantum;
9388       break;
9389     }
9390     case XK_Right:
9391     case XK_KP_Right:
9392     {
9393       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9394         windows->magnify.x+=quantum;
9395       break;
9396     }
9397     case XK_Down:
9398     case XK_KP_Down:
9399     {
9400       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9401         windows->magnify.y+=quantum;
9402       break;
9403     }
9404     case XK_0:
9405     case XK_1:
9406     case XK_2:
9407     case XK_3:
9408     case XK_4:
9409     case XK_5:
9410     case XK_6:
9411     case XK_7:
9412     case XK_8:
9413     case XK_9:
9414     {
9415       windows->magnify.data=(key_symbol-XK_0);
9416       break;
9417     }
9418     case XK_KP_0:
9419     case XK_KP_1:
9420     case XK_KP_2:
9421     case XK_KP_3:
9422     case XK_KP_4:
9423     case XK_KP_5:
9424     case XK_KP_6:
9425     case XK_KP_7:
9426     case XK_KP_8:
9427     case XK_KP_9:
9428     {
9429       windows->magnify.data=(key_symbol-XK_KP_0);
9430       break;
9431     }
9432     default:
9433       break;
9434   }
9435   XMakeMagnifyImage(display,windows);
9436 }
9437 
9438 /*
9439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9440 %                                                                             %
9441 %                                                                             %
9442 %                                                                             %
9443 +   X M a k e P a n I m a g e                                                 %
9444 %                                                                             %
9445 %                                                                             %
9446 %                                                                             %
9447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9448 %
9449 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9450 %  icon window.
9451 %
9452 %  The format of the XMakePanImage method is:
9453 %
9454 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9455 %          XWindows *windows,Image *image)
9456 %
9457 %  A description of each parameter follows:
9458 %
9459 %    o display: Specifies a connection to an X server;  returned from
9460 %      XOpenDisplay.
9461 %
9462 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9463 %
9464 %    o windows: Specifies a pointer to a XWindows structure.
9465 %
9466 %    o image: the image.
9467 %
9468 */
XMakePanImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image)9469 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9470   XWindows *windows,Image *image)
9471 {
9472   MagickStatusType
9473     status;
9474 
9475   /*
9476     Create and display image for panning icon.
9477   */
9478   XSetCursorState(display,windows,MagickTrue);
9479   XCheckRefreshWindows(display,windows);
9480   windows->pan.x=(int) windows->image.x;
9481   windows->pan.y=(int) windows->image.y;
9482   status=XMakeImage(display,resource_info,&windows->pan,image,
9483     windows->pan.width,windows->pan.height);
9484   if (status == MagickFalse)
9485     ThrowXWindowFatalException(XServerFatalError,image->exception.reason,
9486       image->exception.description);
9487   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9488     windows->pan.pixmap);
9489   (void) XClearWindow(display,windows->pan.id);
9490   XDrawPanRectangle(display,windows);
9491   XSetCursorState(display,windows,MagickFalse);
9492 }
9493 
9494 /*
9495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9496 %                                                                             %
9497 %                                                                             %
9498 %                                                                             %
9499 +   X M a t t a E d i t I m a g e                                             %
9500 %                                                                             %
9501 %                                                                             %
9502 %                                                                             %
9503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9504 %
9505 %  XMatteEditImage() allows the user to interactively change the Matte channel
9506 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9507 %  before the matte information is stored.
9508 %
9509 %  The format of the XMatteEditImage method is:
9510 %
9511 %      MagickBooleanType XMatteEditImage(Display *display,
9512 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
9513 %
9514 %  A description of each parameter follows:
9515 %
9516 %    o display: Specifies a connection to an X server;  returned from
9517 %      XOpenDisplay.
9518 %
9519 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9520 %
9521 %    o windows: Specifies a pointer to a XWindows structure.
9522 %
9523 %    o image: the image; returned from ReadImage.
9524 %
9525 */
XMatteEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image)9526 static MagickBooleanType XMatteEditImage(Display *display,
9527   XResourceInfo *resource_info,XWindows *windows,Image **image)
9528 {
9529   const char
9530     *const MatteEditMenu[] =
9531     {
9532       "Method",
9533       "Border Color",
9534       "Fuzz",
9535       "Matte Value",
9536       "Undo",
9537       "Help",
9538       "Dismiss",
9539       (char *) NULL
9540     };
9541 
9542   static char
9543     matte[MaxTextExtent] = "0";
9544 
9545   static const ModeType
9546     MatteEditCommands[] =
9547     {
9548       MatteEditMethod,
9549       MatteEditBorderCommand,
9550       MatteEditFuzzCommand,
9551       MatteEditValueCommand,
9552       MatteEditUndoCommand,
9553       MatteEditHelpCommand,
9554       MatteEditDismissCommand
9555     };
9556 
9557   static PaintMethod
9558     method = PointMethod;
9559 
9560   static XColor
9561     border_color = { 0, 0, 0, 0, 0, 0 };
9562 
9563   char
9564     command[MaxTextExtent],
9565     text[MaxTextExtent];
9566 
9567   Cursor
9568     cursor;
9569 
9570   int
9571     entry,
9572     id,
9573     x,
9574     x_offset,
9575     y,
9576     y_offset;
9577 
9578   int
9579     i;
9580 
9581   PixelPacket
9582     *q;
9583 
9584   unsigned int
9585     height,
9586     width;
9587 
9588   size_t
9589     state;
9590 
9591   XEvent
9592     event;
9593 
9594   /*
9595     Map Command widget.
9596   */
9597   (void) CloneString(&windows->command.name,"Matte Edit");
9598   windows->command.data=4;
9599   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9600   (void) XMapRaised(display,windows->command.id);
9601   XClientMessage(display,windows->image.id,windows->im_protocols,
9602     windows->im_update_widget,CurrentTime);
9603   /*
9604     Make cursor.
9605   */
9606   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9607     resource_info->background_color,resource_info->foreground_color);
9608   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9609   /*
9610     Track pointer until button 1 is pressed.
9611   */
9612   XQueryPosition(display,windows->image.id,&x,&y);
9613   (void) XSelectInput(display,windows->image.id,
9614     windows->image.attributes.event_mask | PointerMotionMask);
9615   state=DefaultState;
9616   do
9617   {
9618     if (windows->info.mapped != MagickFalse)
9619       {
9620         /*
9621           Display pointer position.
9622         */
9623         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9624           x+windows->image.x,y+windows->image.y);
9625         XInfoWidget(display,windows,text);
9626       }
9627     /*
9628       Wait for next event.
9629     */
9630     XScreenEvent(display,windows,&event);
9631     if (event.xany.window == windows->command.id)
9632       {
9633         /*
9634           Select a command from the Command widget.
9635         */
9636         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9637         if (id < 0)
9638           {
9639             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9640             continue;
9641           }
9642         switch (MatteEditCommands[id])
9643         {
9644           case MatteEditMethod:
9645           {
9646             char
9647               **methods;
9648 
9649             /*
9650               Select a method from the pop-up menu.
9651             */
9652             methods=GetCommandOptions(MagickMethodOptions);
9653             if (methods == (char **) NULL)
9654               break;
9655             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9656               (const char **) methods,command);
9657             if (entry >= 0)
9658               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9659                 MagickFalse,methods[entry]);
9660             methods=DestroyStringList(methods);
9661             break;
9662           }
9663           case MatteEditBorderCommand:
9664           {
9665             const char
9666               *ColorMenu[MaxNumberPens];
9667 
9668             int
9669               pen_number;
9670 
9671             /*
9672               Initialize menu selections.
9673             */
9674             for (i=0; i < (int) (MaxNumberPens-2); i++)
9675               ColorMenu[i]=resource_info->pen_colors[i];
9676             ColorMenu[MaxNumberPens-2]="Browser...";
9677             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9678             /*
9679               Select a pen color from the pop-up menu.
9680             */
9681             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9682               (const char **) ColorMenu,command);
9683             if (pen_number < 0)
9684               break;
9685             if (pen_number == (MaxNumberPens-2))
9686               {
9687                 static char
9688                   color_name[MaxTextExtent] = "gray";
9689 
9690                 /*
9691                   Select a pen color from a dialog.
9692                 */
9693                 resource_info->pen_colors[pen_number]=color_name;
9694                 XColorBrowserWidget(display,windows,"Select",color_name);
9695                 if (*color_name == '\0')
9696                   break;
9697               }
9698             /*
9699               Set border color.
9700             */
9701             (void) XParseColor(display,windows->map_info->colormap,
9702               resource_info->pen_colors[pen_number],&border_color);
9703             break;
9704           }
9705           case MatteEditFuzzCommand:
9706           {
9707             const char
9708               *const FuzzMenu[] =
9709               {
9710                 "0%",
9711                 "2%",
9712                 "5%",
9713                 "10%",
9714                 "15%",
9715                 "Dialog...",
9716                 (char *) NULL,
9717               };
9718 
9719             static char
9720               fuzz[MaxTextExtent];
9721 
9722             /*
9723               Select a command from the pop-up menu.
9724             */
9725             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9726               command);
9727             if (entry < 0)
9728               break;
9729             if (entry != 5)
9730               {
9731                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9732                   QuantumRange+1.0);
9733                 break;
9734               }
9735             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9736             (void) XDialogWidget(display,windows,"Ok",
9737               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9738             if (*fuzz == '\0')
9739               break;
9740             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9741             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9742               1.0);
9743             break;
9744           }
9745           case MatteEditValueCommand:
9746           {
9747             const char
9748               *const MatteMenu[] =
9749               {
9750                 "Opaque",
9751                 "Transparent",
9752                 "Dialog...",
9753                 (char *) NULL,
9754               };
9755 
9756             static char
9757               message[MaxTextExtent];
9758 
9759             /*
9760               Select a command from the pop-up menu.
9761             */
9762             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9763               command);
9764             if (entry < 0)
9765               break;
9766             if (entry != 2)
9767               {
9768                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9769                   OpaqueOpacity);
9770                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9771                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9772                     (Quantum) TransparentOpacity);
9773                 break;
9774               }
9775             (void) FormatLocaleString(message,MaxTextExtent,
9776               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9777               QuantumRange);
9778             (void) XDialogWidget(display,windows,"Matte",message,matte);
9779             if (*matte == '\0')
9780               break;
9781             break;
9782           }
9783           case MatteEditUndoCommand:
9784           {
9785             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9786               image);
9787             break;
9788           }
9789           case MatteEditHelpCommand:
9790           {
9791             XTextViewHelp(display,resource_info,windows,MagickFalse,
9792               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9793             break;
9794           }
9795           case MatteEditDismissCommand:
9796           {
9797             /*
9798               Prematurely exit.
9799             */
9800             state|=EscapeState;
9801             state|=ExitState;
9802             break;
9803           }
9804           default:
9805             break;
9806         }
9807         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9808         continue;
9809       }
9810     switch (event.type)
9811     {
9812       case ButtonPress:
9813       {
9814         if (event.xbutton.button != Button1)
9815           break;
9816         if ((event.xbutton.window != windows->image.id) &&
9817             (event.xbutton.window != windows->magnify.id))
9818           break;
9819         /*
9820           Update matte data.
9821         */
9822         x=event.xbutton.x;
9823         y=event.xbutton.y;
9824         (void) XMagickCommand(display,resource_info,windows,
9825           SaveToUndoBufferCommand,image);
9826         state|=UpdateConfigurationState;
9827         break;
9828       }
9829       case ButtonRelease:
9830       {
9831         if (event.xbutton.button != Button1)
9832           break;
9833         if ((event.xbutton.window != windows->image.id) &&
9834             (event.xbutton.window != windows->magnify.id))
9835           break;
9836         /*
9837           Update colormap information.
9838         */
9839         x=event.xbutton.x;
9840         y=event.xbutton.y;
9841         XConfigureImageColormap(display,resource_info,windows,*image);
9842         (void) XConfigureImage(display,resource_info,windows,*image);
9843         XInfoWidget(display,windows,text);
9844         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9845         state&=(~UpdateConfigurationState);
9846         break;
9847       }
9848       case Expose:
9849         break;
9850       case KeyPress:
9851       {
9852         char
9853           command[MaxTextExtent];
9854 
9855         KeySym
9856           key_symbol;
9857 
9858         if (event.xkey.window == windows->magnify.id)
9859           {
9860             Window
9861               window;
9862 
9863             window=windows->magnify.id;
9864             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9865           }
9866         if (event.xkey.window != windows->image.id)
9867           break;
9868         /*
9869           Respond to a user key press.
9870         */
9871         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9872           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9873         switch ((int) key_symbol)
9874         {
9875           case XK_Escape:
9876           case XK_F20:
9877           {
9878             /*
9879               Prematurely exit.
9880             */
9881             state|=ExitState;
9882             break;
9883           }
9884           case XK_F1:
9885           case XK_Help:
9886           {
9887             XTextViewHelp(display,resource_info,windows,MagickFalse,
9888               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9889             break;
9890           }
9891           default:
9892           {
9893             (void) XBell(display,0);
9894             break;
9895           }
9896         }
9897         break;
9898       }
9899       case MotionNotify:
9900       {
9901         /*
9902           Map and unmap Info widget as cursor crosses its boundaries.
9903         */
9904         x=event.xmotion.x;
9905         y=event.xmotion.y;
9906         if (windows->info.mapped != MagickFalse)
9907           {
9908             if ((x < (int) (windows->info.x+windows->info.width)) &&
9909                 (y < (int) (windows->info.y+windows->info.height)))
9910               (void) XWithdrawWindow(display,windows->info.id,
9911                 windows->info.screen);
9912           }
9913         else
9914           if ((x > (int) (windows->info.x+windows->info.width)) ||
9915               (y > (int) (windows->info.y+windows->info.height)))
9916             (void) XMapWindow(display,windows->info.id);
9917         break;
9918       }
9919       default:
9920         break;
9921     }
9922     if (event.xany.window == windows->magnify.id)
9923       {
9924         x=windows->magnify.x-windows->image.x;
9925         y=windows->magnify.y-windows->image.y;
9926       }
9927     x_offset=x;
9928     y_offset=y;
9929     if ((state & UpdateConfigurationState) != 0)
9930       {
9931         CacheView
9932           *image_view;
9933 
9934         ExceptionInfo
9935           *exception;
9936 
9937         int
9938           x,
9939           y;
9940 
9941         /*
9942           Matte edit is relative to image configuration.
9943         */
9944         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9945           MagickTrue);
9946         XPutPixel(windows->image.ximage,x_offset,y_offset,
9947           windows->pixel_info->background_color.pixel);
9948         width=(unsigned int) (*image)->columns;
9949         height=(unsigned int) (*image)->rows;
9950         x=0;
9951         y=0;
9952         if (windows->image.crop_geometry != (char *) NULL)
9953           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9954             &width,&height);
9955         x_offset=(int)
9956           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9957         y_offset=(int)
9958           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9959         if ((x_offset < 0) || (y_offset < 0))
9960           continue;
9961         if ((x_offset >= (int) (*image)->columns) ||
9962             (y_offset >= (int) (*image)->rows))
9963           continue;
9964         if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9965           return(MagickFalse);
9966         (*image)->matte=MagickTrue;
9967         exception=(&(*image)->exception);
9968         image_view=AcquireAuthenticCacheView(*image,exception);
9969         switch (method)
9970         {
9971           case PointMethod:
9972           default:
9973           {
9974             /*
9975               Update matte information using point algorithm.
9976             */
9977             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9978               (ssize_t) y_offset,1,1,exception);
9979             if (q == (PixelPacket *) NULL)
9980               break;
9981             q->opacity=(Quantum) StringToLong(matte);
9982             (void) SyncCacheViewAuthenticPixels(image_view,exception);
9983             break;
9984           }
9985           case ReplaceMethod:
9986           {
9987             PixelPacket
9988               target;
9989 
9990             /*
9991               Update matte information using replace algorithm.
9992             */
9993             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
9994               (ssize_t) y_offset,&target,exception);
9995             for (y=0; y < (int) (*image)->rows; y++)
9996             {
9997               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
9998                 (*image)->columns,1,&(*image)->exception);
9999               if (q == (PixelPacket *) NULL)
10000                 break;
10001               for (x=0; x < (int) (*image)->columns; x++)
10002               {
10003                 if (IsColorSimilar(*image,q,&target))
10004                   q->opacity=(Quantum) StringToLong(matte);
10005                 q++;
10006               }
10007               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10008                 break;
10009             }
10010             break;
10011           }
10012           case FloodfillMethod:
10013           case FillToBorderMethod:
10014           {
10015             DrawInfo
10016               *draw_info;
10017 
10018             MagickPixelPacket
10019               target;
10020 
10021             /*
10022               Update matte information using floodfill algorithm.
10023             */
10024             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10025               (ssize_t) y_offset,&target,exception);
10026             if (method == FillToBorderMethod)
10027               {
10028                 target.red=(MagickRealType)
10029                   ScaleShortToQuantum(border_color.red);
10030                 target.green=(MagickRealType)
10031                   ScaleShortToQuantum(border_color.green);
10032                 target.blue=(MagickRealType)
10033                   ScaleShortToQuantum(border_color.blue);
10034               }
10035             draw_info=CloneDrawInfo(resource_info->image_info,
10036               (DrawInfo *) NULL);
10037             draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte,
10038               (char **) NULL));
10039             (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10040               (ssize_t) x_offset,(ssize_t) y_offset,
10041               method == FloodfillMethod ? MagickFalse : MagickTrue);
10042             draw_info=DestroyDrawInfo(draw_info);
10043             break;
10044           }
10045           case ResetMethod:
10046           {
10047             /*
10048               Update matte information using reset algorithm.
10049             */
10050             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10051               return(MagickFalse);
10052             for (y=0; y < (int) (*image)->rows; y++)
10053             {
10054               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10055                 (*image)->columns,1,exception);
10056               if (q == (PixelPacket *) NULL)
10057                 break;
10058               for (x=0; x < (int) (*image)->columns; x++)
10059               {
10060                 q->opacity=(Quantum) StringToLong(matte);
10061                 q++;
10062               }
10063               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10064                 break;
10065             }
10066             if (StringToLong(matte) == OpaqueOpacity)
10067               (*image)->matte=MagickFalse;
10068             break;
10069           }
10070         }
10071         image_view=DestroyCacheView(image_view);
10072         state&=(~UpdateConfigurationState);
10073       }
10074   } while ((state & ExitState) == 0);
10075   (void) XSelectInput(display,windows->image.id,
10076     windows->image.attributes.event_mask);
10077   XSetCursorState(display,windows,MagickFalse);
10078   (void) XFreeCursor(display,cursor);
10079   return(MagickTrue);
10080 }
10081 
10082 /*
10083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10084 %                                                                             %
10085 %                                                                             %
10086 %                                                                             %
10087 +   X O p e n I m a g e                                                       %
10088 %                                                                             %
10089 %                                                                             %
10090 %                                                                             %
10091 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10092 %
10093 %  XOpenImage() loads an image from a file.
10094 %
10095 %  The format of the XOpenImage method is:
10096 %
10097 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10098 %       XWindows *windows,const unsigned int command)
10099 %
10100 %  A description of each parameter follows:
10101 %
10102 %    o display: Specifies a connection to an X server; returned from
10103 %      XOpenDisplay.
10104 %
10105 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10106 %
10107 %    o windows: Specifies a pointer to a XWindows structure.
10108 %
10109 %    o command: A value other than zero indicates that the file is selected
10110 %      from the command line argument list.
10111 %
10112 */
XOpenImage(Display * display,XResourceInfo * resource_info,XWindows * windows,const MagickBooleanType command)10113 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10114   XWindows *windows,const MagickBooleanType command)
10115 {
10116   const MagickInfo
10117     *magick_info;
10118 
10119   ExceptionInfo
10120     *exception;
10121 
10122   Image
10123     *nexus;
10124 
10125   ImageInfo
10126     *image_info;
10127 
10128   static char
10129     filename[MaxTextExtent] = "\0";
10130 
10131   /*
10132     Request file name from user.
10133   */
10134   if (command == MagickFalse)
10135     XFileBrowserWidget(display,windows,"Open",filename);
10136   else
10137     {
10138       char
10139         **filelist,
10140         **files;
10141 
10142       int
10143         count,
10144         status;
10145 
10146       int
10147         i,
10148         j;
10149 
10150       /*
10151         Select next image from the command line.
10152       */
10153       status=XGetCommand(display,windows->image.id,&files,&count);
10154       if (status == 0)
10155           ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10156       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10157       if (filelist == (char **) NULL)
10158         {
10159           (void) XFreeStringList(files);
10160           ThrowXWindowException(ResourceLimitError,
10161             "MemoryAllocationFailed","...");
10162           return((Image *) NULL);
10163         }
10164       j=0;
10165       for (i=1; i < count; i++)
10166         if (*files[i] != '-')
10167           filelist[j++]=files[i];
10168       filelist[j]=(char *) NULL;
10169       XListBrowserWidget(display,windows,&windows->widget,
10170         (const char **) filelist,"Load","Select Image to Load:",filename);
10171       filelist=(char **) RelinquishMagickMemory(filelist);
10172       (void) XFreeStringList(files);
10173     }
10174   if (*filename == '\0')
10175     return((Image *) NULL);
10176   image_info=CloneImageInfo(resource_info->image_info);
10177   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10178     (void *) NULL);
10179   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10180   exception=AcquireExceptionInfo();
10181   (void) SetImageInfo(image_info,0,exception);
10182   if (LocaleCompare(image_info->magick,"X") == 0)
10183     {
10184       char
10185         seconds[MaxTextExtent];
10186 
10187       /*
10188         User may want to delay the X server screen grab.
10189       */
10190       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10191       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10192         seconds);
10193       if (*seconds == '\0')
10194         return((Image *) NULL);
10195       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10196     }
10197   magick_info=GetMagickInfo(image_info->magick,exception);
10198   if ((magick_info != (const MagickInfo *) NULL) &&
10199       (magick_info->raw != MagickFalse))
10200     {
10201       char
10202         geometry[MaxTextExtent];
10203 
10204       /*
10205         Request image size from the user.
10206       */
10207       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10208       if (image_info->size != (char *) NULL)
10209         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10210       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10211         geometry);
10212       (void) CloneString(&image_info->size,geometry);
10213     }
10214   /*
10215     Load the image.
10216   */
10217   XSetCursorState(display,windows,MagickTrue);
10218   XCheckRefreshWindows(display,windows);
10219   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10220   nexus=ReadImage(image_info,exception);
10221   CatchException(exception);
10222   XSetCursorState(display,windows,MagickFalse);
10223   if (nexus != (Image *) NULL)
10224     XClientMessage(display,windows->image.id,windows->im_protocols,
10225       windows->im_next_image,CurrentTime);
10226   else
10227     {
10228       char
10229         *text,
10230         **textlist;
10231 
10232       /*
10233         Unknown image format.
10234       */
10235       text=FileToString(filename,~0UL,exception);
10236       if (text == (char *) NULL)
10237         return((Image *) NULL);
10238       textlist=StringToList(text);
10239       if (textlist != (char **) NULL)
10240         {
10241           char
10242             title[MaxTextExtent];
10243 
10244           int
10245             i;
10246 
10247           (void) FormatLocaleString(title,MaxTextExtent,
10248             "Unknown format: %s",filename);
10249           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10250             (const char **) textlist);
10251           for (i=0; textlist[i] != (char *) NULL; i++)
10252             textlist[i]=DestroyString(textlist[i]);
10253           textlist=(char **) RelinquishMagickMemory(textlist);
10254         }
10255       text=DestroyString(text);
10256     }
10257   exception=DestroyExceptionInfo(exception);
10258   image_info=DestroyImageInfo(image_info);
10259   return(nexus);
10260 }
10261 
10262 /*
10263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10264 %                                                                             %
10265 %                                                                             %
10266 %                                                                             %
10267 +   X P a n I m a g e                                                         %
10268 %                                                                             %
10269 %                                                                             %
10270 %                                                                             %
10271 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10272 %
10273 %  XPanImage() pans the image until the mouse button is released.
10274 %
10275 %  The format of the XPanImage method is:
10276 %
10277 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10278 %
10279 %  A description of each parameter follows:
10280 %
10281 %    o display: Specifies a connection to an X server;  returned from
10282 %      XOpenDisplay.
10283 %
10284 %    o windows: Specifies a pointer to a XWindows structure.
10285 %
10286 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10287 %      the entire image is refreshed.
10288 %
10289 */
XPanImage(Display * display,XWindows * windows,XEvent * event)10290 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10291 {
10292   char
10293     text[MaxTextExtent];
10294 
10295   Cursor
10296     cursor;
10297 
10298   MagickRealType
10299     x_factor,
10300     y_factor;
10301 
10302   RectangleInfo
10303     pan_info;
10304 
10305   size_t
10306     state;
10307 
10308   /*
10309     Define cursor.
10310   */
10311   if ((windows->image.ximage->width > (int) windows->image.width) &&
10312       (windows->image.ximage->height > (int) windows->image.height))
10313     cursor=XCreateFontCursor(display,XC_fleur);
10314   else
10315     if (windows->image.ximage->width > (int) windows->image.width)
10316       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10317     else
10318       if (windows->image.ximage->height > (int) windows->image.height)
10319         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10320       else
10321         cursor=XCreateFontCursor(display,XC_arrow);
10322   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10323   /*
10324     Pan image as pointer moves until the mouse button is released.
10325   */
10326   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10327   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10328   pan_info.width=windows->pan.width*windows->image.width/
10329     windows->image.ximage->width;
10330   pan_info.height=windows->pan.height*windows->image.height/
10331     windows->image.ximage->height;
10332   pan_info.x=0;
10333   pan_info.y=0;
10334   state=UpdateConfigurationState;
10335   do
10336   {
10337     switch (event->type)
10338     {
10339       case ButtonPress:
10340       {
10341         /*
10342           User choose an initial pan location.
10343         */
10344         pan_info.x=(ssize_t) event->xbutton.x;
10345         pan_info.y=(ssize_t) event->xbutton.y;
10346         state|=UpdateConfigurationState;
10347         break;
10348       }
10349       case ButtonRelease:
10350       {
10351         /*
10352           User has finished panning the image.
10353         */
10354         pan_info.x=(ssize_t) event->xbutton.x;
10355         pan_info.y=(ssize_t) event->xbutton.y;
10356         state|=UpdateConfigurationState | ExitState;
10357         break;
10358       }
10359       case MotionNotify:
10360       {
10361         pan_info.x=(ssize_t) event->xmotion.x;
10362         pan_info.y=(ssize_t) event->xmotion.y;
10363         state|=UpdateConfigurationState;
10364       }
10365       default:
10366         break;
10367     }
10368     if ((state & UpdateConfigurationState) != 0)
10369       {
10370         /*
10371           Check boundary conditions.
10372         */
10373         if (pan_info.x < (ssize_t) (pan_info.width/2))
10374           pan_info.x=0;
10375         else
10376           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10377         if (pan_info.x < 0)
10378           pan_info.x=0;
10379         else
10380           if ((int) (pan_info.x+windows->image.width) >
10381               windows->image.ximage->width)
10382             pan_info.x=(ssize_t)
10383               (windows->image.ximage->width-windows->image.width);
10384         if (pan_info.y < (ssize_t) (pan_info.height/2))
10385           pan_info.y=0;
10386         else
10387           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10388         if (pan_info.y < 0)
10389           pan_info.y=0;
10390         else
10391           if ((int) (pan_info.y+windows->image.height) >
10392               windows->image.ximage->height)
10393             pan_info.y=(ssize_t)
10394               (windows->image.ximage->height-windows->image.height);
10395         if ((windows->image.x != (int) pan_info.x) ||
10396             (windows->image.y != (int) pan_info.y))
10397           {
10398             /*
10399               Display image pan offset.
10400             */
10401             windows->image.x=(int) pan_info.x;
10402             windows->image.y=(int) pan_info.y;
10403             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10404               windows->image.width,windows->image.height,windows->image.x,
10405               windows->image.y);
10406             XInfoWidget(display,windows,text);
10407             /*
10408               Refresh Image window.
10409             */
10410             XDrawPanRectangle(display,windows);
10411             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10412           }
10413         state&=(~UpdateConfigurationState);
10414       }
10415     /*
10416       Wait for next event.
10417     */
10418     if ((state & ExitState) == 0)
10419       XScreenEvent(display,windows,event);
10420   } while ((state & ExitState) == 0);
10421   /*
10422     Restore cursor.
10423   */
10424   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10425   (void) XFreeCursor(display,cursor);
10426   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10427 }
10428 
10429 /*
10430 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10431 %                                                                             %
10432 %                                                                             %
10433 %                                                                             %
10434 +   X P a s t e I m a g e                                                     %
10435 %                                                                             %
10436 %                                                                             %
10437 %                                                                             %
10438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10439 %
10440 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10441 %  window image at a location the user chooses with the pointer.
10442 %
10443 %  The format of the XPasteImage method is:
10444 %
10445 %      MagickBooleanType XPasteImage(Display *display,
10446 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10447 %
10448 %  A description of each parameter follows:
10449 %
10450 %    o display: Specifies a connection to an X server;  returned from
10451 %      XOpenDisplay.
10452 %
10453 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10454 %
10455 %    o windows: Specifies a pointer to a XWindows structure.
10456 %
10457 %    o image: the image; returned from ReadImage.
10458 %
10459 */
XPasteImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image)10460 static MagickBooleanType XPasteImage(Display *display,
10461   XResourceInfo *resource_info,XWindows *windows,Image *image)
10462 {
10463   const char
10464     *const PasteMenu[] =
10465     {
10466       "Operator",
10467       "Help",
10468       "Dismiss",
10469       (char *) NULL
10470     };
10471 
10472   static const ModeType
10473     PasteCommands[] =
10474     {
10475       PasteOperatorsCommand,
10476       PasteHelpCommand,
10477       PasteDismissCommand
10478     };
10479 
10480   static CompositeOperator
10481     compose = CopyCompositeOp;
10482 
10483   char
10484     text[MaxTextExtent];
10485 
10486   Cursor
10487     cursor;
10488 
10489   Image
10490     *paste_image;
10491 
10492   int
10493     entry,
10494     id,
10495     x,
10496     y;
10497 
10498   MagickRealType
10499     scale_factor;
10500 
10501   RectangleInfo
10502     highlight_info,
10503     paste_info;
10504 
10505   unsigned int
10506     height,
10507     width;
10508 
10509   size_t
10510     state;
10511 
10512   XEvent
10513     event;
10514 
10515   /*
10516     Copy image.
10517   */
10518   if (resource_info->copy_image == (Image *) NULL)
10519     return(MagickFalse);
10520   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10521     &image->exception);
10522   if (paste_image == (Image *) NULL)
10523     return(MagickFalse);
10524   /*
10525     Map Command widget.
10526   */
10527   (void) CloneString(&windows->command.name,"Paste");
10528   windows->command.data=1;
10529   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10530   (void) XMapRaised(display,windows->command.id);
10531   XClientMessage(display,windows->image.id,windows->im_protocols,
10532     windows->im_update_widget,CurrentTime);
10533   /*
10534     Track pointer until button 1 is pressed.
10535   */
10536   XSetCursorState(display,windows,MagickFalse);
10537   XQueryPosition(display,windows->image.id,&x,&y);
10538   (void) XSelectInput(display,windows->image.id,
10539     windows->image.attributes.event_mask | PointerMotionMask);
10540   paste_info.x=(ssize_t) windows->image.x+x;
10541   paste_info.y=(ssize_t) windows->image.y+y;
10542   paste_info.width=0;
10543   paste_info.height=0;
10544   cursor=XCreateFontCursor(display,XC_ul_angle);
10545   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10546   state=DefaultState;
10547   do
10548   {
10549     if (windows->info.mapped != MagickFalse)
10550       {
10551         /*
10552           Display pointer position.
10553         */
10554         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10555           (long) paste_info.x,(long) paste_info.y);
10556         XInfoWidget(display,windows,text);
10557       }
10558     highlight_info=paste_info;
10559     highlight_info.x=paste_info.x-windows->image.x;
10560     highlight_info.y=paste_info.y-windows->image.y;
10561     XHighlightRectangle(display,windows->image.id,
10562       windows->image.highlight_context,&highlight_info);
10563     /*
10564       Wait for next event.
10565     */
10566     XScreenEvent(display,windows,&event);
10567     XHighlightRectangle(display,windows->image.id,
10568       windows->image.highlight_context,&highlight_info);
10569     if (event.xany.window == windows->command.id)
10570       {
10571         /*
10572           Select a command from the Command widget.
10573         */
10574         id=XCommandWidget(display,windows,PasteMenu,&event);
10575         if (id < 0)
10576           continue;
10577         switch (PasteCommands[id])
10578         {
10579           case PasteOperatorsCommand:
10580           {
10581             char
10582               command[MaxTextExtent],
10583               **operators;
10584 
10585             /*
10586               Select a command from the pop-up menu.
10587             */
10588             operators=GetCommandOptions(MagickComposeOptions);
10589             if (operators == (char **) NULL)
10590               break;
10591             entry=XMenuWidget(display,windows,PasteMenu[id],
10592               (const char **) operators,command);
10593             if (entry >= 0)
10594               compose=(CompositeOperator) ParseCommandOption(
10595                 MagickComposeOptions,MagickFalse,operators[entry]);
10596             operators=DestroyStringList(operators);
10597             break;
10598           }
10599           case PasteHelpCommand:
10600           {
10601             XTextViewHelp(display,resource_info,windows,MagickFalse,
10602               "Help Viewer - Image Composite",ImagePasteHelp);
10603             break;
10604           }
10605           case PasteDismissCommand:
10606           {
10607             /*
10608               Prematurely exit.
10609             */
10610             state|=EscapeState;
10611             state|=ExitState;
10612             break;
10613           }
10614           default:
10615             break;
10616         }
10617         continue;
10618       }
10619     switch (event.type)
10620     {
10621       case ButtonPress:
10622       {
10623         if (image->debug != MagickFalse)
10624           (void) LogMagickEvent(X11Event,GetMagickModule(),
10625             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10626             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10627         if (event.xbutton.button != Button1)
10628           break;
10629         if (event.xbutton.window != windows->image.id)
10630           break;
10631         /*
10632           Paste rectangle is relative to image configuration.
10633         */
10634         width=(unsigned int) image->columns;
10635         height=(unsigned int) image->rows;
10636         x=0;
10637         y=0;
10638         if (windows->image.crop_geometry != (char *) NULL)
10639           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10640             &width,&height);
10641         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10642         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10643         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10644         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10645         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10646         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10647         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10648         break;
10649       }
10650       case ButtonRelease:
10651       {
10652         if (image->debug != MagickFalse)
10653           (void) LogMagickEvent(X11Event,GetMagickModule(),
10654             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10655             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10656         if (event.xbutton.button != Button1)
10657           break;
10658         if (event.xbutton.window != windows->image.id)
10659           break;
10660         if ((paste_info.width != 0) && (paste_info.height != 0))
10661           {
10662             /*
10663               User has selected the location of the paste image.
10664             */
10665             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10666             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10667             state|=ExitState;
10668           }
10669         break;
10670       }
10671       case Expose:
10672         break;
10673       case KeyPress:
10674       {
10675         char
10676           command[MaxTextExtent];
10677 
10678         KeySym
10679           key_symbol;
10680 
10681         int
10682           length;
10683 
10684         if (event.xkey.window != windows->image.id)
10685           break;
10686         /*
10687           Respond to a user key press.
10688         */
10689         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10690           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10691         *(command+length)='\0';
10692         if (image->debug != MagickFalse)
10693           (void) LogMagickEvent(X11Event,GetMagickModule(),
10694             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10695         switch ((int) key_symbol)
10696         {
10697           case XK_Escape:
10698           case XK_F20:
10699           {
10700             /*
10701               Prematurely exit.
10702             */
10703             paste_image=DestroyImage(paste_image);
10704             state|=EscapeState;
10705             state|=ExitState;
10706             break;
10707           }
10708           case XK_F1:
10709           case XK_Help:
10710           {
10711             (void) XSetFunction(display,windows->image.highlight_context,
10712               GXcopy);
10713             XTextViewHelp(display,resource_info,windows,MagickFalse,
10714               "Help Viewer - Image Composite",ImagePasteHelp);
10715             (void) XSetFunction(display,windows->image.highlight_context,
10716               GXinvert);
10717             break;
10718           }
10719           default:
10720           {
10721             (void) XBell(display,0);
10722             break;
10723           }
10724         }
10725         break;
10726       }
10727       case MotionNotify:
10728       {
10729         /*
10730           Map and unmap Info widget as text cursor crosses its boundaries.
10731         */
10732         x=event.xmotion.x;
10733         y=event.xmotion.y;
10734         if (windows->info.mapped != MagickFalse)
10735           {
10736             if ((x < (int) (windows->info.x+windows->info.width)) &&
10737                 (y < (int) (windows->info.y+windows->info.height)))
10738               (void) XWithdrawWindow(display,windows->info.id,
10739                 windows->info.screen);
10740           }
10741         else
10742           if ((x > (int) (windows->info.x+windows->info.width)) ||
10743               (y > (int) (windows->info.y+windows->info.height)))
10744             (void) XMapWindow(display,windows->info.id);
10745         paste_info.x=(ssize_t) windows->image.x+x;
10746         paste_info.y=(ssize_t) windows->image.y+y;
10747         break;
10748       }
10749       default:
10750       {
10751         if (image->debug != MagickFalse)
10752           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10753             event.type);
10754         break;
10755       }
10756     }
10757   } while ((state & ExitState) == 0);
10758   (void) XSelectInput(display,windows->image.id,
10759     windows->image.attributes.event_mask);
10760   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10761   XSetCursorState(display,windows,MagickFalse);
10762   (void) XFreeCursor(display,cursor);
10763   if ((state & EscapeState) != 0)
10764     return(MagickTrue);
10765   /*
10766     Image pasting is relative to image configuration.
10767   */
10768   XSetCursorState(display,windows,MagickTrue);
10769   XCheckRefreshWindows(display,windows);
10770   width=(unsigned int) image->columns;
10771   height=(unsigned int) image->rows;
10772   x=0;
10773   y=0;
10774   if (windows->image.crop_geometry != (char *) NULL)
10775     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10776   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10777   paste_info.x+=x;
10778   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10779   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10780   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10781   paste_info.y+=y;
10782   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10783   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10784   /*
10785     Paste image with X Image window.
10786   */
10787   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10788   paste_image=DestroyImage(paste_image);
10789   XSetCursorState(display,windows,MagickFalse);
10790   /*
10791     Update image colormap.
10792   */
10793   XConfigureImageColormap(display,resource_info,windows,image);
10794   (void) XConfigureImage(display,resource_info,windows,image);
10795   return(MagickTrue);
10796 }
10797 
10798 /*
10799 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10800 %                                                                             %
10801 %                                                                             %
10802 %                                                                             %
10803 +   X P r i n t I m a g e                                                     %
10804 %                                                                             %
10805 %                                                                             %
10806 %                                                                             %
10807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10808 %
10809 %  XPrintImage() prints an image to a Postscript printer.
10810 %
10811 %  The format of the XPrintImage method is:
10812 %
10813 %      MagickBooleanType XPrintImage(Display *display,
10814 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10815 %
10816 %  A description of each parameter follows:
10817 %
10818 %    o display: Specifies a connection to an X server; returned from
10819 %      XOpenDisplay.
10820 %
10821 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10822 %
10823 %    o windows: Specifies a pointer to a XWindows structure.
10824 %
10825 %    o image: the image.
10826 %
10827 */
XPrintImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image)10828 static MagickBooleanType XPrintImage(Display *display,
10829   XResourceInfo *resource_info,XWindows *windows,Image *image)
10830 {
10831   char
10832     filename[MaxTextExtent],
10833     geometry[MaxTextExtent];
10834 
10835   const char
10836     *const PageSizes[] =
10837     {
10838       "Letter",
10839       "Tabloid",
10840       "Ledger",
10841       "Legal",
10842       "Statement",
10843       "Executive",
10844       "A3",
10845       "A4",
10846       "A5",
10847       "B4",
10848       "B5",
10849       "Folio",
10850       "Quarto",
10851       "10x14",
10852       (char *) NULL
10853     };
10854 
10855   Image
10856     *print_image;
10857 
10858   ImageInfo
10859     *image_info;
10860 
10861   MagickStatusType
10862     status;
10863 
10864   /*
10865     Request Postscript page geometry from user.
10866   */
10867   image_info=CloneImageInfo(resource_info->image_info);
10868   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10869   if (image_info->page != (char *) NULL)
10870     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10871   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10872     "Select Postscript Page Geometry:",geometry);
10873   if (*geometry == '\0')
10874     return(MagickTrue);
10875   image_info->page=GetPageGeometry(geometry);
10876   /*
10877     Apply image transforms.
10878   */
10879   XSetCursorState(display,windows,MagickTrue);
10880   XCheckRefreshWindows(display,windows);
10881   print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10882   if (print_image == (Image *) NULL)
10883     return(MagickFalse);
10884   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10885     windows->image.ximage->width,windows->image.ximage->height);
10886   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10887   /*
10888     Print image.
10889   */
10890   (void) AcquireUniqueFilename(filename);
10891   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10892     filename);
10893   status=WriteImage(image_info,print_image);
10894   (void) RelinquishUniqueFileResource(filename);
10895   print_image=DestroyImage(print_image);
10896   image_info=DestroyImageInfo(image_info);
10897   XSetCursorState(display,windows,MagickFalse);
10898   return(status != 0 ? MagickTrue : MagickFalse);
10899 }
10900 
10901 /*
10902 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10903 %                                                                             %
10904 %                                                                             %
10905 %                                                                             %
10906 +   X R O I I m a g e                                                         %
10907 %                                                                             %
10908 %                                                                             %
10909 %                                                                             %
10910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10911 %
10912 %  XROIImage() applies an image processing technique to a region of interest.
10913 %
10914 %  The format of the XROIImage method is:
10915 %
10916 %      MagickBooleanType XROIImage(Display *display,
10917 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
10918 %
10919 %  A description of each parameter follows:
10920 %
10921 %    o display: Specifies a connection to an X server; returned from
10922 %      XOpenDisplay.
10923 %
10924 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10925 %
10926 %    o windows: Specifies a pointer to a XWindows structure.
10927 %
10928 %    o image: the image; returned from ReadImage.
10929 %
10930 */
XROIImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image)10931 static MagickBooleanType XROIImage(Display *display,
10932   XResourceInfo *resource_info,XWindows *windows,Image **image)
10933 {
10934 #define ApplyMenus  7
10935 
10936   const char
10937     *const ROIMenu[] =
10938     {
10939       "Help",
10940       "Dismiss",
10941       (char *) NULL
10942     },
10943     *const ApplyMenu[] =
10944     {
10945       "File",
10946       "Edit",
10947       "Transform",
10948       "Enhance",
10949       "Effects",
10950       "F/X",
10951       "Miscellany",
10952       "Help",
10953       "Dismiss",
10954       (char *) NULL
10955     },
10956     *const FileMenu[] =
10957     {
10958       "Save...",
10959       "Print...",
10960       (char *) NULL
10961     },
10962     *const EditMenu[] =
10963     {
10964       "Undo",
10965       "Redo",
10966       (char *) NULL
10967     },
10968     *const TransformMenu[] =
10969     {
10970       "Flop",
10971       "Flip",
10972       "Rotate Right",
10973       "Rotate Left",
10974       (char *) NULL
10975     },
10976     *const EnhanceMenu[] =
10977     {
10978       "Hue...",
10979       "Saturation...",
10980       "Brightness...",
10981       "Gamma...",
10982       "Spiff",
10983       "Dull",
10984       "Contrast Stretch...",
10985       "Sigmoidal Contrast...",
10986       "Normalize",
10987       "Equalize",
10988       "Negate",
10989       "Grayscale",
10990       "Map...",
10991       "Quantize...",
10992       (char *) NULL
10993     },
10994     *const EffectsMenu[] =
10995     {
10996       "Despeckle",
10997       "Emboss",
10998       "Reduce Noise",
10999       "Add Noise",
11000       "Sharpen...",
11001       "Blur...",
11002       "Threshold...",
11003       "Edge Detect...",
11004       "Spread...",
11005       "Shade...",
11006       "Raise...",
11007       "Segment...",
11008       (char *) NULL
11009     },
11010     *const FXMenu[] =
11011     {
11012       "Solarize...",
11013       "Sepia Tone...",
11014       "Swirl...",
11015       "Implode...",
11016       "Vignette...",
11017       "Wave...",
11018       "Oil Paint...",
11019       "Charcoal Draw...",
11020       (char *) NULL
11021     },
11022     *const MiscellanyMenu[] =
11023     {
11024       "Image Info",
11025       "Zoom Image",
11026       "Show Preview...",
11027       "Show Histogram",
11028       "Show Matte",
11029       (char *) NULL
11030     };
11031 
11032   const char
11033     *const *Menus[ApplyMenus] =
11034     {
11035       FileMenu,
11036       EditMenu,
11037       TransformMenu,
11038       EnhanceMenu,
11039       EffectsMenu,
11040       FXMenu,
11041       MiscellanyMenu
11042     };
11043 
11044   static const CommandType
11045     ApplyCommands[] =
11046     {
11047       NullCommand,
11048       NullCommand,
11049       NullCommand,
11050       NullCommand,
11051       NullCommand,
11052       NullCommand,
11053       NullCommand,
11054       HelpCommand,
11055       QuitCommand
11056     },
11057     FileCommands[] =
11058     {
11059       SaveCommand,
11060       PrintCommand
11061     },
11062     EditCommands[] =
11063     {
11064       UndoCommand,
11065       RedoCommand
11066     },
11067     TransformCommands[] =
11068     {
11069       FlopCommand,
11070       FlipCommand,
11071       RotateRightCommand,
11072       RotateLeftCommand
11073     },
11074     EnhanceCommands[] =
11075     {
11076       HueCommand,
11077       SaturationCommand,
11078       BrightnessCommand,
11079       GammaCommand,
11080       SpiffCommand,
11081       DullCommand,
11082       ContrastStretchCommand,
11083       SigmoidalContrastCommand,
11084       NormalizeCommand,
11085       EqualizeCommand,
11086       NegateCommand,
11087       GrayscaleCommand,
11088       MapCommand,
11089       QuantizeCommand
11090     },
11091     EffectsCommands[] =
11092     {
11093       DespeckleCommand,
11094       EmbossCommand,
11095       ReduceNoiseCommand,
11096       AddNoiseCommand,
11097       SharpenCommand,
11098       BlurCommand,
11099       EdgeDetectCommand,
11100       SpreadCommand,
11101       ShadeCommand,
11102       RaiseCommand,
11103       SegmentCommand
11104     },
11105     FXCommands[] =
11106     {
11107       SolarizeCommand,
11108       SepiaToneCommand,
11109       SwirlCommand,
11110       ImplodeCommand,
11111       VignetteCommand,
11112       WaveCommand,
11113       OilPaintCommand,
11114       CharcoalDrawCommand
11115     },
11116     MiscellanyCommands[] =
11117     {
11118       InfoCommand,
11119       ZoomCommand,
11120       ShowPreviewCommand,
11121       ShowHistogramCommand,
11122       ShowMatteCommand
11123     },
11124     ROICommands[] =
11125     {
11126       ROIHelpCommand,
11127       ROIDismissCommand
11128     };
11129 
11130   static const CommandType
11131     *Commands[ApplyMenus] =
11132     {
11133       FileCommands,
11134       EditCommands,
11135       TransformCommands,
11136       EnhanceCommands,
11137       EffectsCommands,
11138       FXCommands,
11139       MiscellanyCommands
11140     };
11141 
11142   char
11143     command[MaxTextExtent],
11144     text[MaxTextExtent];
11145 
11146   CommandType
11147     command_type;
11148 
11149   Cursor
11150     cursor;
11151 
11152   Image
11153     *roi_image;
11154 
11155   int
11156     entry,
11157     id,
11158     x,
11159     y;
11160 
11161   MagickRealType
11162     scale_factor;
11163 
11164   MagickProgressMonitor
11165     progress_monitor;
11166 
11167   RectangleInfo
11168     crop_info,
11169     highlight_info,
11170     roi_info;
11171 
11172   unsigned int
11173     height,
11174     width;
11175 
11176   size_t
11177     state;
11178 
11179   XEvent
11180     event;
11181 
11182   /*
11183     Map Command widget.
11184   */
11185   (void) CloneString(&windows->command.name,"ROI");
11186   windows->command.data=0;
11187   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11188   (void) XMapRaised(display,windows->command.id);
11189   XClientMessage(display,windows->image.id,windows->im_protocols,
11190     windows->im_update_widget,CurrentTime);
11191   /*
11192     Track pointer until button 1 is pressed.
11193   */
11194   XQueryPosition(display,windows->image.id,&x,&y);
11195   (void) XSelectInput(display,windows->image.id,
11196     windows->image.attributes.event_mask | PointerMotionMask);
11197   crop_info.width=0;
11198   crop_info.height=0;
11199   crop_info.x=0;
11200   crop_info.y=0;
11201   roi_info.x=(ssize_t) windows->image.x+x;
11202   roi_info.y=(ssize_t) windows->image.y+y;
11203   roi_info.width=0;
11204   roi_info.height=0;
11205   cursor=XCreateFontCursor(display,XC_fleur);
11206   state=DefaultState;
11207   do
11208   {
11209     if (windows->info.mapped != MagickFalse)
11210       {
11211         /*
11212           Display pointer position.
11213         */
11214         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11215           (long) roi_info.x,(long) roi_info.y);
11216         XInfoWidget(display,windows,text);
11217       }
11218     /*
11219       Wait for next event.
11220     */
11221     XScreenEvent(display,windows,&event);
11222     if (event.xany.window == windows->command.id)
11223       {
11224         /*
11225           Select a command from the Command widget.
11226         */
11227         id=XCommandWidget(display,windows,ROIMenu,&event);
11228         if (id < 0)
11229           continue;
11230         switch (ROICommands[id])
11231         {
11232           case ROIHelpCommand:
11233           {
11234             XTextViewHelp(display,resource_info,windows,MagickFalse,
11235               "Help Viewer - Region of Interest",ImageROIHelp);
11236             break;
11237           }
11238           case ROIDismissCommand:
11239           {
11240             /*
11241               Prematurely exit.
11242             */
11243             state|=EscapeState;
11244             state|=ExitState;
11245             break;
11246           }
11247           default:
11248             break;
11249         }
11250         continue;
11251       }
11252     switch (event.type)
11253     {
11254       case ButtonPress:
11255       {
11256         if (event.xbutton.button != Button1)
11257           break;
11258         if (event.xbutton.window != windows->image.id)
11259           break;
11260         /*
11261           Note first corner of region of interest rectangle-- exit loop.
11262         */
11263         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11264         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11265         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11266         state|=ExitState;
11267         break;
11268       }
11269       case ButtonRelease:
11270         break;
11271       case Expose:
11272         break;
11273       case KeyPress:
11274       {
11275         KeySym
11276           key_symbol;
11277 
11278         if (event.xkey.window != windows->image.id)
11279           break;
11280         /*
11281           Respond to a user key press.
11282         */
11283         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11284           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11285         switch ((int) key_symbol)
11286         {
11287           case XK_Escape:
11288           case XK_F20:
11289           {
11290             /*
11291               Prematurely exit.
11292             */
11293             state|=EscapeState;
11294             state|=ExitState;
11295             break;
11296           }
11297           case XK_F1:
11298           case XK_Help:
11299           {
11300             XTextViewHelp(display,resource_info,windows,MagickFalse,
11301               "Help Viewer - Region of Interest",ImageROIHelp);
11302             break;
11303           }
11304           default:
11305           {
11306             (void) XBell(display,0);
11307             break;
11308           }
11309         }
11310         break;
11311       }
11312       case MotionNotify:
11313       {
11314         /*
11315           Map and unmap Info widget as text cursor crosses its boundaries.
11316         */
11317         x=event.xmotion.x;
11318         y=event.xmotion.y;
11319         if (windows->info.mapped != MagickFalse)
11320           {
11321             if ((x < (int) (windows->info.x+windows->info.width)) &&
11322                 (y < (int) (windows->info.y+windows->info.height)))
11323               (void) XWithdrawWindow(display,windows->info.id,
11324                 windows->info.screen);
11325           }
11326         else
11327           if ((x > (int) (windows->info.x+windows->info.width)) ||
11328               (y > (int) (windows->info.y+windows->info.height)))
11329             (void) XMapWindow(display,windows->info.id);
11330         roi_info.x=(ssize_t) windows->image.x+x;
11331         roi_info.y=(ssize_t) windows->image.y+y;
11332         break;
11333       }
11334       default:
11335         break;
11336     }
11337   } while ((state & ExitState) == 0);
11338   (void) XSelectInput(display,windows->image.id,
11339     windows->image.attributes.event_mask);
11340   if ((state & EscapeState) != 0)
11341     {
11342       /*
11343         User want to exit without region of interest.
11344       */
11345       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11346       (void) XFreeCursor(display,cursor);
11347       return(MagickTrue);
11348     }
11349   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11350   do
11351   {
11352     /*
11353       Size rectangle as pointer moves until the mouse button is released.
11354     */
11355     x=(int) roi_info.x;
11356     y=(int) roi_info.y;
11357     roi_info.width=0;
11358     roi_info.height=0;
11359     state=DefaultState;
11360     do
11361     {
11362       highlight_info=roi_info;
11363       highlight_info.x=roi_info.x-windows->image.x;
11364       highlight_info.y=roi_info.y-windows->image.y;
11365       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11366         {
11367           /*
11368             Display info and draw region of interest rectangle.
11369           */
11370           if (windows->info.mapped == MagickFalse)
11371             (void) XMapWindow(display,windows->info.id);
11372           (void) FormatLocaleString(text,MaxTextExtent,
11373             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11374             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11375           XInfoWidget(display,windows,text);
11376           XHighlightRectangle(display,windows->image.id,
11377             windows->image.highlight_context,&highlight_info);
11378         }
11379       else
11380         if (windows->info.mapped != MagickFalse)
11381           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11382       /*
11383         Wait for next event.
11384       */
11385       XScreenEvent(display,windows,&event);
11386       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11387         XHighlightRectangle(display,windows->image.id,
11388           windows->image.highlight_context,&highlight_info);
11389       switch (event.type)
11390       {
11391         case ButtonPress:
11392         {
11393           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11394           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11395           break;
11396         }
11397         case ButtonRelease:
11398         {
11399           /*
11400             User has committed to region of interest rectangle.
11401           */
11402           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11403           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11404           XSetCursorState(display,windows,MagickFalse);
11405           state|=ExitState;
11406           if (LocaleCompare(windows->command.name,"Apply") == 0)
11407             break;
11408           (void) CloneString(&windows->command.name,"Apply");
11409           windows->command.data=ApplyMenus;
11410           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11411           break;
11412         }
11413         case Expose:
11414           break;
11415         case MotionNotify:
11416         {
11417           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11418           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11419         }
11420         default:
11421           break;
11422       }
11423       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11424           ((state & ExitState) != 0))
11425         {
11426           /*
11427             Check boundary conditions.
11428           */
11429           if (roi_info.x < 0)
11430             roi_info.x=0;
11431           else
11432             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11433               roi_info.x=(ssize_t) windows->image.ximage->width;
11434           if ((int) roi_info.x < x)
11435             roi_info.width=(unsigned int) (x-roi_info.x);
11436           else
11437             {
11438               roi_info.width=(unsigned int) (roi_info.x-x);
11439               roi_info.x=(ssize_t) x;
11440             }
11441           if (roi_info.y < 0)
11442             roi_info.y=0;
11443           else
11444             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11445               roi_info.y=(ssize_t) windows->image.ximage->height;
11446           if ((int) roi_info.y < y)
11447             roi_info.height=(unsigned int) (y-roi_info.y);
11448           else
11449             {
11450               roi_info.height=(unsigned int) (roi_info.y-y);
11451               roi_info.y=(ssize_t) y;
11452             }
11453         }
11454     } while ((state & ExitState) == 0);
11455     /*
11456       Wait for user to grab a corner of the rectangle or press return.
11457     */
11458     state=DefaultState;
11459     command_type=NullCommand;
11460     (void) XMapWindow(display,windows->info.id);
11461     do
11462     {
11463       if (windows->info.mapped != MagickFalse)
11464         {
11465           /*
11466             Display pointer position.
11467           */
11468           (void) FormatLocaleString(text,MaxTextExtent,
11469             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11470             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11471           XInfoWidget(display,windows,text);
11472         }
11473       highlight_info=roi_info;
11474       highlight_info.x=roi_info.x-windows->image.x;
11475       highlight_info.y=roi_info.y-windows->image.y;
11476       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11477         {
11478           state|=EscapeState;
11479           state|=ExitState;
11480           break;
11481         }
11482       if ((state & UpdateRegionState) != 0)
11483         {
11484           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11485           switch (command_type)
11486           {
11487             case UndoCommand:
11488             case RedoCommand:
11489             {
11490               (void) XMagickCommand(display,resource_info,windows,command_type,
11491                 image);
11492               break;
11493             }
11494             default:
11495             {
11496               /*
11497                 Region of interest is relative to image configuration.
11498               */
11499               progress_monitor=SetImageProgressMonitor(*image,
11500                 (MagickProgressMonitor) NULL,(*image)->client_data);
11501               crop_info=roi_info;
11502               width=(unsigned int) (*image)->columns;
11503               height=(unsigned int) (*image)->rows;
11504               x=0;
11505               y=0;
11506               if (windows->image.crop_geometry != (char *) NULL)
11507                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11508                   &width,&height);
11509               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11510               crop_info.x+=x;
11511               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11512               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11513               scale_factor=(MagickRealType)
11514                 height/windows->image.ximage->height;
11515               crop_info.y+=y;
11516               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11517               crop_info.height=(unsigned int)
11518                 (scale_factor*crop_info.height+0.5);
11519               roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11520               (void) SetImageProgressMonitor(*image,progress_monitor,
11521                 (*image)->client_data);
11522               if (roi_image == (Image *) NULL)
11523                 continue;
11524               /*
11525                 Apply image processing technique to the region of interest.
11526               */
11527               windows->image.orphan=MagickTrue;
11528               (void) XMagickCommand(display,resource_info,windows,command_type,
11529                 &roi_image);
11530               progress_monitor=SetImageProgressMonitor(*image,
11531                 (MagickProgressMonitor) NULL,(*image)->client_data);
11532               (void) XMagickCommand(display,resource_info,windows,
11533                 SaveToUndoBufferCommand,image);
11534               windows->image.orphan=MagickFalse;
11535               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11536                 crop_info.x,crop_info.y);
11537               roi_image=DestroyImage(roi_image);
11538               (void) SetImageProgressMonitor(*image,progress_monitor,
11539                 (*image)->client_data);
11540               break;
11541             }
11542           }
11543           if (command_type != InfoCommand)
11544             {
11545               XConfigureImageColormap(display,resource_info,windows,*image);
11546               (void) XConfigureImage(display,resource_info,windows,*image);
11547             }
11548           XCheckRefreshWindows(display,windows);
11549           XInfoWidget(display,windows,text);
11550           (void) XSetFunction(display,windows->image.highlight_context,
11551             GXinvert);
11552           state&=(~UpdateRegionState);
11553         }
11554       XHighlightRectangle(display,windows->image.id,
11555         windows->image.highlight_context,&highlight_info);
11556       XScreenEvent(display,windows,&event);
11557       if (event.xany.window == windows->command.id)
11558         {
11559           /*
11560             Select a command from the Command widget.
11561           */
11562           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11563           command_type=NullCommand;
11564           id=XCommandWidget(display,windows,ApplyMenu,&event);
11565           if (id >= 0)
11566             {
11567               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11568               command_type=ApplyCommands[id];
11569               if (id < ApplyMenus)
11570                 {
11571                   /*
11572                     Select a command from a pop-up menu.
11573                   */
11574                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11575                     (const char **) Menus[id],command);
11576                   if (entry >= 0)
11577                     {
11578                       (void) CopyMagickString(command,Menus[id][entry],
11579                         MaxTextExtent);
11580                       command_type=Commands[id][entry];
11581                     }
11582                 }
11583             }
11584           (void) XSetFunction(display,windows->image.highlight_context,
11585             GXinvert);
11586           XHighlightRectangle(display,windows->image.id,
11587             windows->image.highlight_context,&highlight_info);
11588           if (command_type == HelpCommand)
11589             {
11590               (void) XSetFunction(display,windows->image.highlight_context,
11591                 GXcopy);
11592               XTextViewHelp(display,resource_info,windows,MagickFalse,
11593                 "Help Viewer - Region of Interest",ImageROIHelp);
11594               (void) XSetFunction(display,windows->image.highlight_context,
11595                 GXinvert);
11596               continue;
11597             }
11598           if (command_type == QuitCommand)
11599             {
11600               /*
11601                 exit.
11602               */
11603               state|=EscapeState;
11604               state|=ExitState;
11605               continue;
11606             }
11607           if (command_type != NullCommand)
11608             state|=UpdateRegionState;
11609           continue;
11610         }
11611       XHighlightRectangle(display,windows->image.id,
11612         windows->image.highlight_context,&highlight_info);
11613       switch (event.type)
11614       {
11615         case ButtonPress:
11616         {
11617           x=windows->image.x;
11618           y=windows->image.y;
11619           if (event.xbutton.button != Button1)
11620             break;
11621           if (event.xbutton.window != windows->image.id)
11622             break;
11623           x=windows->image.x+event.xbutton.x;
11624           y=windows->image.y+event.xbutton.y;
11625           if ((x < (int) (roi_info.x+RoiDelta)) &&
11626               (x > (int) (roi_info.x-RoiDelta)) &&
11627               (y < (int) (roi_info.y+RoiDelta)) &&
11628               (y > (int) (roi_info.y-RoiDelta)))
11629             {
11630               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11631               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11632               state|=UpdateConfigurationState;
11633               break;
11634             }
11635           if ((x < (int) (roi_info.x+RoiDelta)) &&
11636               (x > (int) (roi_info.x-RoiDelta)) &&
11637               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11638               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11639             {
11640               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11641               state|=UpdateConfigurationState;
11642               break;
11643             }
11644           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11645               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11646               (y < (int) (roi_info.y+RoiDelta)) &&
11647               (y > (int) (roi_info.y-RoiDelta)))
11648             {
11649               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11650               state|=UpdateConfigurationState;
11651               break;
11652             }
11653           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11654               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11655               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11656               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11657             {
11658               state|=UpdateConfigurationState;
11659               break;
11660             }
11661         }
11662         case ButtonRelease:
11663         {
11664           if (event.xbutton.window == windows->pan.id)
11665             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11666                 (highlight_info.y != crop_info.y-windows->image.y))
11667               XHighlightRectangle(display,windows->image.id,
11668                 windows->image.highlight_context,&highlight_info);
11669           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11670             event.xbutton.time);
11671           break;
11672         }
11673         case Expose:
11674         {
11675           if (event.xexpose.window == windows->image.id)
11676             if (event.xexpose.count == 0)
11677               {
11678                 event.xexpose.x=(int) highlight_info.x;
11679                 event.xexpose.y=(int) highlight_info.y;
11680                 event.xexpose.width=(int) highlight_info.width;
11681                 event.xexpose.height=(int) highlight_info.height;
11682                 XRefreshWindow(display,&windows->image,&event);
11683               }
11684           if (event.xexpose.window == windows->info.id)
11685             if (event.xexpose.count == 0)
11686               XInfoWidget(display,windows,text);
11687           break;
11688         }
11689         case KeyPress:
11690         {
11691           KeySym
11692             key_symbol;
11693 
11694           if (event.xkey.window != windows->image.id)
11695             break;
11696           /*
11697             Respond to a user key press.
11698           */
11699           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11700             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11701           switch ((int) key_symbol)
11702           {
11703             case XK_Shift_L:
11704             case XK_Shift_R:
11705               break;
11706             case XK_Escape:
11707             case XK_F20:
11708               state|=EscapeState;
11709             case XK_Return:
11710             {
11711               state|=ExitState;
11712               break;
11713             }
11714             case XK_Home:
11715             case XK_KP_Home:
11716             {
11717               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11718               roi_info.y=(ssize_t) (windows->image.height/2L-
11719                 roi_info.height/2L);
11720               break;
11721             }
11722             case XK_Left:
11723             case XK_KP_Left:
11724             {
11725               roi_info.x--;
11726               break;
11727             }
11728             case XK_Up:
11729             case XK_KP_Up:
11730             case XK_Next:
11731             {
11732               roi_info.y--;
11733               break;
11734             }
11735             case XK_Right:
11736             case XK_KP_Right:
11737             {
11738               roi_info.x++;
11739               break;
11740             }
11741             case XK_Prior:
11742             case XK_Down:
11743             case XK_KP_Down:
11744             {
11745               roi_info.y++;
11746               break;
11747             }
11748             case XK_F1:
11749             case XK_Help:
11750             {
11751               (void) XSetFunction(display,windows->image.highlight_context,
11752                 GXcopy);
11753               XTextViewHelp(display,resource_info,windows,MagickFalse,
11754                 "Help Viewer - Region of Interest",ImageROIHelp);
11755               (void) XSetFunction(display,windows->image.highlight_context,
11756                 GXinvert);
11757               break;
11758             }
11759             default:
11760             {
11761               command_type=XImageWindowCommand(display,resource_info,windows,
11762                 event.xkey.state,key_symbol,image);
11763               if (command_type != NullCommand)
11764                 state|=UpdateRegionState;
11765               break;
11766             }
11767           }
11768           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11769             event.xkey.time);
11770           break;
11771         }
11772         case KeyRelease:
11773           break;
11774         case MotionNotify:
11775         {
11776           if (event.xbutton.window != windows->image.id)
11777             break;
11778           /*
11779             Map and unmap Info widget as text cursor crosses its boundaries.
11780           */
11781           x=event.xmotion.x;
11782           y=event.xmotion.y;
11783           if (windows->info.mapped != MagickFalse)
11784             {
11785               if ((x < (int) (windows->info.x+windows->info.width)) &&
11786                   (y < (int) (windows->info.y+windows->info.height)))
11787                 (void) XWithdrawWindow(display,windows->info.id,
11788                   windows->info.screen);
11789             }
11790           else
11791             if ((x > (int) (windows->info.x+windows->info.width)) ||
11792                 (y > (int) (windows->info.y+windows->info.height)))
11793               (void) XMapWindow(display,windows->info.id);
11794           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11795           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11796           break;
11797         }
11798         case SelectionRequest:
11799         {
11800           XSelectionEvent
11801             notify;
11802 
11803           XSelectionRequestEvent
11804             *request;
11805 
11806           /*
11807             Set primary selection.
11808           */
11809           (void) FormatLocaleString(text,MaxTextExtent,
11810             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11811             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11812           request=(&(event.xselectionrequest));
11813           (void) XChangeProperty(request->display,request->requestor,
11814             request->property,request->target,8,PropModeReplace,
11815             (unsigned char *) text,(int) strlen(text));
11816           notify.type=SelectionNotify;
11817           notify.display=request->display;
11818           notify.requestor=request->requestor;
11819           notify.selection=request->selection;
11820           notify.target=request->target;
11821           notify.time=request->time;
11822           if (request->property == None)
11823             notify.property=request->target;
11824           else
11825             notify.property=request->property;
11826           (void) XSendEvent(request->display,request->requestor,False,0,
11827             (XEvent *) &notify);
11828         }
11829         default:
11830           break;
11831       }
11832       if ((state & UpdateConfigurationState) != 0)
11833         {
11834           (void) XPutBackEvent(display,&event);
11835           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11836           break;
11837         }
11838     } while ((state & ExitState) == 0);
11839   } while ((state & ExitState) == 0);
11840   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11841   XSetCursorState(display,windows,MagickFalse);
11842   if ((state & EscapeState) != 0)
11843     return(MagickTrue);
11844   return(MagickTrue);
11845 }
11846 
11847 /*
11848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11849 %                                                                             %
11850 %                                                                             %
11851 %                                                                             %
11852 +   X R o t a t e I m a g e                                                   %
11853 %                                                                             %
11854 %                                                                             %
11855 %                                                                             %
11856 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11857 %
11858 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11859 %  rotation angle is computed from the slope of a line drawn by the user.
11860 %
11861 %  The format of the XRotateImage method is:
11862 %
11863 %      MagickBooleanType XRotateImage(Display *display,
11864 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11865 %        Image **image)
11866 %
11867 %  A description of each parameter follows:
11868 %
11869 %    o display: Specifies a connection to an X server; returned from
11870 %      XOpenDisplay.
11871 %
11872 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11873 %
11874 %    o windows: Specifies a pointer to a XWindows structure.
11875 %
11876 %    o degrees: Specifies the number of degrees to rotate the image.
11877 %
11878 %    o image: the image.
11879 %
11880 */
XRotateImage(Display * display,XResourceInfo * resource_info,XWindows * windows,double degrees,Image ** image)11881 static MagickBooleanType XRotateImage(Display *display,
11882   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11883 {
11884   const char
11885     *const RotateMenu[] =
11886     {
11887       "Pixel Color",
11888       "Direction",
11889       "Help",
11890       "Dismiss",
11891       (char *) NULL
11892     };
11893 
11894   static ModeType
11895     direction = HorizontalRotateCommand;
11896 
11897   static const ModeType
11898     DirectionCommands[] =
11899     {
11900       HorizontalRotateCommand,
11901       VerticalRotateCommand
11902     },
11903     RotateCommands[] =
11904     {
11905       RotateColorCommand,
11906       RotateDirectionCommand,
11907       RotateHelpCommand,
11908       RotateDismissCommand
11909     };
11910 
11911   static unsigned int
11912     pen_id = 0;
11913 
11914   char
11915     command[MaxTextExtent],
11916     text[MaxTextExtent];
11917 
11918   Image
11919     *rotate_image;
11920 
11921   int
11922     id,
11923     x,
11924     y;
11925 
11926   MagickRealType
11927     normalized_degrees;
11928 
11929   int
11930     i;
11931 
11932   unsigned int
11933     height,
11934     rotations,
11935     width;
11936 
11937   if (degrees == 0.0)
11938     {
11939       unsigned int
11940         distance;
11941 
11942       size_t
11943         state;
11944 
11945       XEvent
11946         event;
11947 
11948       XSegment
11949         rotate_info;
11950 
11951       /*
11952         Map Command widget.
11953       */
11954       (void) CloneString(&windows->command.name,"Rotate");
11955       windows->command.data=2;
11956       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11957       (void) XMapRaised(display,windows->command.id);
11958       XClientMessage(display,windows->image.id,windows->im_protocols,
11959         windows->im_update_widget,CurrentTime);
11960       /*
11961         Wait for first button press.
11962       */
11963       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11964       XQueryPosition(display,windows->image.id,&x,&y);
11965       rotate_info.x1=x;
11966       rotate_info.y1=y;
11967       rotate_info.x2=x;
11968       rotate_info.y2=y;
11969       state=DefaultState;
11970       do
11971       {
11972         XHighlightLine(display,windows->image.id,
11973           windows->image.highlight_context,&rotate_info);
11974         /*
11975           Wait for next event.
11976         */
11977         XScreenEvent(display,windows,&event);
11978         XHighlightLine(display,windows->image.id,
11979           windows->image.highlight_context,&rotate_info);
11980         if (event.xany.window == windows->command.id)
11981           {
11982             /*
11983               Select a command from the Command widget.
11984             */
11985             id=XCommandWidget(display,windows,RotateMenu,&event);
11986             if (id < 0)
11987               continue;
11988             (void) XSetFunction(display,windows->image.highlight_context,
11989               GXcopy);
11990             switch (RotateCommands[id])
11991             {
11992               case RotateColorCommand:
11993               {
11994                 const char
11995                   *ColorMenu[MaxNumberPens];
11996 
11997                 int
11998                   pen_number;
11999 
12000                 XColor
12001                   color;
12002 
12003                 /*
12004                   Initialize menu selections.
12005                 */
12006                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12007                   ColorMenu[i]=resource_info->pen_colors[i];
12008                 ColorMenu[MaxNumberPens-2]="Browser...";
12009                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12010                 /*
12011                   Select a pen color from the pop-up menu.
12012                 */
12013                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12014                   (const char **) ColorMenu,command);
12015                 if (pen_number < 0)
12016                   break;
12017                 if (pen_number == (MaxNumberPens-2))
12018                   {
12019                     static char
12020                       color_name[MaxTextExtent] = "gray";
12021 
12022                     /*
12023                       Select a pen color from a dialog.
12024                     */
12025                     resource_info->pen_colors[pen_number]=color_name;
12026                     XColorBrowserWidget(display,windows,"Select",color_name);
12027                     if (*color_name == '\0')
12028                       break;
12029                   }
12030                 /*
12031                   Set pen color.
12032                 */
12033                 (void) XParseColor(display,windows->map_info->colormap,
12034                   resource_info->pen_colors[pen_number],&color);
12035                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12036                   (unsigned int) MaxColors,&color);
12037                 windows->pixel_info->pen_colors[pen_number]=color;
12038                 pen_id=(unsigned int) pen_number;
12039                 break;
12040               }
12041               case RotateDirectionCommand:
12042               {
12043                 const char
12044                   *const Directions[] =
12045                   {
12046                     "horizontal",
12047                     "vertical",
12048                     (char *) NULL,
12049                   };
12050 
12051                 /*
12052                   Select a command from the pop-up menu.
12053                 */
12054                 id=XMenuWidget(display,windows,RotateMenu[id],
12055                   Directions,command);
12056                 if (id >= 0)
12057                   direction=DirectionCommands[id];
12058                 break;
12059               }
12060               case RotateHelpCommand:
12061               {
12062                 XTextViewHelp(display,resource_info,windows,MagickFalse,
12063                   "Help Viewer - Image Rotation",ImageRotateHelp);
12064                 break;
12065               }
12066               case RotateDismissCommand:
12067               {
12068                 /*
12069                   Prematurely exit.
12070                 */
12071                 state|=EscapeState;
12072                 state|=ExitState;
12073                 break;
12074               }
12075               default:
12076                 break;
12077             }
12078             (void) XSetFunction(display,windows->image.highlight_context,
12079               GXinvert);
12080             continue;
12081           }
12082         switch (event.type)
12083         {
12084           case ButtonPress:
12085           {
12086             if (event.xbutton.button != Button1)
12087               break;
12088             if (event.xbutton.window != windows->image.id)
12089               break;
12090             /*
12091               exit loop.
12092             */
12093             (void) XSetFunction(display,windows->image.highlight_context,
12094               GXcopy);
12095             rotate_info.x1=event.xbutton.x;
12096             rotate_info.y1=event.xbutton.y;
12097             state|=ExitState;
12098             break;
12099           }
12100           case ButtonRelease:
12101             break;
12102           case Expose:
12103             break;
12104           case KeyPress:
12105           {
12106             char
12107               command[MaxTextExtent];
12108 
12109             KeySym
12110               key_symbol;
12111 
12112             if (event.xkey.window != windows->image.id)
12113               break;
12114             /*
12115               Respond to a user key press.
12116             */
12117             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12118               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12119             switch ((int) key_symbol)
12120             {
12121               case XK_Escape:
12122               case XK_F20:
12123               {
12124                 /*
12125                   Prematurely exit.
12126                 */
12127                 state|=EscapeState;
12128                 state|=ExitState;
12129                 break;
12130               }
12131               case XK_F1:
12132               case XK_Help:
12133               {
12134                 (void) XSetFunction(display,windows->image.highlight_context,
12135                   GXcopy);
12136                 XTextViewHelp(display,resource_info,windows,MagickFalse,
12137                   "Help Viewer - Image Rotation",ImageRotateHelp);
12138                 (void) XSetFunction(display,windows->image.highlight_context,
12139                   GXinvert);
12140                 break;
12141               }
12142               default:
12143               {
12144                 (void) XBell(display,0);
12145                 break;
12146               }
12147             }
12148             break;
12149           }
12150           case MotionNotify:
12151           {
12152             rotate_info.x1=event.xmotion.x;
12153             rotate_info.y1=event.xmotion.y;
12154           }
12155         }
12156         rotate_info.x2=rotate_info.x1;
12157         rotate_info.y2=rotate_info.y1;
12158         if (direction == HorizontalRotateCommand)
12159           rotate_info.x2+=32;
12160         else
12161           rotate_info.y2-=32;
12162       } while ((state & ExitState) == 0);
12163       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12164       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12165       if ((state & EscapeState) != 0)
12166         return(MagickTrue);
12167       /*
12168         Draw line as pointer moves until the mouse button is released.
12169       */
12170       distance=0;
12171       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12172       state=DefaultState;
12173       do
12174       {
12175         if (distance > 9)
12176           {
12177             /*
12178               Display info and draw rotation line.
12179             */
12180             if (windows->info.mapped == MagickFalse)
12181               (void) XMapWindow(display,windows->info.id);
12182             (void) FormatLocaleString(text,MaxTextExtent," %g",
12183               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12184             XInfoWidget(display,windows,text);
12185             XHighlightLine(display,windows->image.id,
12186               windows->image.highlight_context,&rotate_info);
12187           }
12188         else
12189           if (windows->info.mapped != MagickFalse)
12190             (void) XWithdrawWindow(display,windows->info.id,
12191               windows->info.screen);
12192         /*
12193           Wait for next event.
12194         */
12195         XScreenEvent(display,windows,&event);
12196         if (distance > 9)
12197           XHighlightLine(display,windows->image.id,
12198             windows->image.highlight_context,&rotate_info);
12199         switch (event.type)
12200         {
12201           case ButtonPress:
12202             break;
12203           case ButtonRelease:
12204           {
12205             /*
12206               User has committed to rotation line.
12207             */
12208             rotate_info.x2=event.xbutton.x;
12209             rotate_info.y2=event.xbutton.y;
12210             state|=ExitState;
12211             break;
12212           }
12213           case Expose:
12214             break;
12215           case MotionNotify:
12216           {
12217             rotate_info.x2=event.xmotion.x;
12218             rotate_info.y2=event.xmotion.y;
12219           }
12220           default:
12221             break;
12222         }
12223         /*
12224           Check boundary conditions.
12225         */
12226         if (rotate_info.x2 < 0)
12227           rotate_info.x2=0;
12228         else
12229           if (rotate_info.x2 > (int) windows->image.width)
12230             rotate_info.x2=(short) windows->image.width;
12231         if (rotate_info.y2 < 0)
12232           rotate_info.y2=0;
12233         else
12234           if (rotate_info.y2 > (int) windows->image.height)
12235             rotate_info.y2=(short) windows->image.height;
12236         /*
12237           Compute rotation angle from the slope of the line.
12238         */
12239         degrees=0.0;
12240         distance=(unsigned int)
12241           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12242           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12243         if (distance > 9)
12244           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12245             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12246       } while ((state & ExitState) == 0);
12247       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12248       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12249       if (distance <= 9)
12250         return(MagickTrue);
12251     }
12252   if (direction == VerticalRotateCommand)
12253     degrees-=90.0;
12254   if (degrees == 0.0)
12255     return(MagickTrue);
12256   /*
12257     Rotate image.
12258   */
12259   normalized_degrees=degrees;
12260   while (normalized_degrees < -45.0)
12261     normalized_degrees+=360.0;
12262   for (rotations=0; normalized_degrees > 45.0; rotations++)
12263     normalized_degrees-=90.0;
12264   if (normalized_degrees != 0.0)
12265     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12266   XSetCursorState(display,windows,MagickTrue);
12267   XCheckRefreshWindows(display,windows);
12268   (*image)->background_color.red=ScaleShortToQuantum(
12269     windows->pixel_info->pen_colors[pen_id].red);
12270   (*image)->background_color.green=ScaleShortToQuantum(
12271     windows->pixel_info->pen_colors[pen_id].green);
12272   (*image)->background_color.blue=ScaleShortToQuantum(
12273     windows->pixel_info->pen_colors[pen_id].blue);
12274   rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12275   XSetCursorState(display,windows,MagickFalse);
12276   if (rotate_image == (Image *) NULL)
12277     return(MagickFalse);
12278   *image=DestroyImage(*image);
12279   *image=rotate_image;
12280   if (windows->image.crop_geometry != (char *) NULL)
12281     {
12282       /*
12283         Rotate crop geometry.
12284       */
12285       width=(unsigned int) (*image)->columns;
12286       height=(unsigned int) (*image)->rows;
12287       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12288       switch (rotations % 4)
12289       {
12290         default:
12291         case 0:
12292           break;
12293         case 1:
12294         {
12295           /*
12296             Rotate 90 degrees.
12297           */
12298           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12299             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12300             (int) height-y,x);
12301           break;
12302         }
12303         case 2:
12304         {
12305           /*
12306             Rotate 180 degrees.
12307           */
12308           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12309             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12310           break;
12311         }
12312         case 3:
12313         {
12314           /*
12315             Rotate 270 degrees.
12316           */
12317           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12318             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12319           break;
12320         }
12321       }
12322     }
12323   if (windows->image.orphan != MagickFalse)
12324     return(MagickTrue);
12325   if (normalized_degrees != 0.0)
12326     {
12327       /*
12328         Update image colormap.
12329       */
12330       windows->image.window_changes.width=(int) (*image)->columns;
12331       windows->image.window_changes.height=(int) (*image)->rows;
12332       if (windows->image.crop_geometry != (char *) NULL)
12333         {
12334           /*
12335             Obtain dimensions of image from crop geometry.
12336           */
12337           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12338             &width,&height);
12339           windows->image.window_changes.width=(int) width;
12340           windows->image.window_changes.height=(int) height;
12341         }
12342       XConfigureImageColormap(display,resource_info,windows,*image);
12343     }
12344   else
12345     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12346       {
12347         windows->image.window_changes.width=windows->image.ximage->height;
12348         windows->image.window_changes.height=windows->image.ximage->width;
12349       }
12350   /*
12351     Update image configuration.
12352   */
12353   (void) XConfigureImage(display,resource_info,windows,*image);
12354   return(MagickTrue);
12355 }
12356 
12357 /*
12358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12359 %                                                                             %
12360 %                                                                             %
12361 %                                                                             %
12362 +   X S a v e I m a g e                                                       %
12363 %                                                                             %
12364 %                                                                             %
12365 %                                                                             %
12366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12367 %
12368 %  XSaveImage() saves an image to a file.
12369 %
12370 %  The format of the XSaveImage method is:
12371 %
12372 %      MagickBooleanType XSaveImage(Display *display,
12373 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
12374 %
12375 %  A description of each parameter follows:
12376 %
12377 %    o display: Specifies a connection to an X server; returned from
12378 %      XOpenDisplay.
12379 %
12380 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12381 %
12382 %    o windows: Specifies a pointer to a XWindows structure.
12383 %
12384 %    o image: the image.
12385 %
12386 */
XSaveImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image)12387 static MagickBooleanType XSaveImage(Display *display,
12388   XResourceInfo *resource_info,XWindows *windows,Image *image)
12389 {
12390   char
12391     filename[MaxTextExtent],
12392     geometry[MaxTextExtent];
12393 
12394   Image
12395     *save_image;
12396 
12397   ImageInfo
12398     *image_info;
12399 
12400   MagickStatusType
12401     status;
12402 
12403   /*
12404     Request file name from user.
12405   */
12406   if (resource_info->write_filename != (char *) NULL)
12407     (void) CopyMagickString(filename,resource_info->write_filename,
12408       MaxTextExtent);
12409   else
12410     {
12411       char
12412         path[MaxTextExtent];
12413 
12414       int
12415         status;
12416 
12417       GetPathComponent(image->filename,HeadPath,path);
12418       GetPathComponent(image->filename,TailPath,filename);
12419       if (*path != '\0')
12420         {
12421           status=chdir(path);
12422           if (status == -1)
12423             (void) ThrowMagickException(&image->exception,GetMagickModule(),
12424               FileOpenError,"UnableToOpenFile","%s",path);
12425         }
12426     }
12427   XFileBrowserWidget(display,windows,"Save",filename);
12428   if (*filename == '\0')
12429     return(MagickTrue);
12430   if (IsPathAccessible(filename) != MagickFalse)
12431     {
12432       int
12433         status;
12434 
12435       /*
12436         File exists-- seek user's permission before overwriting.
12437       */
12438       status=XConfirmWidget(display,windows,"Overwrite",filename);
12439       if (status <= 0)
12440         return(MagickTrue);
12441     }
12442   image_info=CloneImageInfo(resource_info->image_info);
12443   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12444   (void) SetImageInfo(image_info,1,&image->exception);
12445   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12446       (LocaleCompare(image_info->magick,"JPG") == 0))
12447     {
12448       char
12449         quality[MaxTextExtent];
12450 
12451       int
12452         status;
12453 
12454       /*
12455         Request JPEG quality from user.
12456       */
12457       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12458         image->quality);
12459       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12460         quality);
12461       if (*quality == '\0')
12462         return(MagickTrue);
12463       image->quality=StringToUnsignedLong(quality);
12464       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12465     }
12466   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12467       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12468       (LocaleCompare(image_info->magick,"PS") == 0) ||
12469       (LocaleCompare(image_info->magick,"PS2") == 0))
12470     {
12471       char
12472         geometry[MaxTextExtent];
12473 
12474       const char
12475         *const PageSizes[] =
12476         {
12477           "Letter",
12478           "Tabloid",
12479           "Ledger",
12480           "Legal",
12481           "Statement",
12482           "Executive",
12483           "A3",
12484           "A4",
12485           "A5",
12486           "B4",
12487           "B5",
12488           "Folio",
12489           "Quarto",
12490           "10x14",
12491           (char *) NULL
12492         };
12493 
12494       /*
12495         Request page geometry from user.
12496       */
12497       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12498       if (LocaleCompare(image_info->magick,"PDF") == 0)
12499         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12500       if (image_info->page != (char *) NULL)
12501         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12502       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12503         "Select page geometry:",geometry);
12504       if (*geometry != '\0')
12505         image_info->page=GetPageGeometry(geometry);
12506     }
12507   /*
12508     Apply image transforms.
12509   */
12510   XSetCursorState(display,windows,MagickTrue);
12511   XCheckRefreshWindows(display,windows);
12512   save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12513   if (save_image == (Image *) NULL)
12514     return(MagickFalse);
12515   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12516     windows->image.ximage->width,windows->image.ximage->height);
12517   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12518   /*
12519     Write image.
12520   */
12521   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12522   status=WriteImage(image_info,save_image);
12523   if (status != MagickFalse)
12524     image->taint=MagickFalse;
12525   save_image=DestroyImage(save_image);
12526   image_info=DestroyImageInfo(image_info);
12527   XSetCursorState(display,windows,MagickFalse);
12528   return(status != 0 ? MagickTrue : MagickFalse);
12529 }
12530 
12531 /*
12532 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12533 %                                                                             %
12534 %                                                                             %
12535 %                                                                             %
12536 +   X S c r e e n E v e n t                                                   %
12537 %                                                                             %
12538 %                                                                             %
12539 %                                                                             %
12540 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12541 %
12542 %  XScreenEvent() handles global events associated with the Pan and Magnify
12543 %  windows.
12544 %
12545 %  The format of the XScreenEvent function is:
12546 %
12547 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12548 %
12549 %  A description of each parameter follows:
12550 %
12551 %    o display: Specifies a pointer to the Display structure;  returned from
12552 %      XOpenDisplay.
12553 %
12554 %    o windows: Specifies a pointer to a XWindows structure.
12555 %
12556 %    o event: Specifies a pointer to a X11 XEvent structure.
12557 %
12558 %
12559 */
12560 
12561 #if defined(__cplusplus) || defined(c_plusplus)
12562 extern "C" {
12563 #endif
12564 
XPredicate(Display * magick_unused (display),XEvent * event,char * data)12565 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12566 {
12567   XWindows
12568     *windows;
12569 
12570   magick_unreferenced(display);
12571 
12572   windows=(XWindows *) data;
12573   if ((event->type == ClientMessage) &&
12574       (event->xclient.window == windows->image.id))
12575     return(MagickFalse);
12576   return(MagickTrue);
12577 }
12578 
12579 #if defined(__cplusplus) || defined(c_plusplus)
12580 }
12581 #endif
12582 
XScreenEvent(Display * display,XWindows * windows,XEvent * event)12583 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12584 {
12585   int
12586     x,
12587     y;
12588 
12589   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12590   if (event->xany.window == windows->command.id)
12591     return;
12592   switch (event->type)
12593   {
12594     case ButtonPress:
12595     case ButtonRelease:
12596     {
12597       if ((event->xbutton.button == Button3) &&
12598           (event->xbutton.state & Mod1Mask))
12599         {
12600           /*
12601             Convert Alt-Button3 to Button2.
12602           */
12603           event->xbutton.button=Button2;
12604           event->xbutton.state&=(~Mod1Mask);
12605         }
12606       if (event->xbutton.window == windows->backdrop.id)
12607         {
12608           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12609             event->xbutton.time);
12610           break;
12611         }
12612       if (event->xbutton.window == windows->pan.id)
12613         {
12614           XPanImage(display,windows,event);
12615           break;
12616         }
12617       if (event->xbutton.window == windows->image.id)
12618         if (event->xbutton.button == Button2)
12619           {
12620             /*
12621               Update magnified image.
12622             */
12623             x=event->xbutton.x;
12624             y=event->xbutton.y;
12625             if (x < 0)
12626               x=0;
12627             else
12628               if (x >= (int) windows->image.width)
12629                 x=(int) (windows->image.width-1);
12630             windows->magnify.x=(int) windows->image.x+x;
12631             if (y < 0)
12632               y=0;
12633             else
12634              if (y >= (int) windows->image.height)
12635                y=(int) (windows->image.height-1);
12636             windows->magnify.y=windows->image.y+y;
12637             if (windows->magnify.mapped == MagickFalse)
12638               (void) XMapRaised(display,windows->magnify.id);
12639             XMakeMagnifyImage(display,windows);
12640             if (event->type == ButtonRelease)
12641               (void) XWithdrawWindow(display,windows->info.id,
12642                 windows->info.screen);
12643             break;
12644           }
12645       break;
12646     }
12647     case ClientMessage:
12648     {
12649       /*
12650         If client window delete message, exit.
12651       */
12652       if (event->xclient.message_type != windows->wm_protocols)
12653         break;
12654       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12655         break;
12656       if (event->xclient.window == windows->magnify.id)
12657         {
12658           (void) XWithdrawWindow(display,windows->magnify.id,
12659             windows->magnify.screen);
12660           break;
12661         }
12662       break;
12663     }
12664     case ConfigureNotify:
12665     {
12666       if (event->xconfigure.window == windows->magnify.id)
12667         {
12668           unsigned int
12669             magnify;
12670 
12671           /*
12672             Magnify window has a new configuration.
12673           */
12674           windows->magnify.width=(unsigned int) event->xconfigure.width;
12675           windows->magnify.height=(unsigned int) event->xconfigure.height;
12676           if (windows->magnify.mapped == MagickFalse)
12677             break;
12678           magnify=1;
12679           while ((int) magnify <= event->xconfigure.width)
12680             magnify<<=1;
12681           while ((int) magnify <= event->xconfigure.height)
12682             magnify<<=1;
12683           magnify>>=1;
12684           if (((int) magnify != event->xconfigure.width) ||
12685               ((int) magnify != event->xconfigure.height))
12686             {
12687               XWindowChanges
12688                 window_changes;
12689 
12690               window_changes.width=(int) magnify;
12691               window_changes.height=(int) magnify;
12692               (void) XReconfigureWMWindow(display,windows->magnify.id,
12693                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12694                 &window_changes);
12695               break;
12696             }
12697           XMakeMagnifyImage(display,windows);
12698           break;
12699         }
12700       break;
12701     }
12702     case Expose:
12703     {
12704       if (event->xexpose.window == windows->image.id)
12705         {
12706           XRefreshWindow(display,&windows->image,event);
12707           break;
12708         }
12709       if (event->xexpose.window == windows->pan.id)
12710         if (event->xexpose.count == 0)
12711           {
12712             XDrawPanRectangle(display,windows);
12713             break;
12714           }
12715       if (event->xexpose.window == windows->magnify.id)
12716         if (event->xexpose.count == 0)
12717           {
12718             XMakeMagnifyImage(display,windows);
12719             break;
12720           }
12721       break;
12722     }
12723     case KeyPress:
12724     {
12725       char
12726         command[MaxTextExtent];
12727 
12728       KeySym
12729         key_symbol;
12730 
12731       if (event->xkey.window != windows->magnify.id)
12732         break;
12733       /*
12734         Respond to a user key press.
12735       */
12736       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12737         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12738       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12739       break;
12740     }
12741     case MapNotify:
12742     {
12743       if (event->xmap.window == windows->magnify.id)
12744         {
12745           windows->magnify.mapped=MagickTrue;
12746           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12747           break;
12748         }
12749       if (event->xmap.window == windows->info.id)
12750         {
12751           windows->info.mapped=MagickTrue;
12752           break;
12753         }
12754       break;
12755     }
12756     case MotionNotify:
12757     {
12758       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12759       if (event->xmotion.window == windows->image.id)
12760         if (windows->magnify.mapped != MagickFalse)
12761           {
12762             /*
12763               Update magnified image.
12764             */
12765             x=event->xmotion.x;
12766             y=event->xmotion.y;
12767             if (x < 0)
12768               x=0;
12769             else
12770               if (x >= (int) windows->image.width)
12771                 x=(int) (windows->image.width-1);
12772             windows->magnify.x=(int) windows->image.x+x;
12773             if (y < 0)
12774               y=0;
12775             else
12776              if (y >= (int) windows->image.height)
12777                y=(int) (windows->image.height-1);
12778             windows->magnify.y=windows->image.y+y;
12779             XMakeMagnifyImage(display,windows);
12780           }
12781       break;
12782     }
12783     case UnmapNotify:
12784     {
12785       if (event->xunmap.window == windows->magnify.id)
12786         {
12787           windows->magnify.mapped=MagickFalse;
12788           break;
12789         }
12790       if (event->xunmap.window == windows->info.id)
12791         {
12792           windows->info.mapped=MagickFalse;
12793           break;
12794         }
12795       break;
12796     }
12797     default:
12798       break;
12799   }
12800 }
12801 
12802 /*
12803 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12804 %                                                                             %
12805 %                                                                             %
12806 %                                                                             %
12807 +   X S e t C r o p G e o m e t r y                                           %
12808 %                                                                             %
12809 %                                                                             %
12810 %                                                                             %
12811 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12812 %
12813 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12814 %  and translates it to a cropping geometry relative to the image.
12815 %
12816 %  The format of the XSetCropGeometry method is:
12817 %
12818 %      void XSetCropGeometry(Display *display,XWindows *windows,
12819 %        RectangleInfo *crop_info,Image *image)
12820 %
12821 %  A description of each parameter follows:
12822 %
12823 %    o display: Specifies a connection to an X server; returned from
12824 %      XOpenDisplay.
12825 %
12826 %    o windows: Specifies a pointer to a XWindows structure.
12827 %
12828 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12829 %      Image window to crop.
12830 %
12831 %    o image: the image.
12832 %
12833 */
XSetCropGeometry(Display * display,XWindows * windows,RectangleInfo * crop_info,Image * image)12834 static void XSetCropGeometry(Display *display,XWindows *windows,
12835   RectangleInfo *crop_info,Image *image)
12836 {
12837   char
12838     text[MaxTextExtent];
12839 
12840   int
12841     x,
12842     y;
12843 
12844   MagickRealType
12845     scale_factor;
12846 
12847   unsigned int
12848     height,
12849     width;
12850 
12851   if (windows->info.mapped != MagickFalse)
12852     {
12853       /*
12854         Display info on cropping rectangle.
12855       */
12856       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12857         (double) crop_info->width,(double) crop_info->height,(double)
12858         crop_info->x,(double) crop_info->y);
12859       XInfoWidget(display,windows,text);
12860     }
12861   /*
12862     Cropping geometry is relative to any previous crop geometry.
12863   */
12864   x=0;
12865   y=0;
12866   width=(unsigned int) image->columns;
12867   height=(unsigned int) image->rows;
12868   if (windows->image.crop_geometry != (char *) NULL)
12869     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12870   else
12871     windows->image.crop_geometry=AcquireString((char *) NULL);
12872   /*
12873     Define the crop geometry string from the cropping rectangle.
12874   */
12875   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12876   if (crop_info->x > 0)
12877     x+=(int) (scale_factor*crop_info->x+0.5);
12878   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12879   if (width == 0)
12880     width=1;
12881   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12882   if (crop_info->y > 0)
12883     y+=(int) (scale_factor*crop_info->y+0.5);
12884   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12885   if (height == 0)
12886     height=1;
12887   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12888     "%ux%u%+d%+d",width,height,x,y);
12889 }
12890 
12891 /*
12892 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12893 %                                                                             %
12894 %                                                                             %
12895 %                                                                             %
12896 +   X T i l e I m a g e                                                       %
12897 %                                                                             %
12898 %                                                                             %
12899 %                                                                             %
12900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12901 %
12902 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12903 %  The load or delete command is chosen from a menu.
12904 %
12905 %  The format of the XTileImage method is:
12906 %
12907 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12908 %        XWindows *windows,Image *image,XEvent *event)
12909 %
12910 %  A description of each parameter follows:
12911 %
12912 %    o tile_image:  XTileImage reads or deletes the tile image
12913 %      and returns it.  A null image is returned if an error occurs.
12914 %
12915 %    o display: Specifies a connection to an X server;  returned from
12916 %      XOpenDisplay.
12917 %
12918 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12919 %
12920 %    o windows: Specifies a pointer to a XWindows structure.
12921 %
12922 %    o image: the image; returned from ReadImage.
12923 %
12924 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
12925 %      the entire image is refreshed.
12926 %
12927 */
XTileImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,XEvent * event)12928 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12929   XWindows *windows,Image *image,XEvent *event)
12930 {
12931   const char
12932     *const VerbMenu[] =
12933     {
12934       "Load",
12935       "Next",
12936       "Former",
12937       "Delete",
12938       "Update",
12939       (char *) NULL,
12940     };
12941 
12942   static const ModeType
12943     TileCommands[] =
12944     {
12945       TileLoadCommand,
12946       TileNextCommand,
12947       TileFormerCommand,
12948       TileDeleteCommand,
12949       TileUpdateCommand
12950     };
12951 
12952   char
12953     command[MaxTextExtent],
12954     filename[MaxTextExtent];
12955 
12956   Image
12957     *tile_image;
12958 
12959   int
12960     id,
12961     status,
12962     tile,
12963     x,
12964     y;
12965 
12966   MagickRealType
12967     scale_factor;
12968 
12969   char
12970     *p,
12971     *q;
12972 
12973   int
12974     i;
12975 
12976   unsigned int
12977     height,
12978     width;
12979 
12980   /*
12981     Tile image is relative to montage image configuration.
12982   */
12983   x=0;
12984   y=0;
12985   width=(unsigned int) image->columns;
12986   height=(unsigned int) image->rows;
12987   if (windows->image.crop_geometry != (char *) NULL)
12988     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12989   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12990   event->xbutton.x+=windows->image.x;
12991   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12992   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12993   event->xbutton.y+=windows->image.y;
12994   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
12995   /*
12996     Determine size and location of each tile in the visual image directory.
12997   */
12998   width=(unsigned int) image->columns;
12999   height=(unsigned int) image->rows;
13000   x=0;
13001   y=0;
13002   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13003   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13004     (event->xbutton.x-x)/width;
13005   if (tile < 0)
13006     {
13007       /*
13008         Button press is outside any tile.
13009       */
13010       (void) XBell(display,0);
13011       return((Image *) NULL);
13012     }
13013   /*
13014     Determine file name from the tile directory.
13015   */
13016   p=image->directory;
13017   for (i=tile; (i != 0) && (*p != '\0'); )
13018   {
13019     if (*p == '\xff')
13020       i--;
13021     p++;
13022   }
13023   if (*p == '\0')
13024     {
13025       /*
13026         Button press is outside any tile.
13027       */
13028       (void) XBell(display,0);
13029       return((Image *) NULL);
13030     }
13031   /*
13032     Select a command from the pop-up menu.
13033   */
13034   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13035   if (id < 0)
13036     return((Image *) NULL);
13037   q=p;
13038   while ((*q != '\xff') && (*q != '\0'))
13039     q++;
13040   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13041   /*
13042     Perform command for the selected tile.
13043   */
13044   XSetCursorState(display,windows,MagickTrue);
13045   XCheckRefreshWindows(display,windows);
13046   tile_image=NewImageList();
13047   switch (TileCommands[id])
13048   {
13049     case TileLoadCommand:
13050     {
13051       /*
13052         Load tile image.
13053       */
13054       XCheckRefreshWindows(display,windows);
13055       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13056         MaxTextExtent);
13057       (void) CopyMagickString(resource_info->image_info->filename,filename,
13058         MaxTextExtent);
13059       tile_image=ReadImage(resource_info->image_info,&image->exception);
13060       CatchException(&image->exception);
13061       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13062       break;
13063     }
13064     case TileNextCommand:
13065     {
13066       /*
13067         Display next image.
13068       */
13069       XClientMessage(display,windows->image.id,windows->im_protocols,
13070         windows->im_next_image,CurrentTime);
13071       break;
13072     }
13073     case TileFormerCommand:
13074     {
13075       /*
13076         Display former image.
13077       */
13078       XClientMessage(display,windows->image.id,windows->im_protocols,
13079         windows->im_former_image,CurrentTime);
13080       break;
13081     }
13082     case TileDeleteCommand:
13083     {
13084       /*
13085         Delete tile image.
13086       */
13087       if (IsPathAccessible(filename) == MagickFalse)
13088         {
13089           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13090           break;
13091         }
13092       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13093       if (status <= 0)
13094         break;
13095       status=ShredFile(filename);
13096       if (status != MagickFalse)
13097         {
13098           XNoticeWidget(display,windows,"Unable to delete image file:",
13099             filename);
13100           break;
13101         }
13102     }
13103     case TileUpdateCommand:
13104     {
13105       ExceptionInfo
13106         *exception;
13107 
13108       int
13109         x_offset,
13110         y_offset;
13111 
13112       PixelPacket
13113         pixel;
13114 
13115       int
13116         j;
13117 
13118       PixelPacket
13119         *s;
13120 
13121       /*
13122         Ensure all the images exist.
13123       */
13124       tile=0;
13125       for (p=image->directory; *p != '\0'; p++)
13126       {
13127         CacheView
13128           *image_view;
13129 
13130         q=p;
13131         while ((*q != '\xff') && (*q != '\0'))
13132           q++;
13133         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13134         p=q;
13135         if (IsPathAccessible(filename) != MagickFalse)
13136           {
13137             tile++;
13138             continue;
13139           }
13140         /*
13141           Overwrite tile with background color.
13142         */
13143         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13144         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13145         exception=(&image->exception);
13146         image_view=AcquireAuthenticCacheView(image,exception);
13147         (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13148         for (i=0; i < (int) height; i++)
13149         {
13150           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13151             y_offset+i,width,1,exception);
13152           if (s == (PixelPacket *) NULL)
13153             break;
13154           for (j=0; j < (int) width; j++)
13155             *s++=pixel;
13156           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13157             break;
13158         }
13159         image_view=DestroyCacheView(image_view);
13160         tile++;
13161       }
13162       windows->image.window_changes.width=(int) image->columns;
13163       windows->image.window_changes.height=(int) image->rows;
13164       XConfigureImageColormap(display,resource_info,windows,image);
13165       (void) XConfigureImage(display,resource_info,windows,image);
13166       break;
13167     }
13168     default:
13169       break;
13170   }
13171   XSetCursorState(display,windows,MagickFalse);
13172   return(tile_image);
13173 }
13174 
13175 /*
13176 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13177 %                                                                             %
13178 %                                                                             %
13179 %                                                                             %
13180 +   X T r a n s l a t e I m a g e                                             %
13181 %                                                                             %
13182 %                                                                             %
13183 %                                                                             %
13184 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13185 %
13186 %  XTranslateImage() translates the image within an Image window by one pixel
13187 %  as specified by the key symbol.  If the image has a `montage string the
13188 %  translation is respect to the width and height contained within the string.
13189 %
13190 %  The format of the XTranslateImage method is:
13191 %
13192 %      void XTranslateImage(Display *display,XWindows *windows,
13193 %        Image *image,const KeySym key_symbol)
13194 %
13195 %  A description of each parameter follows:
13196 %
13197 %    o display: Specifies a connection to an X server; returned from
13198 %      XOpenDisplay.
13199 %
13200 %    o windows: Specifies a pointer to a XWindows structure.
13201 %
13202 %    o image: the image.
13203 %
13204 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13205 %      to trim.
13206 %
13207 */
XTranslateImage(Display * display,XWindows * windows,Image * image,const KeySym key_symbol)13208 static void XTranslateImage(Display *display,XWindows *windows,
13209   Image *image,const KeySym key_symbol)
13210 {
13211   char
13212     text[MaxTextExtent];
13213 
13214   int
13215     x,
13216     y;
13217 
13218   unsigned int
13219     x_offset,
13220     y_offset;
13221 
13222   /*
13223     User specified a pan position offset.
13224   */
13225   x_offset=windows->image.width;
13226   y_offset=windows->image.height;
13227   if (image->montage != (char *) NULL)
13228     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13229   switch ((int) key_symbol)
13230   {
13231     case XK_Home:
13232     case XK_KP_Home:
13233     {
13234       windows->image.x=(int) windows->image.width/2;
13235       windows->image.y=(int) windows->image.height/2;
13236       break;
13237     }
13238     case XK_Left:
13239     case XK_KP_Left:
13240     {
13241       windows->image.x-=x_offset;
13242       break;
13243     }
13244     case XK_Next:
13245     case XK_Up:
13246     case XK_KP_Up:
13247     {
13248       windows->image.y-=y_offset;
13249       break;
13250     }
13251     case XK_Right:
13252     case XK_KP_Right:
13253     {
13254       windows->image.x+=x_offset;
13255       break;
13256     }
13257     case XK_Prior:
13258     case XK_Down:
13259     case XK_KP_Down:
13260     {
13261       windows->image.y+=y_offset;
13262       break;
13263     }
13264     default:
13265       return;
13266   }
13267   /*
13268     Check boundary conditions.
13269   */
13270   if (windows->image.x < 0)
13271     windows->image.x=0;
13272   else
13273     if ((int) (windows->image.x+windows->image.width) >
13274         windows->image.ximage->width)
13275       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13276   if (windows->image.y < 0)
13277     windows->image.y=0;
13278   else
13279     if ((int) (windows->image.y+windows->image.height) >
13280         windows->image.ximage->height)
13281       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13282   /*
13283     Refresh Image window.
13284   */
13285   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13286     windows->image.width,windows->image.height,windows->image.x,
13287     windows->image.y);
13288   XInfoWidget(display,windows,text);
13289   XCheckRefreshWindows(display,windows);
13290   XDrawPanRectangle(display,windows);
13291   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13292   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13293 }
13294 
13295 /*
13296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13297 %                                                                             %
13298 %                                                                             %
13299 %                                                                             %
13300 +   X T r i m I m a g e                                                       %
13301 %                                                                             %
13302 %                                                                             %
13303 %                                                                             %
13304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13305 %
13306 %  XTrimImage() trims the edges from the Image window.
13307 %
13308 %  The format of the XTrimImage method is:
13309 %
13310 %      MagickBooleanType XTrimImage(Display *display,
13311 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
13312 %
13313 %  A description of each parameter follows:
13314 %
13315 %    o display: Specifies a connection to an X server; returned from
13316 %      XOpenDisplay.
13317 %
13318 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13319 %
13320 %    o windows: Specifies a pointer to a XWindows structure.
13321 %
13322 %    o image: the image.
13323 %
13324 */
XTrimImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image)13325 static MagickBooleanType XTrimImage(Display *display,
13326   XResourceInfo *resource_info,XWindows *windows,Image *image)
13327 {
13328   RectangleInfo
13329     trim_info;
13330 
13331   int
13332     x,
13333     y;
13334 
13335   size_t
13336     background,
13337     pixel;
13338 
13339   /*
13340     Trim edges from image.
13341   */
13342   XSetCursorState(display,windows,MagickTrue);
13343   XCheckRefreshWindows(display,windows);
13344   /*
13345     Crop the left edge.
13346   */
13347   background=XGetPixel(windows->image.ximage,0,0);
13348   trim_info.width=(size_t) windows->image.ximage->width;
13349   for (x=0; x < windows->image.ximage->width; x++)
13350   {
13351     for (y=0; y < windows->image.ximage->height; y++)
13352     {
13353       pixel=XGetPixel(windows->image.ximage,x,y);
13354       if (pixel != background)
13355         break;
13356     }
13357     if (y < windows->image.ximage->height)
13358       break;
13359   }
13360   trim_info.x=(ssize_t) x;
13361   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13362     {
13363       XSetCursorState(display,windows,MagickFalse);
13364       return(MagickFalse);
13365     }
13366   /*
13367     Crop the right edge.
13368   */
13369   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13370   for (x=windows->image.ximage->width-1; x != 0; x--)
13371   {
13372     for (y=0; y < windows->image.ximage->height; y++)
13373     {
13374       pixel=XGetPixel(windows->image.ximage,x,y);
13375       if (pixel != background)
13376         break;
13377     }
13378     if (y < windows->image.ximage->height)
13379       break;
13380   }
13381   trim_info.width=(size_t) (x-trim_info.x+1);
13382   /*
13383     Crop the top edge.
13384   */
13385   background=XGetPixel(windows->image.ximage,0,0);
13386   trim_info.height=(size_t) windows->image.ximage->height;
13387   for (y=0; y < windows->image.ximage->height; y++)
13388   {
13389     for (x=0; x < windows->image.ximage->width; x++)
13390     {
13391       pixel=XGetPixel(windows->image.ximage,x,y);
13392       if (pixel != background)
13393         break;
13394     }
13395     if (x < windows->image.ximage->width)
13396       break;
13397   }
13398   trim_info.y=(ssize_t) y;
13399   /*
13400     Crop the bottom edge.
13401   */
13402   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13403   for (y=windows->image.ximage->height-1; y != 0; y--)
13404   {
13405     for (x=0; x < windows->image.ximage->width; x++)
13406     {
13407       pixel=XGetPixel(windows->image.ximage,x,y);
13408       if (pixel != background)
13409         break;
13410     }
13411     if (x < windows->image.ximage->width)
13412       break;
13413   }
13414   trim_info.height=(size_t) y-trim_info.y+1;
13415   if (((unsigned int) trim_info.width != windows->image.width) ||
13416       ((unsigned int) trim_info.height != windows->image.height))
13417     {
13418       /*
13419         Reconfigure Image window as defined by the trimming rectangle.
13420       */
13421       XSetCropGeometry(display,windows,&trim_info,image);
13422       windows->image.window_changes.width=(int) trim_info.width;
13423       windows->image.window_changes.height=(int) trim_info.height;
13424       (void) XConfigureImage(display,resource_info,windows,image);
13425     }
13426   XSetCursorState(display,windows,MagickFalse);
13427   return(MagickTrue);
13428 }
13429 
13430 /*
13431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13432 %                                                                             %
13433 %                                                                             %
13434 %                                                                             %
13435 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13436 %                                                                             %
13437 %                                                                             %
13438 %                                                                             %
13439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13440 %
13441 %  XVisualDirectoryImage() creates a Visual Image Directory.
13442 %
13443 %  The format of the XVisualDirectoryImage method is:
13444 %
13445 %      Image *XVisualDirectoryImage(Display *display,
13446 %        XResourceInfo *resource_info,XWindows *windows)
13447 %
13448 %  A description of each parameter follows:
13449 %
13450 %    o nexus: Method XVisualDirectoryImage returns a visual image
13451 %      directory if it can be created successfully.  Otherwise a null image
13452 %      is returned.
13453 %
13454 %    o display: Specifies a connection to an X server; returned from
13455 %      XOpenDisplay.
13456 %
13457 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13458 %
13459 %    o windows: Specifies a pointer to a XWindows structure.
13460 %
13461 */
XVisualDirectoryImage(Display * display,XResourceInfo * resource_info,XWindows * windows)13462 static Image *XVisualDirectoryImage(Display *display,
13463   XResourceInfo *resource_info,XWindows *windows)
13464 {
13465 #define TileImageTag  "Scale/Image"
13466 #define XClientName  "montage"
13467 
13468   char
13469     **filelist;
13470 
13471   ExceptionInfo
13472     *exception;
13473 
13474   Image
13475     *images,
13476     *montage_image,
13477     *next_image,
13478     *thumbnail_image;
13479 
13480   ImageInfo
13481     *read_info;
13482 
13483   int
13484     number_files;
13485 
13486   MagickBooleanType
13487     backdrop;
13488 
13489   MagickStatusType
13490     status;
13491 
13492   MontageInfo
13493     *montage_info;
13494 
13495   RectangleInfo
13496     geometry;
13497 
13498   int
13499     i;
13500 
13501   static char
13502     filename[MaxTextExtent] = "\0",
13503     filenames[MaxTextExtent] = "*";
13504 
13505   XResourceInfo
13506     background_resources;
13507 
13508   /*
13509     Request file name from user.
13510   */
13511   XFileBrowserWidget(display,windows,"Directory",filenames);
13512   if (*filenames == '\0')
13513     return((Image *) NULL);
13514   /*
13515     Expand the filenames.
13516   */
13517   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13518   if (filelist == (char **) NULL)
13519     {
13520       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13521         filenames);
13522       return((Image *) NULL);
13523     }
13524   number_files=1;
13525   filelist[0]=filenames;
13526   status=ExpandFilenames(&number_files,&filelist);
13527   if ((status == MagickFalse) || (number_files == 0))
13528     {
13529       if (number_files == 0)
13530         ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13531       else
13532         ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13533           filenames);
13534       return((Image *) NULL);
13535     }
13536   /*
13537     Set image background resources.
13538   */
13539   background_resources=(*resource_info);
13540   background_resources.window_id=AcquireString("");
13541   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13542     "0x%lx",windows->image.id);
13543   background_resources.backdrop=MagickTrue;
13544   /*
13545     Read each image and convert them to a tile.
13546   */
13547   backdrop=(windows->visual_info->klass == TrueColor) ||
13548     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13549   read_info=CloneImageInfo(resource_info->image_info);
13550   (void) SetImageOption(read_info,"jpeg:size","120x120");
13551   (void) CloneString(&read_info->size,DefaultTileGeometry);
13552   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13553     (void *) NULL);
13554   images=NewImageList();
13555   exception=AcquireExceptionInfo();
13556   XSetCursorState(display,windows,MagickTrue);
13557   XCheckRefreshWindows(display,windows);
13558   for (i=0; i < (int) number_files; i++)
13559   {
13560     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13561     filelist[i]=DestroyString(filelist[i]);
13562     *read_info->magick='\0';
13563     next_image=ReadImage(read_info,exception);
13564     CatchException(exception);
13565     if (next_image != (Image *) NULL)
13566       {
13567         (void) DeleteImageProperty(next_image,"label");
13568         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13569           read_info,next_image,DefaultTileLabel));
13570         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13571           exception);
13572         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13573           geometry.height,exception);
13574         if (thumbnail_image != (Image *) NULL)
13575           {
13576             next_image=DestroyImage(next_image);
13577             next_image=thumbnail_image;
13578           }
13579         if (backdrop)
13580           {
13581             (void) XDisplayBackgroundImage(display,&background_resources,
13582               next_image);
13583             XSetCursorState(display,windows,MagickTrue);
13584           }
13585         AppendImageToList(&images,next_image);
13586         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13587           {
13588             MagickBooleanType
13589               proceed;
13590 
13591             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13592               (MagickSizeType) number_files);
13593             if (proceed == MagickFalse)
13594               break;
13595           }
13596       }
13597   }
13598   exception=DestroyExceptionInfo(exception);
13599   filelist=(char **) RelinquishMagickMemory(filelist);
13600   if (images == (Image *) NULL)
13601     {
13602       read_info=DestroyImageInfo(read_info);
13603       XSetCursorState(display,windows,MagickFalse);
13604       ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13605       return((Image *) NULL);
13606     }
13607   /*
13608     Create the Visual Image Directory.
13609   */
13610   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13611   montage_info->pointsize=10;
13612   if (resource_info->font != (char *) NULL)
13613     (void) CloneString(&montage_info->font,resource_info->font);
13614   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13615   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13616     images),&images->exception);
13617   images=DestroyImageList(images);
13618   montage_info=DestroyMontageInfo(montage_info);
13619   read_info=DestroyImageInfo(read_info);
13620   XSetCursorState(display,windows,MagickFalse);
13621   if (montage_image == (Image *) NULL)
13622     return(montage_image);
13623   XClientMessage(display,windows->image.id,windows->im_protocols,
13624     windows->im_next_image,CurrentTime);
13625   return(montage_image);
13626 }
13627 
13628 /*
13629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13630 %                                                                             %
13631 %                                                                             %
13632 %                                                                             %
13633 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13634 %                                                                             %
13635 %                                                                             %
13636 %                                                                             %
13637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13638 %
13639 %  XDisplayBackgroundImage() displays an image in the background of a window.
13640 %
13641 %  The format of the XDisplayBackgroundImage method is:
13642 %
13643 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13644 %        XResourceInfo *resource_info,Image *image)
13645 %
13646 %  A description of each parameter follows:
13647 %
13648 %    o display: Specifies a connection to an X server;  returned from
13649 %      XOpenDisplay.
13650 %
13651 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13652 %
13653 %    o image: the image.
13654 %
13655 */
XDisplayBackgroundImage(Display * display,XResourceInfo * resource_info,Image * image)13656 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13657   XResourceInfo *resource_info,Image *image)
13658 {
13659   char
13660     geometry[MaxTextExtent],
13661     visual_type[MaxTextExtent];
13662 
13663   int
13664     height,
13665     status,
13666     width;
13667 
13668   RectangleInfo
13669     geometry_info;
13670 
13671   static XPixelInfo
13672     pixel;
13673 
13674   static XStandardColormap
13675     *map_info;
13676 
13677   static XVisualInfo
13678     *visual_info = (XVisualInfo *) NULL;
13679 
13680   static XWindowInfo
13681     window_info;
13682 
13683   size_t
13684     delay;
13685 
13686   Window
13687     root_window;
13688 
13689   XGCValues
13690     context_values;
13691 
13692   XResourceInfo
13693     resources;
13694 
13695   XWindowAttributes
13696     window_attributes;
13697 
13698   /*
13699     Determine target window.
13700   */
13701   assert(image != (Image *) NULL);
13702   assert(image->signature == MagickCoreSignature);
13703   if (image->debug != MagickFalse)
13704     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13705   resources=(*resource_info);
13706   window_info.id=(Window) NULL;
13707   root_window=XRootWindow(display,XDefaultScreen(display));
13708   if (LocaleCompare(resources.window_id,"root") == 0)
13709     window_info.id=root_window;
13710   else
13711     {
13712       if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13713         window_info.id=XWindowByID(display,root_window,
13714           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13715       if (window_info.id == (Window) NULL)
13716         window_info.id=XWindowByName(display,root_window,resources.window_id);
13717     }
13718   if (window_info.id == (Window) NULL)
13719     {
13720       ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13721         resources.window_id);
13722     }
13723   /*
13724     Determine window visual id.
13725   */
13726   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13727   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13728   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13729   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13730   if (status != 0)
13731     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13732       XVisualIDFromVisual(window_attributes.visual));
13733   if (visual_info == (XVisualInfo *) NULL)
13734     {
13735       /*
13736         Allocate standard colormap.
13737       */
13738       map_info=XAllocStandardColormap();
13739       if (map_info == (XStandardColormap *) NULL)
13740         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13741           image->filename);
13742       map_info->colormap=(Colormap) NULL;
13743       pixel.pixels=(unsigned long *) NULL;
13744       /*
13745         Initialize visual info.
13746       */
13747       resources.map_type=(char *) NULL;
13748       resources.visual_type=visual_type;
13749       visual_info=XBestVisualInfo(display,map_info,&resources);
13750       if (visual_info == (XVisualInfo *) NULL)
13751         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13752           resources.visual_type);
13753       /*
13754         Initialize window info.
13755       */
13756       window_info.ximage=(XImage *) NULL;
13757       window_info.matte_image=(XImage *) NULL;
13758       window_info.pixmap=(Pixmap) NULL;
13759       window_info.matte_pixmap=(Pixmap) NULL;
13760     }
13761   /*
13762     Free previous root colors.
13763   */
13764   if (window_info.id == root_window)
13765     (void) XDestroyWindowColors(display,root_window);
13766   /*
13767     Initialize Standard Colormap.
13768   */
13769   resources.colormap=SharedColormap;
13770   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13771   /*
13772     Graphic context superclass.
13773   */
13774   context_values.background=pixel.foreground_color.pixel;
13775   context_values.foreground=pixel.background_color.pixel;
13776   pixel.annotate_context=XCreateGC(display,window_info.id,
13777     (size_t) (GCBackground | GCForeground),&context_values);
13778   if (pixel.annotate_context == (GC) NULL)
13779     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13780       image->filename);
13781   /*
13782     Initialize Image window attributes.
13783   */
13784   window_info.name=AcquireString("\0");
13785   window_info.icon_name=AcquireString("\0");
13786   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13787     &resources,&window_info);
13788   /*
13789     Create the X image.
13790   */
13791   window_info.width=(unsigned int) image->columns;
13792   window_info.height=(unsigned int) image->rows;
13793   if ((image->columns != window_info.width) ||
13794       (image->rows != window_info.height))
13795     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13796       image->filename);
13797   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13798     window_attributes.width,window_attributes.height);
13799   geometry_info.width=window_info.width;
13800   geometry_info.height=window_info.height;
13801   geometry_info.x=(ssize_t) window_info.x;
13802   geometry_info.y=(ssize_t) window_info.y;
13803   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13804     &geometry_info.width,&geometry_info.height);
13805   window_info.width=(unsigned int) geometry_info.width;
13806   window_info.height=(unsigned int) geometry_info.height;
13807   window_info.x=(int) geometry_info.x;
13808   window_info.y=(int) geometry_info.y;
13809   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13810     window_info.height);
13811   if (status == MagickFalse)
13812     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13813       image->filename);
13814   window_info.x=0;
13815   window_info.y=0;
13816   if (image->debug != MagickFalse)
13817     {
13818       (void) LogMagickEvent(X11Event,GetMagickModule(),
13819         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13820         (double) image->columns,(double) image->rows);
13821       if (image->colors != 0)
13822         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13823           image->colors);
13824       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13825     }
13826   /*
13827     Adjust image dimensions as specified by backdrop or geometry options.
13828   */
13829   width=(int) window_info.width;
13830   height=(int) window_info.height;
13831   if (resources.backdrop != MagickFalse)
13832     {
13833       /*
13834         Center image on window.
13835       */
13836       window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13837       window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13838       width=window_attributes.width;
13839       height=window_attributes.height;
13840     }
13841   if ((resources.image_geometry != (char *) NULL) &&
13842       (*resources.image_geometry != '\0'))
13843     {
13844       char
13845         default_geometry[MaxTextExtent];
13846 
13847       int
13848         flags,
13849         gravity;
13850 
13851       XSizeHints
13852         *size_hints;
13853 
13854       /*
13855         User specified geometry.
13856       */
13857       size_hints=XAllocSizeHints();
13858       if (size_hints == (XSizeHints *) NULL)
13859         ThrowXWindowFatalException(ResourceLimitFatalError,
13860           "MemoryAllocationFailed",image->filename);
13861       size_hints->flags=0L;
13862       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13863         width,height);
13864       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13865         default_geometry,window_info.border_width,size_hints,&window_info.x,
13866         &window_info.y,&width,&height,&gravity);
13867       if (flags & (XValue | YValue))
13868         {
13869           width=window_attributes.width;
13870           height=window_attributes.height;
13871         }
13872       (void) XFree((void *) size_hints);
13873     }
13874   /*
13875     Create the X pixmap.
13876   */
13877   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13878     (unsigned int) height,window_info.depth);
13879   if (window_info.pixmap == (Pixmap) NULL)
13880     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13881       image->filename);
13882   /*
13883     Display pixmap on the window.
13884   */
13885   if (((unsigned int) width > window_info.width) ||
13886       ((unsigned int) height > window_info.height))
13887     (void) XFillRectangle(display,window_info.pixmap,
13888       window_info.annotate_context,0,0,(unsigned int) width,
13889       (unsigned int) height);
13890   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13891     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13892     window_info.width,(unsigned int) window_info.height);
13893   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13894   (void) XClearWindow(display,window_info.id);
13895   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13896   XDelay(display,delay == 0UL ? 10UL : delay);
13897   (void) XSync(display,MagickFalse);
13898   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13899 }
13900 
13901 /*
13902 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13903 %                                                                             %
13904 %                                                                             %
13905 %                                                                             %
13906 +   X D i s p l a y I m a g e                                                 %
13907 %                                                                             %
13908 %                                                                             %
13909 %                                                                             %
13910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13911 %
13912 %  XDisplayImage() displays an image via X11.  A new image is created and
13913 %  returned if the user interactively transforms the displayed image.
13914 %
13915 %  The format of the XDisplayImage method is:
13916 %
13917 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13918 %        char **argv,int argc,Image **image,size_t *state)
13919 %
13920 %  A description of each parameter follows:
13921 %
13922 %    o nexus:  Method XDisplayImage returns an image when the
13923 %      user chooses 'Open Image' from the command menu or picks a tile
13924 %      from the image directory.  Otherwise a null image is returned.
13925 %
13926 %    o display: Specifies a connection to an X server;  returned from
13927 %      XOpenDisplay.
13928 %
13929 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13930 %
13931 %    o argv: Specifies the application's argument list.
13932 %
13933 %    o argc: Specifies the number of arguments.
13934 %
13935 %    o image: Specifies an address to an address of an Image structure;
13936 %
13937 */
XDisplayImage(Display * display,XResourceInfo * resource_info,char ** argv,int argc,Image ** image,size_t * state)13938 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13939   char **argv,int argc,Image **image,size_t *state)
13940 {
13941 #define MagnifySize  256  /* must be a power of 2 */
13942 #define MagickMenus  10
13943 #define MagickTitle  "Commands"
13944 
13945   const char
13946     *const CommandMenu[] =
13947     {
13948       "File",
13949       "Edit",
13950       "View",
13951       "Transform",
13952       "Enhance",
13953       "Effects",
13954       "F/X",
13955       "Image Edit",
13956       "Miscellany",
13957       "Help",
13958       (char *) NULL
13959     },
13960     *const FileMenu[] =
13961     {
13962       "Open...",
13963       "Next",
13964       "Former",
13965       "Select...",
13966       "Save...",
13967       "Print...",
13968       "Delete...",
13969       "New...",
13970       "Visual Directory...",
13971       "Quit",
13972       (char *) NULL
13973     },
13974     *const EditMenu[] =
13975     {
13976       "Undo",
13977       "Redo",
13978       "Cut",
13979       "Copy",
13980       "Paste",
13981       (char *) NULL
13982     },
13983     *const ViewMenu[] =
13984     {
13985       "Half Size",
13986       "Original Size",
13987       "Double Size",
13988       "Resize...",
13989       "Apply",
13990       "Refresh",
13991       "Restore",
13992       (char *) NULL
13993     },
13994     *const TransformMenu[] =
13995     {
13996       "Crop",
13997       "Chop",
13998       "Flop",
13999       "Flip",
14000       "Rotate Right",
14001       "Rotate Left",
14002       "Rotate...",
14003       "Shear...",
14004       "Roll...",
14005       "Trim Edges",
14006       (char *) NULL
14007     },
14008     *const EnhanceMenu[] =
14009     {
14010       "Hue...",
14011       "Saturation...",
14012       "Brightness...",
14013       "Gamma...",
14014       "Spiff",
14015       "Dull",
14016       "Contrast Stretch...",
14017       "Sigmoidal Contrast...",
14018       "Normalize",
14019       "Equalize",
14020       "Negate",
14021       "Grayscale",
14022       "Map...",
14023       "Quantize...",
14024       (char *) NULL
14025     },
14026     *const EffectsMenu[] =
14027     {
14028       "Despeckle",
14029       "Emboss",
14030       "Reduce Noise",
14031       "Add Noise...",
14032       "Sharpen...",
14033       "Blur...",
14034       "Threshold...",
14035       "Edge Detect...",
14036       "Spread...",
14037       "Shade...",
14038       "Raise...",
14039       "Segment...",
14040       (char *) NULL
14041     },
14042     *const FXMenu[] =
14043     {
14044       "Solarize...",
14045       "Sepia Tone...",
14046       "Swirl...",
14047       "Implode...",
14048       "Vignette...",
14049       "Wave...",
14050       "Oil Paint...",
14051       "Charcoal Draw...",
14052       (char *) NULL
14053     },
14054     *const ImageEditMenu[] =
14055     {
14056       "Annotate...",
14057       "Draw...",
14058       "Color...",
14059       "Matte...",
14060       "Composite...",
14061       "Add Border...",
14062       "Add Frame...",
14063       "Comment...",
14064       "Launch...",
14065       "Region of Interest...",
14066       (char *) NULL
14067     },
14068     *const MiscellanyMenu[] =
14069     {
14070       "Image Info",
14071       "Zoom Image",
14072       "Show Preview...",
14073       "Show Histogram",
14074       "Show Matte",
14075       "Background...",
14076       "Slide Show...",
14077       "Preferences...",
14078       (char *) NULL
14079     },
14080     *const HelpMenu[] =
14081     {
14082       "Overview",
14083       "Browse Documentation",
14084       "About Display",
14085       (char *) NULL
14086     },
14087     *const ShortCutsMenu[] =
14088     {
14089       "Next",
14090       "Former",
14091       "Open...",
14092       "Save...",
14093       "Print...",
14094       "Undo",
14095       "Restore",
14096       "Image Info",
14097       "Quit",
14098       (char *) NULL
14099     },
14100     *const VirtualMenu[] =
14101     {
14102       "Image Info",
14103       "Print",
14104       "Next",
14105       "Quit",
14106       (char *) NULL
14107     };
14108 
14109   const char
14110     *const *Menus[MagickMenus] =
14111     {
14112       FileMenu,
14113       EditMenu,
14114       ViewMenu,
14115       TransformMenu,
14116       EnhanceMenu,
14117       EffectsMenu,
14118       FXMenu,
14119       ImageEditMenu,
14120       MiscellanyMenu,
14121       HelpMenu
14122     };
14123 
14124   static CommandType
14125     CommandMenus[] =
14126     {
14127       NullCommand,
14128       NullCommand,
14129       NullCommand,
14130       NullCommand,
14131       NullCommand,
14132       NullCommand,
14133       NullCommand,
14134       NullCommand,
14135       NullCommand,
14136       NullCommand,
14137     },
14138     FileCommands[] =
14139     {
14140       OpenCommand,
14141       NextCommand,
14142       FormerCommand,
14143       SelectCommand,
14144       SaveCommand,
14145       PrintCommand,
14146       DeleteCommand,
14147       NewCommand,
14148       VisualDirectoryCommand,
14149       QuitCommand
14150     },
14151     EditCommands[] =
14152     {
14153       UndoCommand,
14154       RedoCommand,
14155       CutCommand,
14156       CopyCommand,
14157       PasteCommand
14158     },
14159     ViewCommands[] =
14160     {
14161       HalfSizeCommand,
14162       OriginalSizeCommand,
14163       DoubleSizeCommand,
14164       ResizeCommand,
14165       ApplyCommand,
14166       RefreshCommand,
14167       RestoreCommand
14168     },
14169     TransformCommands[] =
14170     {
14171       CropCommand,
14172       ChopCommand,
14173       FlopCommand,
14174       FlipCommand,
14175       RotateRightCommand,
14176       RotateLeftCommand,
14177       RotateCommand,
14178       ShearCommand,
14179       RollCommand,
14180       TrimCommand
14181     },
14182     EnhanceCommands[] =
14183     {
14184       HueCommand,
14185       SaturationCommand,
14186       BrightnessCommand,
14187       GammaCommand,
14188       SpiffCommand,
14189       DullCommand,
14190       ContrastStretchCommand,
14191       SigmoidalContrastCommand,
14192       NormalizeCommand,
14193       EqualizeCommand,
14194       NegateCommand,
14195       GrayscaleCommand,
14196       MapCommand,
14197       QuantizeCommand
14198     },
14199     EffectsCommands[] =
14200     {
14201       DespeckleCommand,
14202       EmbossCommand,
14203       ReduceNoiseCommand,
14204       AddNoiseCommand,
14205       SharpenCommand,
14206       BlurCommand,
14207       ThresholdCommand,
14208       EdgeDetectCommand,
14209       SpreadCommand,
14210       ShadeCommand,
14211       RaiseCommand,
14212       SegmentCommand
14213     },
14214     FXCommands[] =
14215     {
14216       SolarizeCommand,
14217       SepiaToneCommand,
14218       SwirlCommand,
14219       ImplodeCommand,
14220       VignetteCommand,
14221       WaveCommand,
14222       OilPaintCommand,
14223       CharcoalDrawCommand
14224     },
14225     ImageEditCommands[] =
14226     {
14227       AnnotateCommand,
14228       DrawCommand,
14229       ColorCommand,
14230       MatteCommand,
14231       CompositeCommand,
14232       AddBorderCommand,
14233       AddFrameCommand,
14234       CommentCommand,
14235       LaunchCommand,
14236       RegionofInterestCommand
14237     },
14238     MiscellanyCommands[] =
14239     {
14240       InfoCommand,
14241       ZoomCommand,
14242       ShowPreviewCommand,
14243       ShowHistogramCommand,
14244       ShowMatteCommand,
14245       BackgroundCommand,
14246       SlideShowCommand,
14247       PreferencesCommand
14248     },
14249     HelpCommands[] =
14250     {
14251       HelpCommand,
14252       BrowseDocumentationCommand,
14253       VersionCommand
14254     },
14255     ShortCutsCommands[] =
14256     {
14257       NextCommand,
14258       FormerCommand,
14259       OpenCommand,
14260       SaveCommand,
14261       PrintCommand,
14262       UndoCommand,
14263       RestoreCommand,
14264       InfoCommand,
14265       QuitCommand
14266     },
14267     VirtualCommands[] =
14268     {
14269       InfoCommand,
14270       PrintCommand,
14271       NextCommand,
14272       QuitCommand
14273     };
14274 
14275   static CommandType
14276     *Commands[MagickMenus] =
14277     {
14278       FileCommands,
14279       EditCommands,
14280       ViewCommands,
14281       TransformCommands,
14282       EnhanceCommands,
14283       EffectsCommands,
14284       FXCommands,
14285       ImageEditCommands,
14286       MiscellanyCommands,
14287       HelpCommands
14288     };
14289 
14290   char
14291     command[MaxTextExtent],
14292     *directory,
14293     geometry[MaxTextExtent],
14294     resource_name[MaxTextExtent];
14295 
14296   CommandType
14297     command_type;
14298 
14299   Image
14300     *display_image,
14301     *nexus;
14302 
14303   int
14304     entry,
14305     id;
14306 
14307   KeySym
14308     key_symbol;
14309 
14310   MagickStatusType
14311     context_mask,
14312     status;
14313 
14314   RectangleInfo
14315     geometry_info;
14316 
14317   int
14318     i;
14319 
14320   static char
14321     working_directory[MaxTextExtent];
14322 
14323   static XPoint
14324     vid_info;
14325 
14326   static XWindowInfo
14327     *magick_windows[MaxXWindows];
14328 
14329   static unsigned int
14330     number_windows;
14331 
14332   struct stat
14333     attributes;
14334 
14335   time_t
14336     timer,
14337     timestamp,
14338     update_time;
14339 
14340   unsigned int
14341     height,
14342     width;
14343 
14344   size_t
14345     delay;
14346 
14347   WarningHandler
14348     warning_handler;
14349 
14350   Window
14351     root_window;
14352 
14353   XClassHint
14354     *class_hints;
14355 
14356   XEvent
14357     event;
14358 
14359   XFontStruct
14360     *font_info;
14361 
14362   XGCValues
14363     context_values;
14364 
14365   XPixelInfo
14366     *icon_pixel,
14367     *pixel;
14368 
14369   XResourceInfo
14370     *icon_resources;
14371 
14372   XStandardColormap
14373     *icon_map,
14374     *map_info;
14375 
14376   XVisualInfo
14377     *icon_visual,
14378     *visual_info;
14379 
14380   XWindowChanges
14381     window_changes;
14382 
14383   XWindows
14384     *windows;
14385 
14386   XWMHints
14387     *manager_hints;
14388 
14389   assert(image != (Image **) NULL);
14390   assert((*image)->signature == MagickCoreSignature);
14391   if ((*image)->debug != MagickFalse)
14392     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14393   display_image=(*image);
14394   warning_handler=(WarningHandler) NULL;
14395   windows=XSetWindows((XWindows *) ~0);
14396   if (windows != (XWindows *) NULL)
14397     {
14398       int
14399         status;
14400 
14401       if (*working_directory == '\0')
14402         (void) CopyMagickString(working_directory,".",MaxTextExtent);
14403       status=chdir(working_directory);
14404       if (status == -1)
14405         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14406           FileOpenError,"UnableToOpenFile","%s",working_directory);
14407       warning_handler=resource_info->display_warnings ?
14408         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14409       warning_handler=resource_info->display_warnings ?
14410         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14411     }
14412   else
14413     {
14414       /*
14415         Allocate windows structure.
14416       */
14417       resource_info->colors=display_image->colors;
14418       windows=XSetWindows(XInitializeWindows(display,resource_info));
14419       if (windows == (XWindows *) NULL)
14420         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14421           (*image)->filename);
14422       /*
14423         Initialize window id's.
14424       */
14425       number_windows=0;
14426       magick_windows[number_windows++]=(&windows->icon);
14427       magick_windows[number_windows++]=(&windows->backdrop);
14428       magick_windows[number_windows++]=(&windows->image);
14429       magick_windows[number_windows++]=(&windows->info);
14430       magick_windows[number_windows++]=(&windows->command);
14431       magick_windows[number_windows++]=(&windows->widget);
14432       magick_windows[number_windows++]=(&windows->popup);
14433       magick_windows[number_windows++]=(&windows->magnify);
14434       magick_windows[number_windows++]=(&windows->pan);
14435       for (i=0; i < (int) number_windows; i++)
14436         magick_windows[i]->id=(Window) NULL;
14437       vid_info.x=0;
14438       vid_info.y=0;
14439     }
14440   /*
14441     Initialize font info.
14442   */
14443   if (windows->font_info != (XFontStruct *) NULL)
14444     (void) XFreeFont(display,windows->font_info);
14445   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14446   if (windows->font_info == (XFontStruct *) NULL)
14447     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14448       resource_info->font);
14449   /*
14450     Initialize Standard Colormap.
14451   */
14452   map_info=windows->map_info;
14453   icon_map=windows->icon_map;
14454   visual_info=windows->visual_info;
14455   icon_visual=windows->icon_visual;
14456   pixel=windows->pixel_info;
14457   icon_pixel=windows->icon_pixel;
14458   font_info=windows->font_info;
14459   icon_resources=windows->icon_resources;
14460   class_hints=windows->class_hints;
14461   manager_hints=windows->manager_hints;
14462   root_window=XRootWindow(display,visual_info->screen);
14463   nexus=NewImageList();
14464   if (display_image->debug != MagickFalse)
14465     {
14466       (void) LogMagickEvent(X11Event,GetMagickModule(),
14467         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14468         (double) display_image->scene,(double) display_image->columns,
14469         (double) display_image->rows);
14470       if (display_image->colors != 0)
14471         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14472           display_image->colors);
14473       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14474         display_image->magick);
14475     }
14476   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14477     map_info,pixel);
14478   display_image->taint=MagickFalse;
14479   /*
14480     Initialize graphic context.
14481   */
14482   windows->context.id=(Window) NULL;
14483   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14484     resource_info,&windows->context);
14485   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14486   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14487   class_hints->res_class[0]=(char) LocaleUppercase((int)
14488     class_hints->res_class[0]);
14489   manager_hints->flags=InputHint | StateHint;
14490   manager_hints->input=MagickFalse;
14491   manager_hints->initial_state=WithdrawnState;
14492   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14493     &windows->context);
14494   if (display_image->debug != MagickFalse)
14495     (void) LogMagickEvent(X11Event,GetMagickModule(),
14496       "Window id: 0x%lx (context)",windows->context.id);
14497   context_values.background=pixel->background_color.pixel;
14498   context_values.font=font_info->fid;
14499   context_values.foreground=pixel->foreground_color.pixel;
14500   context_values.graphics_exposures=MagickFalse;
14501   context_mask=(MagickStatusType)
14502     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14503   if (pixel->annotate_context != (GC) NULL)
14504     (void) XFreeGC(display,pixel->annotate_context);
14505   pixel->annotate_context=XCreateGC(display,windows->context.id,
14506     context_mask,&context_values);
14507   if (pixel->annotate_context == (GC) NULL)
14508     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14509       display_image->filename);
14510   context_values.background=pixel->depth_color.pixel;
14511   if (pixel->widget_context != (GC) NULL)
14512     (void) XFreeGC(display,pixel->widget_context);
14513   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14514     &context_values);
14515   if (pixel->widget_context == (GC) NULL)
14516     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14517       display_image->filename);
14518   context_values.background=pixel->foreground_color.pixel;
14519   context_values.foreground=pixel->background_color.pixel;
14520   context_values.plane_mask=context_values.background ^
14521     context_values.foreground;
14522   if (pixel->highlight_context != (GC) NULL)
14523     (void) XFreeGC(display,pixel->highlight_context);
14524   pixel->highlight_context=XCreateGC(display,windows->context.id,
14525     (size_t) (context_mask | GCPlaneMask),&context_values);
14526   if (pixel->highlight_context == (GC) NULL)
14527     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14528       display_image->filename);
14529   (void) XDestroyWindow(display,windows->context.id);
14530   /*
14531     Initialize icon window.
14532   */
14533   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14534     icon_resources,&windows->icon);
14535   windows->icon.geometry=resource_info->icon_geometry;
14536   XBestIconSize(display,&windows->icon,display_image);
14537   windows->icon.attributes.colormap=XDefaultColormap(display,
14538     icon_visual->screen);
14539   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14540   manager_hints->flags=InputHint | StateHint;
14541   manager_hints->input=MagickFalse;
14542   manager_hints->initial_state=IconicState;
14543   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14544     &windows->icon);
14545   if (display_image->debug != MagickFalse)
14546     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14547       windows->icon.id);
14548   /*
14549     Initialize graphic context for icon window.
14550   */
14551   if (icon_pixel->annotate_context != (GC) NULL)
14552     (void) XFreeGC(display,icon_pixel->annotate_context);
14553   context_values.background=icon_pixel->background_color.pixel;
14554   context_values.foreground=icon_pixel->foreground_color.pixel;
14555   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14556     (size_t) (GCBackground | GCForeground),&context_values);
14557   if (icon_pixel->annotate_context == (GC) NULL)
14558     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14559       display_image->filename);
14560   windows->icon.annotate_context=icon_pixel->annotate_context;
14561   /*
14562     Initialize Image window.
14563   */
14564   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14565     &windows->image);
14566   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14567   if (resource_info->use_shared_memory == MagickFalse)
14568     windows->image.shared_memory=MagickFalse;
14569   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14570     {
14571       char
14572         *title;
14573 
14574       title=InterpretImageProperties(resource_info->image_info,display_image,
14575         resource_info->title);
14576       (void) CloneString(&windows->image.name,title);
14577       (void) CloneString(&windows->image.icon_name,title);
14578       title=DestroyString(title);
14579     }
14580   else
14581     {
14582       char
14583         filename[MaxTextExtent],
14584         window_name[MaxTextExtent];
14585 
14586       /*
14587         Window name is the base of the filename.
14588       */
14589       GetPathComponent(display_image->magick_filename,TailPath,filename);
14590       if (display_image->scene == 0)
14591         (void) FormatLocaleString(window_name,MaxTextExtent,"%s: %s",
14592           MagickPackageName,filename);
14593       else
14594         (void) FormatLocaleString(window_name,MaxTextExtent,
14595           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14596           (double) display_image->scene,(double) GetImageListLength(
14597           display_image));
14598       (void) CloneString(&windows->image.name,window_name);
14599       (void) CloneString(&windows->image.icon_name,filename);
14600     }
14601   if (resource_info->immutable)
14602     windows->image.immutable=MagickTrue;
14603   windows->image.use_pixmap=resource_info->use_pixmap;
14604   windows->image.geometry=resource_info->image_geometry;
14605   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14606     XDisplayWidth(display,visual_info->screen),
14607     XDisplayHeight(display,visual_info->screen));
14608   geometry_info.width=display_image->columns;
14609   geometry_info.height=display_image->rows;
14610   geometry_info.x=0;
14611   geometry_info.y=0;
14612   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14613     &geometry_info.width,&geometry_info.height);
14614   windows->image.width=(unsigned int) geometry_info.width;
14615   windows->image.height=(unsigned int) geometry_info.height;
14616   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14617     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14618     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14619     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14620   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14621     resource_info,&windows->backdrop);
14622   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14623     {
14624       /*
14625         Initialize backdrop window.
14626       */
14627       windows->backdrop.x=0;
14628       windows->backdrop.y=0;
14629       (void) CloneString(&windows->backdrop.name,"Backdrop");
14630       windows->backdrop.flags=(size_t) (USSize | USPosition);
14631       windows->backdrop.width=(unsigned int)
14632         XDisplayWidth(display,visual_info->screen);
14633       windows->backdrop.height=(unsigned int)
14634         XDisplayHeight(display,visual_info->screen);
14635       windows->backdrop.border_width=0;
14636       windows->backdrop.immutable=MagickTrue;
14637       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14638         ButtonReleaseMask;
14639       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14640         StructureNotifyMask;
14641       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14642       manager_hints->icon_window=windows->icon.id;
14643       manager_hints->input=MagickTrue;
14644       manager_hints->initial_state=resource_info->iconic ? IconicState :
14645         NormalState;
14646       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14647         &windows->backdrop);
14648       if (display_image->debug != MagickFalse)
14649         (void) LogMagickEvent(X11Event,GetMagickModule(),
14650           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14651       (void) XMapWindow(display,windows->backdrop.id);
14652       (void) XClearWindow(display,windows->backdrop.id);
14653       if (windows->image.id != (Window) NULL)
14654         {
14655           (void) XDestroyWindow(display,windows->image.id);
14656           windows->image.id=(Window) NULL;
14657         }
14658       /*
14659         Position image in the center the backdrop.
14660       */
14661       windows->image.flags|=USPosition;
14662       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14663         (windows->image.width/2);
14664       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14665         (windows->image.height/2);
14666     }
14667   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14668   manager_hints->icon_window=windows->icon.id;
14669   manager_hints->input=MagickTrue;
14670   manager_hints->initial_state=resource_info->iconic ? IconicState :
14671     NormalState;
14672   if (windows->group_leader.id != (Window) NULL)
14673     {
14674       /*
14675         Follow the leader.
14676       */
14677       manager_hints->flags|=WindowGroupHint;
14678       manager_hints->window_group=windows->group_leader.id;
14679       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14680       if (display_image->debug != MagickFalse)
14681         (void) LogMagickEvent(X11Event,GetMagickModule(),
14682           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14683     }
14684   XMakeWindow(display,
14685     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14686     argv,argc,class_hints,manager_hints,&windows->image);
14687   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14688     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14689   if (windows->group_leader.id != (Window) NULL)
14690     (void) XSetTransientForHint(display,windows->image.id,
14691       windows->group_leader.id);
14692   if (display_image->debug != MagickFalse)
14693     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14694       windows->image.id);
14695   /*
14696     Initialize Info widget.
14697   */
14698   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14699     &windows->info);
14700   (void) CloneString(&windows->info.name,"Info");
14701   (void) CloneString(&windows->info.icon_name,"Info");
14702   windows->info.border_width=1;
14703   windows->info.x=2;
14704   windows->info.y=2;
14705   windows->info.flags|=PPosition;
14706   windows->info.attributes.win_gravity=UnmapGravity;
14707   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14708     StructureNotifyMask;
14709   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14710   manager_hints->input=MagickFalse;
14711   manager_hints->initial_state=NormalState;
14712   manager_hints->window_group=windows->image.id;
14713   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14714     &windows->info);
14715   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14716     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14717   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14718     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14719   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14720   if (windows->image.mapped != MagickFalse)
14721     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14722   if (display_image->debug != MagickFalse)
14723     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14724       windows->info.id);
14725   /*
14726     Initialize Command widget.
14727   */
14728   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14729     resource_info,&windows->command);
14730   windows->command.data=MagickMenus;
14731   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14732   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14733     resource_info->client_name);
14734   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14735     resource_name,"geometry",(char *) NULL);
14736   (void) CloneString(&windows->command.name,MagickTitle);
14737   windows->command.border_width=0;
14738   windows->command.flags|=PPosition;
14739   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14740     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14741     OwnerGrabButtonMask | StructureNotifyMask;
14742   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14743   manager_hints->input=MagickTrue;
14744   manager_hints->initial_state=NormalState;
14745   manager_hints->window_group=windows->image.id;
14746   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14747     &windows->command);
14748   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14749     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14750     HighlightHeight);
14751   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14752     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14753   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14754   if (windows->command.mapped != MagickFalse)
14755     (void) XMapRaised(display,windows->command.id);
14756   if (display_image->debug != MagickFalse)
14757     (void) LogMagickEvent(X11Event,GetMagickModule(),
14758       "Window id: 0x%lx (command)",windows->command.id);
14759   /*
14760     Initialize Widget window.
14761   */
14762   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14763     resource_info,&windows->widget);
14764   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14765     resource_info->client_name);
14766   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14767     resource_name,"geometry",(char *) NULL);
14768   windows->widget.border_width=0;
14769   windows->widget.flags|=PPosition;
14770   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14771     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14772     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14773     StructureNotifyMask;
14774   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14775   manager_hints->input=MagickTrue;
14776   manager_hints->initial_state=NormalState;
14777   manager_hints->window_group=windows->image.id;
14778   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14779     &windows->widget);
14780   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14781     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14782   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14783     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14784   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14785   if (display_image->debug != MagickFalse)
14786     (void) LogMagickEvent(X11Event,GetMagickModule(),
14787       "Window id: 0x%lx (widget)",windows->widget.id);
14788   /*
14789     Initialize popup window.
14790   */
14791   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14792     resource_info,&windows->popup);
14793   windows->popup.border_width=0;
14794   windows->popup.flags|=PPosition;
14795   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14796     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14797     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14798   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14799   manager_hints->input=MagickTrue;
14800   manager_hints->initial_state=NormalState;
14801   manager_hints->window_group=windows->image.id;
14802   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14803     &windows->popup);
14804   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14805     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14806   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14807     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14808   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14809   if (display_image->debug != MagickFalse)
14810     (void) LogMagickEvent(X11Event,GetMagickModule(),
14811       "Window id: 0x%lx (pop up)",windows->popup.id);
14812   /*
14813     Initialize Magnify window and cursor.
14814   */
14815   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14816     resource_info,&windows->magnify);
14817   if (resource_info->use_shared_memory == MagickFalse)
14818     windows->magnify.shared_memory=MagickFalse;
14819   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14820     resource_info->client_name);
14821   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14822     resource_name,"geometry",(char *) NULL);
14823   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14824     resource_info->magnify);
14825   if (windows->magnify.cursor != (Cursor) NULL)
14826     (void) XFreeCursor(display,windows->magnify.cursor);
14827   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14828     map_info->colormap,resource_info->background_color,
14829     resource_info->foreground_color);
14830   if (windows->magnify.cursor == (Cursor) NULL)
14831     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14832       display_image->filename);
14833   windows->magnify.width=MagnifySize;
14834   windows->magnify.height=MagnifySize;
14835   windows->magnify.flags|=PPosition;
14836   windows->magnify.min_width=MagnifySize;
14837   windows->magnify.min_height=MagnifySize;
14838   windows->magnify.width_inc=MagnifySize;
14839   windows->magnify.height_inc=MagnifySize;
14840   windows->magnify.data=resource_info->magnify;
14841   windows->magnify.attributes.cursor=windows->magnify.cursor;
14842   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14843     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14844     StructureNotifyMask;
14845   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14846   manager_hints->input=MagickTrue;
14847   manager_hints->initial_state=NormalState;
14848   manager_hints->window_group=windows->image.id;
14849   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14850     &windows->magnify);
14851   if (display_image->debug != MagickFalse)
14852     (void) LogMagickEvent(X11Event,GetMagickModule(),
14853       "Window id: 0x%lx (magnify)",windows->magnify.id);
14854   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14855   /*
14856     Initialize panning window.
14857   */
14858   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14859     resource_info,&windows->pan);
14860   (void) CloneString(&windows->pan.name,"Pan Icon");
14861   windows->pan.width=windows->icon.width;
14862   windows->pan.height=windows->icon.height;
14863   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14864     resource_info->client_name);
14865   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14866     resource_name,"geometry",(char *) NULL);
14867   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14868     &windows->pan.width,&windows->pan.height);
14869   windows->pan.flags|=PPosition;
14870   windows->pan.immutable=MagickTrue;
14871   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14872     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14873     StructureNotifyMask;
14874   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14875   manager_hints->input=MagickFalse;
14876   manager_hints->initial_state=NormalState;
14877   manager_hints->window_group=windows->image.id;
14878   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14879     &windows->pan);
14880   if (display_image->debug != MagickFalse)
14881     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14882       windows->pan.id);
14883   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14884   if (windows->info.mapped != MagickFalse)
14885     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14886   if ((windows->image.mapped == MagickFalse) ||
14887       (windows->backdrop.id != (Window) NULL))
14888     (void) XMapWindow(display,windows->image.id);
14889   /*
14890     Set our progress monitor and warning handlers.
14891   */
14892   if (warning_handler == (WarningHandler) NULL)
14893     {
14894       warning_handler=resource_info->display_warnings ?
14895         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14896       warning_handler=resource_info->display_warnings ?
14897         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14898     }
14899   /*
14900     Initialize Image and Magnify X images.
14901   */
14902   windows->image.x=0;
14903   windows->image.y=0;
14904   windows->magnify.shape=MagickFalse;
14905   width=(unsigned int) display_image->columns;
14906   height=(unsigned int) display_image->rows;
14907   if ((display_image->columns != width) || (display_image->rows != height))
14908     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14909       display_image->filename);
14910   status=XMakeImage(display,resource_info,&windows->image,display_image,
14911     width,height);
14912   if (status == MagickFalse)
14913     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14914       display_image->filename);
14915   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14916     windows->magnify.width,windows->magnify.height);
14917   if (status == MagickFalse)
14918     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14919       display_image->filename);
14920   if (windows->magnify.mapped != MagickFalse)
14921     (void) XMapRaised(display,windows->magnify.id);
14922   if (windows->pan.mapped != MagickFalse)
14923     (void) XMapRaised(display,windows->pan.id);
14924   windows->image.window_changes.width=(int) display_image->columns;
14925   windows->image.window_changes.height=(int) display_image->rows;
14926   (void) XConfigureImage(display,resource_info,windows,display_image);
14927   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14928   (void) XSync(display,MagickFalse);
14929   /*
14930     Respond to events.
14931   */
14932   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14933   timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14934   update_time=0;
14935   if (resource_info->update != MagickFalse)
14936     {
14937       MagickBooleanType
14938         status;
14939 
14940       /*
14941         Determine when file data was last modified.
14942       */
14943       status=GetPathAttributes(display_image->filename,&attributes);
14944       if (status != MagickFalse)
14945         update_time=attributes.st_mtime;
14946     }
14947   *state&=(~FormerImageState);
14948   *state&=(~MontageImageState);
14949   *state&=(~NextImageState);
14950   do
14951   {
14952     /*
14953       Handle a window event.
14954     */
14955     if (windows->image.mapped != MagickFalse)
14956       if ((display_image->delay != 0) || (resource_info->update != 0))
14957         {
14958           if (timer < GetMagickTime())
14959             {
14960               if (resource_info->update == MagickFalse)
14961                 *state|=NextImageState | ExitState;
14962               else
14963                 {
14964                   MagickBooleanType
14965                     status;
14966 
14967                   /*
14968                     Determine if image file was modified.
14969                   */
14970                   status=GetPathAttributes(display_image->filename,&attributes);
14971                   if (status != MagickFalse)
14972                     if (update_time != attributes.st_mtime)
14973                       {
14974                         /*
14975                           Redisplay image.
14976                         */
14977                         (void) FormatLocaleString(
14978                           resource_info->image_info->filename,MaxTextExtent,
14979                           "%s:%s",display_image->magick,
14980                           display_image->filename);
14981                         nexus=ReadImage(resource_info->image_info,
14982                           &display_image->exception);
14983                         if (nexus != (Image *) NULL)
14984                           *state|=NextImageState | ExitState;
14985                       }
14986                   delay=display_image->delay/MagickMax(
14987                     display_image->ticks_per_second,1L);
14988                   timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14989                 }
14990             }
14991           if (XEventsQueued(display,QueuedAfterFlush) == 0)
14992             {
14993               /*
14994                 Do not block if delay > 0.
14995               */
14996               XDelay(display,SuspendTime << 2);
14997               continue;
14998             }
14999         }
15000     timestamp=GetMagickTime();
15001     (void) XNextEvent(display,&event);
15002     if (windows->image.stasis == MagickFalse)
15003       windows->image.stasis=(GetMagickTime()-timestamp) > 0 ?
15004         MagickTrue : MagickFalse;
15005     if (windows->magnify.stasis == MagickFalse)
15006       windows->magnify.stasis=(GetMagickTime()-timestamp) > 0 ?
15007         MagickTrue : MagickFalse;
15008     if (event.xany.window == windows->command.id)
15009       {
15010         /*
15011           Select a command from the Command widget.
15012         */
15013         id=XCommandWidget(display,windows,CommandMenu,&event);
15014         if (id < 0)
15015           continue;
15016         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15017         command_type=CommandMenus[id];
15018         if (id < MagickMenus)
15019           {
15020             /*
15021               Select a command from a pop-up menu.
15022             */
15023             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15024               command);
15025             if (entry < 0)
15026               continue;
15027             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15028             command_type=Commands[id][entry];
15029           }
15030         if (command_type != NullCommand)
15031           nexus=XMagickCommand(display,resource_info,windows,command_type,
15032             &display_image);
15033         continue;
15034       }
15035     switch (event.type)
15036     {
15037       case ButtonPress:
15038       {
15039         if (display_image->debug != MagickFalse)
15040           (void) LogMagickEvent(X11Event,GetMagickModule(),
15041             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15042             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15043         if ((event.xbutton.button == Button3) &&
15044             (event.xbutton.state & Mod1Mask))
15045           {
15046             /*
15047               Convert Alt-Button3 to Button2.
15048             */
15049             event.xbutton.button=Button2;
15050             event.xbutton.state&=(~Mod1Mask);
15051           }
15052         if (event.xbutton.window == windows->backdrop.id)
15053           {
15054             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15055               event.xbutton.time);
15056             break;
15057           }
15058         if (event.xbutton.window == windows->image.id)
15059           {
15060             switch (event.xbutton.button)
15061             {
15062               case Button1:
15063               {
15064                 if (resource_info->immutable)
15065                   {
15066                     /*
15067                       Select a command from the Virtual menu.
15068                     */
15069                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15070                       command);
15071                     if (entry >= 0)
15072                       nexus=XMagickCommand(display,resource_info,windows,
15073                         VirtualCommands[entry],&display_image);
15074                     break;
15075                   }
15076                 /*
15077                   Map/unmap Command widget.
15078                 */
15079                 if (windows->command.mapped != MagickFalse)
15080                   (void) XWithdrawWindow(display,windows->command.id,
15081                     windows->command.screen);
15082                 else
15083                   {
15084                     (void) XCommandWidget(display,windows,CommandMenu,
15085                       (XEvent *) NULL);
15086                     (void) XMapRaised(display,windows->command.id);
15087                   }
15088                 break;
15089               }
15090               case Button2:
15091               {
15092                 /*
15093                   User pressed the image magnify button.
15094                 */
15095                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15096                   &display_image);
15097                 XMagnifyImage(display,windows,&event);
15098                 break;
15099               }
15100               case Button3:
15101               {
15102                 if (resource_info->immutable)
15103                   {
15104                     /*
15105                       Select a command from the Virtual menu.
15106                     */
15107                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15108                       command);
15109                     if (entry >= 0)
15110                       nexus=XMagickCommand(display,resource_info,windows,
15111                         VirtualCommands[entry],&display_image);
15112                     break;
15113                   }
15114                 if (display_image->montage != (char *) NULL)
15115                   {
15116                     /*
15117                       Open or delete a tile from a visual image directory.
15118                     */
15119                     nexus=XTileImage(display,resource_info,windows,
15120                       display_image,&event);
15121                     if (nexus != (Image *) NULL)
15122                       *state|=MontageImageState | NextImageState | ExitState;
15123                     vid_info.x=(short int) windows->image.x;
15124                     vid_info.y=(short int) windows->image.y;
15125                     break;
15126                   }
15127                 /*
15128                   Select a command from the Short Cuts menu.
15129                 */
15130                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15131                   command);
15132                 if (entry >= 0)
15133                   nexus=XMagickCommand(display,resource_info,windows,
15134                     ShortCutsCommands[entry],&display_image);
15135                 break;
15136               }
15137               case Button4:
15138               {
15139                 /*
15140                   Wheel up.
15141                 */
15142                 XTranslateImage(display,windows,*image,XK_Up);
15143                 break;
15144               }
15145               case Button5:
15146               {
15147                 /*
15148                   Wheel down.
15149                 */
15150                 XTranslateImage(display,windows,*image,XK_Down);
15151                 break;
15152               }
15153               default:
15154                 break;
15155             }
15156             break;
15157           }
15158         if (event.xbutton.window == windows->magnify.id)
15159           {
15160             const char
15161               *const MagnifyMenu[] =
15162               {
15163                 "2",
15164                 "4",
15165                 "5",
15166                 "6",
15167                 "7",
15168                 "8",
15169                 "9",
15170                 "3",
15171                 (char *) NULL,
15172               };
15173 
15174             int
15175               factor;
15176 
15177             static KeySym
15178               MagnifyCommands[] =
15179               {
15180                 XK_2,
15181                 XK_4,
15182                 XK_5,
15183                 XK_6,
15184                 XK_7,
15185                 XK_8,
15186                 XK_9,
15187                 XK_3
15188               };
15189 
15190             /*
15191               Select a magnify factor from the pop-up menu.
15192             */
15193             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15194             if (factor >= 0)
15195               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15196             break;
15197           }
15198         if (event.xbutton.window == windows->pan.id)
15199           {
15200             switch (event.xbutton.button)
15201             {
15202               case Button4:
15203               {
15204                 /*
15205                   Wheel up.
15206                 */
15207                 XTranslateImage(display,windows,*image,XK_Up);
15208                 break;
15209               }
15210               case Button5:
15211               {
15212                 /*
15213                   Wheel down.
15214                 */
15215                 XTranslateImage(display,windows,*image,XK_Down);
15216                 break;
15217               }
15218               default:
15219               {
15220                 XPanImage(display,windows,&event);
15221                 break;
15222               }
15223             }
15224             break;
15225           }
15226         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15227           1L);
15228         timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15229         break;
15230       }
15231       case ButtonRelease:
15232       {
15233         if (display_image->debug != MagickFalse)
15234           (void) LogMagickEvent(X11Event,GetMagickModule(),
15235             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15236             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15237         break;
15238       }
15239       case ClientMessage:
15240       {
15241         if (display_image->debug != MagickFalse)
15242           (void) LogMagickEvent(X11Event,GetMagickModule(),
15243             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15244             event.xclient.message_type,event.xclient.format,(unsigned long)
15245             event.xclient.data.l[0]);
15246         if (event.xclient.message_type == windows->im_protocols)
15247           {
15248             if (*event.xclient.data.l == (long) windows->im_update_widget)
15249               {
15250                 (void) CloneString(&windows->command.name,MagickTitle);
15251                 windows->command.data=MagickMenus;
15252                 (void) XCommandWidget(display,windows,CommandMenu,
15253                   (XEvent *) NULL);
15254                 break;
15255               }
15256             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15257               {
15258                 /*
15259                   Update graphic context and window colormap.
15260                 */
15261                 for (i=0; i < (int) number_windows; i++)
15262                 {
15263                   if (magick_windows[i]->id == windows->icon.id)
15264                     continue;
15265                   context_values.background=pixel->background_color.pixel;
15266                   context_values.foreground=pixel->foreground_color.pixel;
15267                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15268                     context_mask,&context_values);
15269                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15270                     context_mask,&context_values);
15271                   context_values.background=pixel->foreground_color.pixel;
15272                   context_values.foreground=pixel->background_color.pixel;
15273                   context_values.plane_mask=context_values.background ^
15274                     context_values.foreground;
15275                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15276                     (size_t) (context_mask | GCPlaneMask),
15277                     &context_values);
15278                   magick_windows[i]->attributes.background_pixel=
15279                     pixel->background_color.pixel;
15280                   magick_windows[i]->attributes.border_pixel=
15281                     pixel->border_color.pixel;
15282                   magick_windows[i]->attributes.colormap=map_info->colormap;
15283                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15284                     (unsigned long) magick_windows[i]->mask,
15285                     &magick_windows[i]->attributes);
15286                 }
15287                 if (windows->pan.mapped != MagickFalse)
15288                   {
15289                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15290                       windows->pan.pixmap);
15291                     (void) XClearWindow(display,windows->pan.id);
15292                     XDrawPanRectangle(display,windows);
15293                   }
15294                 if (windows->backdrop.id != (Window) NULL)
15295                   (void) XInstallColormap(display,map_info->colormap);
15296                 break;
15297               }
15298             if (*event.xclient.data.l == (long) windows->im_former_image)
15299               {
15300                 *state|=FormerImageState | ExitState;
15301                 break;
15302               }
15303             if (*event.xclient.data.l == (long) windows->im_next_image)
15304               {
15305                 *state|=NextImageState | ExitState;
15306                 break;
15307               }
15308             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15309               {
15310                 *state|=RetainColorsState;
15311                 break;
15312               }
15313             if (*event.xclient.data.l == (long) windows->im_exit)
15314               {
15315                 *state|=ExitState;
15316                 break;
15317               }
15318             break;
15319           }
15320         if (event.xclient.message_type == windows->dnd_protocols)
15321           {
15322             Atom
15323               selection,
15324               type;
15325 
15326             int
15327               format,
15328               status;
15329 
15330             unsigned char
15331               *data;
15332 
15333             unsigned long
15334               after,
15335               length;
15336 
15337             /*
15338               Display image named by the Drag-and-Drop selection.
15339             */
15340             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15341               break;
15342             selection=XInternAtom(display,"DndSelection",MagickFalse);
15343             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15344               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15345               &length,&after,&data);
15346             if ((status != Success) || (length == 0))
15347               break;
15348             if (*event.xclient.data.l == 2)
15349               {
15350                 /*
15351                   Offix DND.
15352                 */
15353                 (void) CopyMagickString(resource_info->image_info->filename,
15354                   (char *) data,MaxTextExtent);
15355               }
15356             else
15357               {
15358                 /*
15359                   XDND.
15360                 */
15361                 if (strncmp((char *) data, "file:", 5) != 0)
15362                   {
15363                     (void) XFree((void *) data);
15364                     break;
15365                   }
15366                 (void) CopyMagickString(resource_info->image_info->filename,
15367                   ((char *) data)+5,MaxTextExtent);
15368               }
15369             nexus=ReadImage(resource_info->image_info,
15370               &display_image->exception);
15371             CatchException(&display_image->exception);
15372             if (nexus != (Image *) NULL)
15373               *state|=NextImageState | ExitState;
15374             (void) XFree((void *) data);
15375             break;
15376           }
15377         /*
15378           If client window delete message, exit.
15379         */
15380         if (event.xclient.message_type != windows->wm_protocols)
15381           break;
15382         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15383           break;
15384         (void) XWithdrawWindow(display,event.xclient.window,
15385           visual_info->screen);
15386         if (event.xclient.window == windows->image.id)
15387           {
15388             *state|=ExitState;
15389             break;
15390           }
15391         if (event.xclient.window == windows->pan.id)
15392           {
15393             /*
15394               Restore original image size when pan window is deleted.
15395             */
15396             windows->image.window_changes.width=windows->image.ximage->width;
15397             windows->image.window_changes.height=windows->image.ximage->height;
15398             (void) XConfigureImage(display,resource_info,windows,
15399               display_image);
15400           }
15401         break;
15402       }
15403       case ConfigureNotify:
15404       {
15405         if (display_image->debug != MagickFalse)
15406           (void) LogMagickEvent(X11Event,GetMagickModule(),
15407             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15408             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15409             event.xconfigure.y,event.xconfigure.send_event);
15410         if (event.xconfigure.window == windows->image.id)
15411           {
15412             /*
15413               Image window has a new configuration.
15414             */
15415             if (event.xconfigure.send_event != 0)
15416               {
15417                 XWindowChanges
15418                   window_changes;
15419 
15420                 /*
15421                   Position the transient windows relative of the Image window.
15422                 */
15423                 if (windows->command.geometry == (char *) NULL)
15424                   if (windows->command.mapped == MagickFalse)
15425                     {
15426                       windows->command.x=event.xconfigure.x-
15427                         windows->command.width-25;
15428                       windows->command.y=event.xconfigure.y;
15429                       XConstrainWindowPosition(display,&windows->command);
15430                       window_changes.x=windows->command.x;
15431                       window_changes.y=windows->command.y;
15432                       (void) XReconfigureWMWindow(display,windows->command.id,
15433                         windows->command.screen,(unsigned int) (CWX | CWY),
15434                         &window_changes);
15435                     }
15436                 if (windows->widget.geometry == (char *) NULL)
15437                   if (windows->widget.mapped == MagickFalse)
15438                     {
15439                       windows->widget.x=event.xconfigure.x+
15440                         event.xconfigure.width/10;
15441                       windows->widget.y=event.xconfigure.y+
15442                         event.xconfigure.height/10;
15443                       XConstrainWindowPosition(display,&windows->widget);
15444                       window_changes.x=windows->widget.x;
15445                       window_changes.y=windows->widget.y;
15446                       (void) XReconfigureWMWindow(display,windows->widget.id,
15447                         windows->widget.screen,(unsigned int) (CWX | CWY),
15448                         &window_changes);
15449                     }
15450                 if (windows->magnify.geometry == (char *) NULL)
15451                   if (windows->magnify.mapped == MagickFalse)
15452                     {
15453                       windows->magnify.x=event.xconfigure.x+
15454                         event.xconfigure.width+25;
15455                       windows->magnify.y=event.xconfigure.y;
15456                       XConstrainWindowPosition(display,&windows->magnify);
15457                       window_changes.x=windows->magnify.x;
15458                       window_changes.y=windows->magnify.y;
15459                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15460                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15461                         &window_changes);
15462                     }
15463                 if (windows->pan.geometry == (char *) NULL)
15464                   if (windows->pan.mapped == MagickFalse)
15465                     {
15466                       windows->pan.x=event.xconfigure.x+
15467                         event.xconfigure.width+25;
15468                       windows->pan.y=event.xconfigure.y+
15469                         windows->magnify.height+50;
15470                       XConstrainWindowPosition(display,&windows->pan);
15471                       window_changes.x=windows->pan.x;
15472                       window_changes.y=windows->pan.y;
15473                       (void) XReconfigureWMWindow(display,windows->pan.id,
15474                         windows->pan.screen,(unsigned int) (CWX | CWY),
15475                         &window_changes);
15476                     }
15477               }
15478             if ((event.xconfigure.width == (int) windows->image.width) &&
15479                 (event.xconfigure.height == (int) windows->image.height))
15480               break;
15481             windows->image.width=(unsigned int) event.xconfigure.width;
15482             windows->image.height=(unsigned int) event.xconfigure.height;
15483             windows->image.x=0;
15484             windows->image.y=0;
15485             if (display_image->montage != (char *) NULL)
15486               {
15487                 windows->image.x=vid_info.x;
15488                 windows->image.y=vid_info.y;
15489               }
15490             if ((windows->image.mapped != MagickFalse) &&
15491                 (windows->image.stasis != MagickFalse))
15492               {
15493                 /*
15494                   Update image window configuration.
15495                 */
15496                 windows->image.window_changes.width=event.xconfigure.width;
15497                 windows->image.window_changes.height=event.xconfigure.height;
15498                 (void) XConfigureImage(display,resource_info,windows,
15499                   display_image);
15500               }
15501             /*
15502               Update pan window configuration.
15503             */
15504             if ((event.xconfigure.width < windows->image.ximage->width) ||
15505                 (event.xconfigure.height < windows->image.ximage->height))
15506               {
15507                 (void) XMapRaised(display,windows->pan.id);
15508                 XDrawPanRectangle(display,windows);
15509               }
15510             else
15511               if (windows->pan.mapped != MagickFalse)
15512                 (void) XWithdrawWindow(display,windows->pan.id,
15513                   windows->pan.screen);
15514             break;
15515           }
15516         if (event.xconfigure.window == windows->magnify.id)
15517           {
15518             unsigned int
15519               magnify;
15520 
15521             /*
15522               Magnify window has a new configuration.
15523             */
15524             windows->magnify.width=(unsigned int) event.xconfigure.width;
15525             windows->magnify.height=(unsigned int) event.xconfigure.height;
15526             if (windows->magnify.mapped == MagickFalse)
15527               break;
15528             magnify=1;
15529             while ((int) magnify <= event.xconfigure.width)
15530               magnify<<=1;
15531             while ((int) magnify <= event.xconfigure.height)
15532               magnify<<=1;
15533             magnify>>=1;
15534             if (((int) magnify != event.xconfigure.width) ||
15535                 ((int) magnify != event.xconfigure.height))
15536               {
15537                 window_changes.width=(int) magnify;
15538                 window_changes.height=(int) magnify;
15539                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15540                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15541                   &window_changes);
15542                 break;
15543               }
15544             if ((windows->magnify.mapped != MagickFalse) &&
15545                 (windows->magnify.stasis != MagickFalse))
15546               {
15547                 status=XMakeImage(display,resource_info,&windows->magnify,
15548                   display_image,windows->magnify.width,windows->magnify.height);
15549                 XMakeMagnifyImage(display,windows);
15550               }
15551             break;
15552           }
15553         if ((windows->magnify.mapped != MagickFalse) &&
15554             (event.xconfigure.window == windows->pan.id))
15555           {
15556             /*
15557               Pan icon window has a new configuration.
15558             */
15559             if (event.xconfigure.send_event != 0)
15560               {
15561                 windows->pan.x=event.xconfigure.x;
15562                 windows->pan.y=event.xconfigure.y;
15563               }
15564             windows->pan.width=(unsigned int) event.xconfigure.width;
15565             windows->pan.height=(unsigned int) event.xconfigure.height;
15566             break;
15567           }
15568         if (event.xconfigure.window == windows->icon.id)
15569           {
15570             /*
15571               Icon window has a new configuration.
15572             */
15573             windows->icon.width=(unsigned int) event.xconfigure.width;
15574             windows->icon.height=(unsigned int) event.xconfigure.height;
15575             break;
15576           }
15577         break;
15578       }
15579       case DestroyNotify:
15580       {
15581         /*
15582           Group leader has exited.
15583         */
15584         if (display_image->debug != MagickFalse)
15585           (void) LogMagickEvent(X11Event,GetMagickModule(),
15586             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15587         if (event.xdestroywindow.window == windows->group_leader.id)
15588           {
15589             *state|=ExitState;
15590             break;
15591           }
15592         break;
15593       }
15594       case EnterNotify:
15595       {
15596         /*
15597           Selectively install colormap.
15598         */
15599         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15600           if (event.xcrossing.mode != NotifyUngrab)
15601             XInstallColormap(display,map_info->colormap);
15602         break;
15603       }
15604       case Expose:
15605       {
15606         if (display_image->debug != MagickFalse)
15607           (void) LogMagickEvent(X11Event,GetMagickModule(),
15608             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15609             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15610             event.xexpose.y);
15611         /*
15612           Refresh windows that are now exposed.
15613         */
15614         if ((event.xexpose.window == windows->image.id) &&
15615             (windows->image.mapped != MagickFalse))
15616           {
15617             XRefreshWindow(display,&windows->image,&event);
15618             delay=display_image->delay/MagickMax(
15619               display_image->ticks_per_second,1L);
15620             timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15621             break;
15622           }
15623         if ((event.xexpose.window == windows->magnify.id) &&
15624             (windows->magnify.mapped != MagickFalse))
15625           {
15626             XMakeMagnifyImage(display,windows);
15627             break;
15628           }
15629         if (event.xexpose.window == windows->pan.id)
15630           {
15631             XDrawPanRectangle(display,windows);
15632             break;
15633           }
15634         if (event.xexpose.window == windows->icon.id)
15635           {
15636             XRefreshWindow(display,&windows->icon,&event);
15637             break;
15638           }
15639         break;
15640       }
15641       case KeyPress:
15642       {
15643         int
15644           length;
15645 
15646         /*
15647           Respond to a user key press.
15648         */
15649         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15650           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15651         *(command+length)='\0';
15652         if (display_image->debug != MagickFalse)
15653           (void) LogMagickEvent(X11Event,GetMagickModule(),
15654             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15655             key_symbol,command);
15656         if (event.xkey.window == windows->image.id)
15657           {
15658             command_type=XImageWindowCommand(display,resource_info,windows,
15659               event.xkey.state,key_symbol,&display_image);
15660             if (command_type != NullCommand)
15661               nexus=XMagickCommand(display,resource_info,windows,command_type,
15662                 &display_image);
15663           }
15664         if (event.xkey.window == windows->magnify.id)
15665           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15666         if (event.xkey.window == windows->pan.id)
15667           {
15668             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15669               (void) XWithdrawWindow(display,windows->pan.id,
15670                 windows->pan.screen);
15671             else
15672               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15673                 XTextViewHelp(display,resource_info,windows,MagickFalse,
15674                   "Help Viewer - Image Pan",ImagePanHelp);
15675               else
15676                 XTranslateImage(display,windows,*image,key_symbol);
15677           }
15678         delay=display_image->delay/MagickMax(
15679           display_image->ticks_per_second,1L);
15680         timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15681         break;
15682       }
15683       case KeyRelease:
15684       {
15685         /*
15686           Respond to a user key release.
15687         */
15688         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15689           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15690         if (display_image->debug != MagickFalse)
15691           (void) LogMagickEvent(X11Event,GetMagickModule(),
15692             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15693         break;
15694       }
15695       case LeaveNotify:
15696       {
15697         /*
15698           Selectively uninstall colormap.
15699         */
15700         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15701           if (event.xcrossing.mode != NotifyUngrab)
15702             XUninstallColormap(display,map_info->colormap);
15703         break;
15704       }
15705       case MapNotify:
15706       {
15707         if (display_image->debug != MagickFalse)
15708           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15709             event.xmap.window);
15710         if (event.xmap.window == windows->backdrop.id)
15711           {
15712             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15713               CurrentTime);
15714             windows->backdrop.mapped=MagickTrue;
15715             break;
15716           }
15717         if (event.xmap.window == windows->image.id)
15718           {
15719             if (windows->backdrop.id != (Window) NULL)
15720               (void) XInstallColormap(display,map_info->colormap);
15721             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15722               {
15723                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15724                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15725               }
15726             if (((int) windows->image.width < windows->image.ximage->width) ||
15727                 ((int) windows->image.height < windows->image.ximage->height))
15728               (void) XMapRaised(display,windows->pan.id);
15729             windows->image.mapped=MagickTrue;
15730             break;
15731           }
15732         if (event.xmap.window == windows->magnify.id)
15733           {
15734             XMakeMagnifyImage(display,windows);
15735             windows->magnify.mapped=MagickTrue;
15736             (void) XWithdrawWindow(display,windows->info.id,
15737               windows->info.screen);
15738             break;
15739           }
15740         if (event.xmap.window == windows->pan.id)
15741           {
15742             XMakePanImage(display,resource_info,windows,display_image);
15743             windows->pan.mapped=MagickTrue;
15744             break;
15745           }
15746         if (event.xmap.window == windows->info.id)
15747           {
15748             windows->info.mapped=MagickTrue;
15749             break;
15750           }
15751         if (event.xmap.window == windows->icon.id)
15752           {
15753             MagickBooleanType
15754               taint;
15755 
15756             /*
15757               Create an icon image.
15758             */
15759             taint=display_image->taint;
15760             XMakeStandardColormap(display,icon_visual,icon_resources,
15761               display_image,icon_map,icon_pixel);
15762             (void) XMakeImage(display,icon_resources,&windows->icon,
15763               display_image,windows->icon.width,windows->icon.height);
15764             display_image->taint=taint;
15765             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15766               windows->icon.pixmap);
15767             (void) XClearWindow(display,windows->icon.id);
15768             (void) XWithdrawWindow(display,windows->info.id,
15769               windows->info.screen);
15770             windows->icon.mapped=MagickTrue;
15771             break;
15772           }
15773         if (event.xmap.window == windows->command.id)
15774           {
15775             windows->command.mapped=MagickTrue;
15776             break;
15777           }
15778         if (event.xmap.window == windows->popup.id)
15779           {
15780             windows->popup.mapped=MagickTrue;
15781             break;
15782           }
15783         if (event.xmap.window == windows->widget.id)
15784           {
15785             windows->widget.mapped=MagickTrue;
15786             break;
15787           }
15788         break;
15789       }
15790       case MappingNotify:
15791       {
15792         (void) XRefreshKeyboardMapping(&event.xmapping);
15793         break;
15794       }
15795       case NoExpose:
15796         break;
15797       case PropertyNotify:
15798       {
15799         Atom
15800           type;
15801 
15802         int
15803           format,
15804           status;
15805 
15806         unsigned char
15807           *data;
15808 
15809         unsigned long
15810           after,
15811           length;
15812 
15813         if (display_image->debug != MagickFalse)
15814           (void) LogMagickEvent(X11Event,GetMagickModule(),
15815             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15816             event.xproperty.atom,event.xproperty.state);
15817         if (event.xproperty.atom != windows->im_remote_command)
15818           break;
15819         /*
15820           Display image named by the remote command protocol.
15821         */
15822         status=XGetWindowProperty(display,event.xproperty.window,
15823           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15824           AnyPropertyType,&type,&format,&length,&after,&data);
15825         if ((status != Success) || (length == 0))
15826           break;
15827         if (LocaleCompare((char *) data,"-quit") == 0)
15828           {
15829             XClientMessage(display,windows->image.id,windows->im_protocols,
15830               windows->im_exit,CurrentTime);
15831             (void) XFree((void *) data);
15832             break;
15833           }
15834         (void) CopyMagickString(resource_info->image_info->filename,
15835           (char *) data,MaxTextExtent);
15836         (void) XFree((void *) data);
15837         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15838         CatchException(&display_image->exception);
15839         if (nexus != (Image *) NULL)
15840           *state|=NextImageState | ExitState;
15841         break;
15842       }
15843       case ReparentNotify:
15844       {
15845         if (display_image->debug != MagickFalse)
15846           (void) LogMagickEvent(X11Event,GetMagickModule(),
15847             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15848             event.xreparent.window);
15849         break;
15850       }
15851       case UnmapNotify:
15852       {
15853         if (display_image->debug != MagickFalse)
15854           (void) LogMagickEvent(X11Event,GetMagickModule(),
15855             "Unmap Notify: 0x%lx",event.xunmap.window);
15856         if (event.xunmap.window == windows->backdrop.id)
15857           {
15858             windows->backdrop.mapped=MagickFalse;
15859             break;
15860           }
15861         if (event.xunmap.window == windows->image.id)
15862           {
15863             windows->image.mapped=MagickFalse;
15864             break;
15865           }
15866         if (event.xunmap.window == windows->magnify.id)
15867           {
15868             windows->magnify.mapped=MagickFalse;
15869             break;
15870           }
15871         if (event.xunmap.window == windows->pan.id)
15872           {
15873             windows->pan.mapped=MagickFalse;
15874             break;
15875           }
15876         if (event.xunmap.window == windows->info.id)
15877           {
15878             windows->info.mapped=MagickFalse;
15879             break;
15880           }
15881         if (event.xunmap.window == windows->icon.id)
15882           {
15883             if (map_info->colormap == icon_map->colormap)
15884               XConfigureImageColormap(display,resource_info,windows,
15885                 display_image);
15886             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15887               icon_pixel);
15888             windows->icon.mapped=MagickFalse;
15889             break;
15890           }
15891         if (event.xunmap.window == windows->command.id)
15892           {
15893             windows->command.mapped=MagickFalse;
15894             break;
15895           }
15896         if (event.xunmap.window == windows->popup.id)
15897           {
15898             if (windows->backdrop.id != (Window) NULL)
15899               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15900                 CurrentTime);
15901             windows->popup.mapped=MagickFalse;
15902             break;
15903           }
15904         if (event.xunmap.window == windows->widget.id)
15905           {
15906             if (windows->backdrop.id != (Window) NULL)
15907               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15908                 CurrentTime);
15909             windows->widget.mapped=MagickFalse;
15910             break;
15911           }
15912         break;
15913       }
15914       default:
15915       {
15916         if (display_image->debug != MagickFalse)
15917           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15918             event.type);
15919         break;
15920       }
15921     }
15922   } while (!(*state & ExitState));
15923   if ((*state & ExitState) == 0)
15924     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15925       &display_image);
15926   else
15927     if (resource_info->confirm_edit != MagickFalse)
15928       {
15929         /*
15930           Query user if image has changed.
15931         */
15932         if ((resource_info->immutable == MagickFalse) &&
15933             (display_image->taint != MagickFalse))
15934           {
15935             int
15936               status;
15937 
15938             status=XConfirmWidget(display,windows,"Your image changed.",
15939               "Do you want to save it");
15940             if (status == 0)
15941               *state&=(~ExitState);
15942             else
15943               if (status > 0)
15944                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15945                   &display_image);
15946           }
15947       }
15948   if ((windows->visual_info->klass == GrayScale) ||
15949       (windows->visual_info->klass == PseudoColor) ||
15950       (windows->visual_info->klass == DirectColor))
15951     {
15952       /*
15953         Withdraw pan and Magnify window.
15954       */
15955       if (windows->info.mapped != MagickFalse)
15956         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15957       if (windows->magnify.mapped != MagickFalse)
15958         (void) XWithdrawWindow(display,windows->magnify.id,
15959           windows->magnify.screen);
15960       if (windows->command.mapped != MagickFalse)
15961         (void) XWithdrawWindow(display,windows->command.id,
15962           windows->command.screen);
15963     }
15964   if (windows->pan.mapped != MagickFalse)
15965     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15966   if (resource_info->backdrop == MagickFalse)
15967     if (windows->backdrop.mapped)
15968       {
15969         (void) XWithdrawWindow(display,windows->backdrop.id,
15970           windows->backdrop.screen);
15971         (void) XDestroyWindow(display,windows->backdrop.id);
15972         windows->backdrop.id=(Window) NULL;
15973         (void) XWithdrawWindow(display,windows->image.id,
15974           windows->image.screen);
15975         (void) XDestroyWindow(display,windows->image.id);
15976         windows->image.id=(Window) NULL;
15977       }
15978   XSetCursorState(display,windows,MagickTrue);
15979   XCheckRefreshWindows(display,windows);
15980   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15981     *state&=(~ExitState);
15982   if (*state & ExitState)
15983     {
15984       /*
15985         Free Standard Colormap.
15986       */
15987       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15988       if (resource_info->map_type == (char *) NULL)
15989         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15990       /*
15991         Free X resources.
15992       */
15993       if (resource_info->copy_image != (Image *) NULL)
15994         resource_info->copy_image=DestroyImage(resource_info->copy_image);
15995       DestroyXResources();
15996     }
15997   (void) XSync(display,MagickFalse);
15998   /*
15999     Restore our progress monitor and warning handlers.
16000   */
16001   (void) SetErrorHandler(warning_handler);
16002   (void) SetWarningHandler(warning_handler);
16003   /*
16004     Change to home directory.
16005   */
16006   directory=getcwd(working_directory,MaxTextExtent);
16007   (void) directory;
16008   {
16009     int
16010       status;
16011 
16012     if (*resource_info->home_directory == '\0')
16013       (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16014     status=chdir(resource_info->home_directory);
16015     if (status == -1)
16016       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16017         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16018   }
16019   *image=display_image;
16020   return(nexus);
16021 }
16022 #else
16023 
16024 /*
16025 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16026 %                                                                             %
16027 %                                                                             %
16028 %                                                                             %
16029 +   D i s p l a y I m a g e s                                                 %
16030 %                                                                             %
16031 %                                                                             %
16032 %                                                                             %
16033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16034 %
16035 %  DisplayImages() displays an image sequence to any X window screen.  It
16036 %  returns a value other than 0 if successful.  Check the exception member
16037 %  of image to determine the reason for any failure.
16038 %
16039 %  The format of the DisplayImages method is:
16040 %
16041 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16042 %        Image *images)
16043 %
16044 %  A description of each parameter follows:
16045 %
16046 %    o image_info: the image info.
16047 %
16048 %    o image: the image.
16049 %
16050 */
DisplayImages(const ImageInfo * image_info,Image * image)16051 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16052   Image *image)
16053 {
16054   assert(image_info != (const ImageInfo *) NULL);
16055   assert(image_info->signature == MagickCoreSignature);
16056   assert(image != (Image *) NULL);
16057   assert(image->signature == MagickCoreSignature);
16058   (void) image_info;
16059   if (image->debug != MagickFalse)
16060     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16061   (void) ThrowMagickException(&image->exception,GetMagickModule(),
16062     MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16063     image->filename);
16064   return(MagickFalse);
16065 }
16066 
16067 /*
16068 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16069 %                                                                             %
16070 %                                                                             %
16071 %                                                                             %
16072 +   R e m o t e D i s p l a y C o m m a n d                                   %
16073 %                                                                             %
16074 %                                                                             %
16075 %                                                                             %
16076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16077 %
16078 %  RemoteDisplayCommand() encourages a remote display program to display the
16079 %  specified image filename.
16080 %
16081 %  The format of the RemoteDisplayCommand method is:
16082 %
16083 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16084 %        const char *window,const char *filename,ExceptionInfo *exception)
16085 %
16086 %  A description of each parameter follows:
16087 %
16088 %    o image_info: the image info.
16089 %
16090 %    o window: Specifies the name or id of an X window.
16091 %
16092 %    o filename: the name of the image filename to display.
16093 %
16094 %    o exception: return any errors or warnings in this structure.
16095 %
16096 */
RemoteDisplayCommand(const ImageInfo * image_info,const char * window,const char * filename,ExceptionInfo * exception)16097 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16098   const char *window,const char *filename,ExceptionInfo *exception)
16099 {
16100   assert(image_info != (const ImageInfo *) NULL);
16101   assert(image_info->signature == MagickCoreSignature);
16102   assert(filename != (char *) NULL);
16103   (void) window;
16104   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16105   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16106     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16107   return(MagickFalse);
16108 }
16109 #endif
16110