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 "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/cache-private.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/composite.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/delegate.h"
56 #include "MagickCore/display.h"
57 #include "MagickCore/display-private.h"
58 #include "MagickCore/distort.h"
59 #include "MagickCore/draw.h"
60 #include "MagickCore/effect.h"
61 #include "MagickCore/enhance.h"
62 #include "MagickCore/exception.h"
63 #include "MagickCore/exception-private.h"
64 #include "MagickCore/fx.h"
65 #include "MagickCore/geometry.h"
66 #include "MagickCore/image.h"
67 #include "MagickCore/image-private.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/memory_.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/montage.h"
75 #include "MagickCore/nt-base-private.h"
76 #include "MagickCore/option.h"
77 #include "MagickCore/paint.h"
78 #include "MagickCore/pixel.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/property.h"
81 #include "MagickCore/quantum.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/resize.h"
84 #include "MagickCore/resource_.h"
85 #include "MagickCore/shear.h"
86 #include "MagickCore/segment.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/string-private.h"
90 #include "MagickCore/timer-private.h"
91 #include "MagickCore/transform.h"
92 #include "MagickCore/transform-private.h"
93 #include "MagickCore/threshold.h"
94 #include "MagickCore/utility.h"
95 #include "MagickCore/utility-private.h"
96 #include "MagickCore/version.h"
97 #include "MagickCore/visual-effects.h"
98 #include "MagickCore/widget.h"
99 #include "MagickCore/widget-private.h"
100 #include "MagickCore/xwindow.h"
101 #include "MagickCore/xwindow-private.h"
102 
103 #if defined(MAGICKCORE_X11_DELEGATE)
104 /*
105   Define declarations.
106 */
107 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
108 
109 /*
110   Constant declarations.
111 */
112 static const unsigned char
113   HighlightBitmap[8] =
114   {
115     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
116   },
117   OpaqueBitmap[8] =
118   {
119     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
120   },
121   ShadowBitmap[8] =
122   {
123     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
124   };
125 
126 /*
127   Help widget declarations.
128 */
129 static const char
130   ImageAnnotateHelp[] =
131   {
132     "In annotate mode, the Command widget has these options:\n"
133     "\n"
134     "    Font Name\n"
135     "      fixed\n"
136     "      variable\n"
137     "      5x8\n"
138     "      6x10\n"
139     "      7x13bold\n"
140     "      8x13bold\n"
141     "      9x15bold\n"
142     "      10x20\n"
143     "      12x24\n"
144     "      Browser...\n"
145     "    Font Color\n"
146     "      black\n"
147     "      blue\n"
148     "      cyan\n"
149     "      green\n"
150     "      gray\n"
151     "      red\n"
152     "      magenta\n"
153     "      yellow\n"
154     "      white\n"
155     "      transparent\n"
156     "      Browser...\n"
157     "    Font Color\n"
158     "      black\n"
159     "      blue\n"
160     "      cyan\n"
161     "      green\n"
162     "      gray\n"
163     "      red\n"
164     "      magenta\n"
165     "      yellow\n"
166     "      white\n"
167     "      transparent\n"
168     "      Browser...\n"
169     "    Rotate Text\n"
170     "      -90\n"
171     "      -45\n"
172     "      -30\n"
173     "      0\n"
174     "      30\n"
175     "      45\n"
176     "      90\n"
177     "      180\n"
178     "      Dialog...\n"
179     "    Help\n"
180     "    Dismiss\n"
181     "\n"
182     "Choose a font name from the Font Name sub-menu.  Additional\n"
183     "font names can be specified with the font browser.  You can\n"
184     "change the menu names by setting the X resources font1\n"
185     "through font9.\n"
186     "\n"
187     "Choose a font color from the Font Color sub-menu.\n"
188     "Additional font colors can be specified with the color\n"
189     "browser.  You can change the menu colors by setting the X\n"
190     "resources pen1 through pen9.\n"
191     "\n"
192     "If you select the color browser and press Grab, you can\n"
193     "choose the font color by moving the pointer to the desired\n"
194     "color on the screen and press any button.\n"
195     "\n"
196     "If you choose to rotate the text, choose Rotate Text from the\n"
197     "menu and select an angle.  Typically you will only want to\n"
198     "rotate one line of text at a time.  Depending on the angle you\n"
199     "choose, subsequent lines may end up overwriting each other.\n"
200     "\n"
201     "Choosing a font and its color is optional.  The default font\n"
202     "is fixed and the default color is black.  However, you must\n"
203     "choose a location to begin entering text and press button 1.\n"
204     "An underscore character will appear at the location of the\n"
205     "pointer.  The cursor changes to a pencil to indicate you are\n"
206     "in text mode.  To exit immediately, press Dismiss.\n"
207     "\n"
208     "In text mode, any key presses will display the character at\n"
209     "the location of the underscore and advance the underscore\n"
210     "cursor.  Enter your text and once completed press Apply to\n"
211     "finish your image annotation.  To correct errors press BACK\n"
212     "SPACE.  To delete an entire line of text, press DELETE.  Any\n"
213     "text that exceeds the boundaries of the image window is\n"
214     "automagically continued onto the next line.\n"
215     "\n"
216     "The actual color you request for the font is saved in the\n"
217     "image.  However, the color that appears in your image window\n"
218     "may be different.  For example, on a monochrome screen the\n"
219     "text will appear black or white even if you choose the color\n"
220     "red as the font color.  However, the image saved to a file\n"
221     "with -write is written with red lettering.  To assure the\n"
222     "correct color text in the final image, any PseudoClass image\n"
223     "is promoted to DirectClass (see miff(5)).  To force a\n"
224     "PseudoClass image to remain PseudoClass, use -colors.\n"
225   },
226   ImageChopHelp[] =
227   {
228     "In chop mode, the Command widget has these options:\n"
229     "\n"
230     "    Direction\n"
231     "      horizontal\n"
232     "      vertical\n"
233     "    Help\n"
234     "    Dismiss\n"
235     "\n"
236     "If the you choose the horizontal direction (this the\n"
237     "default), the area of the image between the two horizontal\n"
238     "endpoints of the chop line is removed.  Otherwise, the area\n"
239     "of the image between the two vertical endpoints of the chop\n"
240     "line is removed.\n"
241     "\n"
242     "Select a location within the image window to begin your chop,\n"
243     "press and hold any button.  Next, move the pointer to\n"
244     "another location in the image.  As you move a line will\n"
245     "connect the initial location and the pointer.  When you\n"
246     "release the button, the area within the image to chop is\n"
247     "determined by which direction you choose from the Command\n"
248     "widget.\n"
249     "\n"
250     "To cancel the image chopping, move the pointer back to the\n"
251     "starting point of the line and release the button.\n"
252   },
253   ImageColorEditHelp[] =
254   {
255     "In color edit mode, the Command widget has these options:\n"
256     "\n"
257     "    Method\n"
258     "      point\n"
259     "      replace\n"
260     "      floodfill\n"
261     "      filltoborder\n"
262     "      reset\n"
263     "    Pixel Color\n"
264     "      black\n"
265     "      blue\n"
266     "      cyan\n"
267     "      green\n"
268     "      gray\n"
269     "      red\n"
270     "      magenta\n"
271     "      yellow\n"
272     "      white\n"
273     "      Browser...\n"
274     "    Border Color\n"
275     "      black\n"
276     "      blue\n"
277     "      cyan\n"
278     "      green\n"
279     "      gray\n"
280     "      red\n"
281     "      magenta\n"
282     "      yellow\n"
283     "      white\n"
284     "      Browser...\n"
285     "    Fuzz\n"
286     "      0%\n"
287     "      2%\n"
288     "      5%\n"
289     "      10%\n"
290     "      15%\n"
291     "      Dialog...\n"
292     "    Undo\n"
293     "    Help\n"
294     "    Dismiss\n"
295     "\n"
296     "Choose a color editing method from the Method sub-menu\n"
297     "of the Command widget.  The point method recolors any pixel\n"
298     "selected with the pointer until the button is released.  The\n"
299     "replace method recolors any pixel that matches the color of\n"
300     "the pixel you select with a button press.  Floodfill recolors\n"
301     "any pixel that matches the color of the pixel you select with\n"
302     "a button press and is a neighbor.  Whereas filltoborder recolors\n"
303     "any neighbor pixel that is not the border color.  Finally reset\n"
304     "changes the entire image to the designated color.\n"
305     "\n"
306     "Next, choose a pixel color from the Pixel Color sub-menu.\n"
307     "Additional pixel colors can be specified with the color\n"
308     "browser.  You can change the menu colors by setting the X\n"
309     "resources pen1 through pen9.\n"
310     "\n"
311     "Now press button 1 to select a pixel within the image window\n"
312     "to change its color.  Additional pixels may be recolored as\n"
313     "prescribed by the method you choose.\n"
314     "\n"
315     "If the Magnify widget is mapped, it can be helpful in positioning\n"
316     "your pointer within the image (refer to button 2).\n"
317     "\n"
318     "The actual color you request for the pixels is saved in the\n"
319     "image.  However, the color that appears in your image window\n"
320     "may be different.  For example, on a monochrome screen the\n"
321     "pixel will appear black or white even if you choose the\n"
322     "color red as the pixel color.  However, the image saved to a\n"
323     "file with -write is written with red pixels.  To assure the\n"
324     "correct color text in the final image, any PseudoClass image\n"
325     "is promoted to DirectClass (see miff(5)).  To force a\n"
326     "PseudoClass image to remain PseudoClass, use -colors.\n"
327   },
328   ImageCompositeHelp[] =
329   {
330     "First a widget window is displayed requesting you to enter an\n"
331     "image name. Press Composite, Grab or type a file name.\n"
332     "Press Cancel if you choose not to create a composite image.\n"
333     "When you choose Grab, move the pointer to the desired window\n"
334     "and press any button.\n"
335     "\n"
336     "If the Composite image does not have any matte information,\n"
337     "you are informed and the file browser is displayed again.\n"
338     "Enter the name of a mask image.  The image is typically\n"
339     "grayscale and the same size as the composite image.  If the\n"
340     "image is not grayscale, it is converted to grayscale and the\n"
341     "resulting intensities are used as matte information.\n"
342     "\n"
343     "A small window appears showing the location of the cursor in\n"
344     "the image window. You are now in composite mode.  To exit\n"
345     "immediately, press Dismiss.  In composite mode, the Command\n"
346     "widget has these options:\n"
347     "\n"
348     "    Operators\n"
349     "      Over\n"
350     "      In\n"
351     "      Out\n"
352     "      Atop\n"
353     "      Xor\n"
354     "      Plus\n"
355     "      Minus\n"
356     "      Add\n"
357     "      Subtract\n"
358     "      Difference\n"
359     "      Multiply\n"
360     "      Bumpmap\n"
361     "      Copy\n"
362     "      CopyRed\n"
363     "      CopyGreen\n"
364     "      CopyBlue\n"
365     "      CopyOpacity\n"
366     "      Clear\n"
367     "    Dissolve\n"
368     "    Displace\n"
369     "    Help\n"
370     "    Dismiss\n"
371     "\n"
372     "Choose a composite operation from the Operators sub-menu of\n"
373     "the Command widget.  How each operator behaves is described\n"
374     "below.  Image window is the image currently displayed on\n"
375     "your X server and image is the image obtained with the File\n"
376     "Browser widget.\n"
377     "\n"
378     "Over     The result is the union of the two image shapes,\n"
379     "         with image obscuring image window in the region of\n"
380     "         overlap.\n"
381     "\n"
382     "In       The result is simply image cut by the shape of\n"
383     "         image window.  None of the image data of image\n"
384     "         window is in the result.\n"
385     "\n"
386     "Out      The resulting image is image with the shape of\n"
387     "         image window cut out.\n"
388     "\n"
389     "Atop     The result is the same shape as image image window,\n"
390     "         with image obscuring image window where the image\n"
391     "         shapes overlap.  Note this differs from over\n"
392     "         because the portion of image outside image window's\n"
393     "         shape does not appear in the result.\n"
394     "\n"
395     "Xor      The result is the image data from both image and\n"
396     "         image window that is outside the overlap region.\n"
397     "         The overlap region is blank.\n"
398     "\n"
399     "Plus     The result is just the sum of the image data.\n"
400     "         Output values are cropped to QuantumRange (no overflow).\n"
401     "\n"
402     "Minus    The result of image - image window, with underflow\n"
403     "         cropped to zero.\n"
404     "\n"
405     "Add      The result of image + image window, with overflow\n"
406     "         wrapping around (mod 256).\n"
407     "\n"
408     "Subtract The result of image - image window, with underflow\n"
409     "         wrapping around (mod 256).  The add and subtract\n"
410     "         operators can be used to perform reversible\n"
411     "         transformations.\n"
412     "\n"
413     "Difference\n"
414     "         The result of abs(image - image window).  This\n"
415     "         useful for comparing two very similar images.\n"
416     "\n"
417     "Multiply\n"
418     "         The result of image * image window.  This\n"
419     "         useful for the creation of drop-shadows.\n"
420     "\n"
421     "Bumpmap  The result of surface normals from image * image\n"
422     "         window.\n"
423     "\n"
424     "Copy     The resulting image is image window replaced with\n"
425     "         image.  Here the matte information is ignored.\n"
426     "\n"
427     "CopyRed  The red layer of the image window is replace with\n"
428     "         the red layer of the image.  The other layers are\n"
429     "         untouched.\n"
430     "\n"
431     "CopyGreen\n"
432     "         The green layer of the image window is replace with\n"
433     "         the green layer of the image.  The other layers are\n"
434     "         untouched.\n"
435     "\n"
436     "CopyBlue The blue layer of the image window is replace with\n"
437     "         the blue layer of the image.  The other layers are\n"
438     "         untouched.\n"
439     "\n"
440     "CopyOpacity\n"
441     "         The matte layer of the image window is replace with\n"
442     "         the matte layer of the image.  The other layers are\n"
443     "         untouched.\n"
444     "\n"
445     "The image compositor requires a matte, or alpha channel in\n"
446     "the image for some operations.  This extra channel usually\n"
447     "defines a mask which represents a sort of a cookie-cutter\n"
448     "for the image.  This the case when matte is opaque (full\n"
449     "coverage) for pixels inside the shape, zero outside, and\n"
450     "between 0 and QuantumRange on the boundary.  If image does not\n"
451     "have a matte channel, it is initialized with 0 for any pixel\n"
452     "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
453     "\n"
454     "If you choose Dissolve, the composite operator becomes Over.  The\n"
455     "image matte channel percent transparency is initialized to factor.\n"
456     "The image window is initialized to (100-factor). Where factor is the\n"
457     "value you specify in the Dialog widget.\n"
458     "\n"
459     "Displace shifts the image pixels as defined by a displacement\n"
460     "map.  With this option, image is used as a displacement map.\n"
461     "Black, within the displacement map, is a maximum positive\n"
462     "displacement.  White is a maximum negative displacement and\n"
463     "middle gray is neutral.  The displacement is scaled to determine\n"
464     "the pixel shift.  By default, the displacement applies in both the\n"
465     "horizontal and vertical directions.  However, if you specify a mask,\n"
466     "image is the horizontal X displacement and mask the vertical Y\n"
467     "displacement.\n"
468     "\n"
469     "Note that matte information for image window is not retained\n"
470     "for colormapped X server visuals (e.g. StaticColor,\n"
471     "StaticColor, GrayScale, PseudoColor).  Correct compositing\n"
472     "behavior may require a TrueColor or DirectColor visual or a\n"
473     "Standard Colormap.\n"
474     "\n"
475     "Choosing a composite operator is optional.  The default\n"
476     "operator is replace.  However, you must choose a location to\n"
477     "composite your image and press button 1.  Press and hold the\n"
478     "button before releasing and an outline of the image will\n"
479     "appear to help you identify your location.\n"
480     "\n"
481     "The actual colors of the composite image is saved.  However,\n"
482     "the color that appears in image window may be different.\n"
483     "For example, on a monochrome screen image window will appear\n"
484     "black or white even though your composited image may have\n"
485     "many colors.  If the image is saved to a file it is written\n"
486     "with the correct colors.  To assure the correct colors are\n"
487     "saved in the final image, any PseudoClass image is promoted\n"
488     "to DirectClass (see miff(5)).  To force a PseudoClass image\n"
489     "to remain PseudoClass, use -colors.\n"
490   },
491   ImageCutHelp[] =
492   {
493     "In cut mode, the Command widget has these options:\n"
494     "\n"
495     "    Help\n"
496     "    Dismiss\n"
497     "\n"
498     "To define a cut region, press button 1 and drag.  The\n"
499     "cut region is defined by a highlighted rectangle that\n"
500     "expands or contracts as it follows the pointer.  Once you\n"
501     "are satisfied with the cut region, release the button.\n"
502     "You are now in rectify mode.  In rectify mode, the Command\n"
503     "widget has these options:\n"
504     "\n"
505     "    Cut\n"
506     "    Help\n"
507     "    Dismiss\n"
508     "\n"
509     "You can make adjustments by moving the pointer to one of the\n"
510     "cut rectangle corners, pressing a button, and dragging.\n"
511     "Finally, press Cut to commit your copy region.  To\n"
512     "exit without cutting the image, press Dismiss.\n"
513   },
514   ImageCopyHelp[] =
515   {
516     "In copy mode, the Command widget has these options:\n"
517     "\n"
518     "    Help\n"
519     "    Dismiss\n"
520     "\n"
521     "To define a copy region, press button 1 and drag.  The\n"
522     "copy region is defined by a highlighted rectangle that\n"
523     "expands or contracts as it follows the pointer.  Once you\n"
524     "are satisfied with the copy region, release the button.\n"
525     "You are now in rectify mode.  In rectify mode, the Command\n"
526     "widget has these options:\n"
527     "\n"
528     "    Copy\n"
529     "    Help\n"
530     "    Dismiss\n"
531     "\n"
532     "You can make adjustments by moving the pointer to one of the\n"
533     "copy rectangle corners, pressing a button, and dragging.\n"
534     "Finally, press Copy to commit your copy region.  To\n"
535     "exit without copying the image, press Dismiss.\n"
536   },
537   ImageCropHelp[] =
538   {
539     "In crop mode, the Command widget has these options:\n"
540     "\n"
541     "    Help\n"
542     "    Dismiss\n"
543     "\n"
544     "To define a cropping region, press button 1 and drag.  The\n"
545     "cropping region is defined by a highlighted rectangle that\n"
546     "expands or contracts as it follows the pointer.  Once you\n"
547     "are satisfied with the cropping region, release the button.\n"
548     "You are now in rectify mode.  In rectify mode, the Command\n"
549     "widget has these options:\n"
550     "\n"
551     "    Crop\n"
552     "    Help\n"
553     "    Dismiss\n"
554     "\n"
555     "You can make adjustments by moving the pointer to one of the\n"
556     "cropping rectangle corners, pressing a button, and dragging.\n"
557     "Finally, press Crop to commit your cropping region.  To\n"
558     "exit without cropping the image, press Dismiss.\n"
559   },
560   ImageDrawHelp[] =
561   {
562     "The cursor changes to a crosshair to indicate you are in\n"
563     "draw mode.  To exit immediately, press Dismiss.  In draw mode,\n"
564     "the Command widget has these options:\n"
565     "\n"
566     "    Element\n"
567     "      point\n"
568     "      line\n"
569     "      rectangle\n"
570     "      fill rectangle\n"
571     "      circle\n"
572     "      fill circle\n"
573     "      ellipse\n"
574     "      fill ellipse\n"
575     "      polygon\n"
576     "      fill polygon\n"
577     "    Color\n"
578     "      black\n"
579     "      blue\n"
580     "      cyan\n"
581     "      green\n"
582     "      gray\n"
583     "      red\n"
584     "      magenta\n"
585     "      yellow\n"
586     "      white\n"
587     "      transparent\n"
588     "      Browser...\n"
589     "    Stipple\n"
590     "      Brick\n"
591     "      Diagonal\n"
592     "      Scales\n"
593     "      Vertical\n"
594     "      Wavy\n"
595     "      Translucent\n"
596     "      Opaque\n"
597     "      Open...\n"
598     "    Width\n"
599     "      1\n"
600     "      2\n"
601     "      4\n"
602     "      8\n"
603     "      16\n"
604     "      Dialog...\n"
605     "    Undo\n"
606     "    Help\n"
607     "    Dismiss\n"
608     "\n"
609     "Choose a drawing primitive from the Element sub-menu.\n"
610     "\n"
611     "Choose a color from the Color sub-menu.  Additional\n"
612     "colors can be specified with the color browser.\n"
613     "\n"
614     "If you choose the color browser and press Grab, you can\n"
615     "select the color by moving the pointer to the desired\n"
616     "color on the screen and press any button.  The transparent\n"
617     "color updates the image matte channel and is useful for\n"
618     "image compositing.\n"
619     "\n"
620     "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
621     "Additional stipples can be specified with the file browser.\n"
622     "Stipples obtained from the file browser must be on disk in the\n"
623     "X11 bitmap format.\n"
624     "\n"
625     "Choose a width, if appropriate, from the Width sub-menu.  To\n"
626     "choose a specific width select the Dialog widget.\n"
627     "\n"
628     "Choose a point in the Image window and press button 1 and\n"
629     "hold.  Next, move the pointer to another location in the\n"
630     "image.  As you move, a line connects the initial location and\n"
631     "the pointer.  When you release the button, the image is\n"
632     "updated with the primitive you just drew.  For polygons, the\n"
633     "image is updated when you press and release the button without\n"
634     "moving the pointer.\n"
635     "\n"
636     "To cancel image drawing, move the pointer back to the\n"
637     "starting point of the line and release the button.\n"
638   },
639   DisplayHelp[] =
640   {
641     "BUTTONS\n"
642     "  The effects of each button press is described below.  Three\n"
643     "  buttons are required.  If you have a two button mouse,\n"
644     "  button 1 and 3 are returned.  Press ALT and button 3 to\n"
645     "  simulate button 2.\n"
646     "\n"
647     "  1    Press this button to map or unmap the Command widget.\n"
648     "\n"
649     "  2    Press and drag to define a region of the image to\n"
650     "       magnify.\n"
651     "\n"
652     "  3    Press and drag to choose from a select set of commands.\n"
653     "       This button behaves differently if the image being\n"
654     "       displayed is a visual image directory.  Here, choose a\n"
655     "       particular tile of the directory and press this button and\n"
656     "       drag to select a command from a pop-up menu.  Choose from\n"
657     "       these menu items:\n"
658     "\n"
659     "           Open\n"
660     "           Next\n"
661     "           Former\n"
662     "           Delete\n"
663     "           Update\n"
664     "\n"
665     "       If you choose Open, the image represented by the tile is\n"
666     "       displayed.  To return to the visual image directory, choose\n"
667     "       Next from the Command widget.  Next and Former moves to the\n"
668     "       next or former image respectively.  Choose Delete to delete\n"
669     "       a particular image tile.  Finally, choose Update to\n"
670     "       synchronize all the image tiles with their respective\n"
671     "       images.\n"
672     "\n"
673     "COMMAND WIDGET\n"
674     "  The Command widget lists a number of sub-menus and commands.\n"
675     "  They are\n"
676     "\n"
677     "      File\n"
678     "        Open...\n"
679     "        Next\n"
680     "        Former\n"
681     "        Select...\n"
682     "        Save...\n"
683     "        Print...\n"
684     "        Delete...\n"
685     "        New...\n"
686     "        Visual Directory...\n"
687     "        Quit\n"
688     "      Edit\n"
689     "        Undo\n"
690     "        Redo\n"
691     "        Cut\n"
692     "        Copy\n"
693     "        Paste\n"
694     "      View\n"
695     "        Half Size\n"
696     "        Original Size\n"
697     "        Double Size\n"
698     "        Resize...\n"
699     "        Apply\n"
700     "        Refresh\n"
701     "        Restore\n"
702     "      Transform\n"
703     "        Crop\n"
704     "        Chop\n"
705     "        Flop\n"
706     "        Flip\n"
707     "        Rotate Right\n"
708     "        Rotate Left\n"
709     "        Rotate...\n"
710     "        Shear...\n"
711     "        Roll...\n"
712     "        Trim Edges\n"
713     "      Enhance\n"
714     "        Brightness...\n"
715     "        Saturation...\n"
716     "        Hue...\n"
717     "        Gamma...\n"
718     "        Sharpen...\n"
719     "        Dull\n"
720     "        Contrast Stretch...\n"
721     "        Sigmoidal Contrast...\n"
722     "        Normalize\n"
723     "        Equalize\n"
724     "        Negate\n"
725     "        Grayscale\n"
726     "        Map...\n"
727     "        Quantize...\n"
728     "      Effects\n"
729     "        Despeckle\n"
730     "        Emboss\n"
731     "        Reduce Noise\n"
732     "        Add Noise\n"
733     "        Sharpen...\n"
734     "        Blur...\n"
735     "        Threshold...\n"
736     "        Edge Detect...\n"
737     "        Spread...\n"
738     "        Shade...\n"
739     "        Painting...\n"
740     "        Segment...\n"
741     "      F/X\n"
742     "        Solarize...\n"
743     "        Sepia Tone...\n"
744     "        Swirl...\n"
745     "        Implode...\n"
746     "        Vignette...\n"
747     "        Wave...\n"
748     "        Oil Painting...\n"
749     "        Charcoal Drawing...\n"
750     "      Image Edit\n"
751     "        Annotate...\n"
752     "        Draw...\n"
753     "        Color...\n"
754     "        Matte...\n"
755     "        Composite...\n"
756     "        Add Border...\n"
757     "        Add Frame...\n"
758     "        Comment...\n"
759     "        Launch...\n"
760     "        Region of Interest...\n"
761     "      Miscellany\n"
762     "        Image Info\n"
763     "        Zoom Image\n"
764     "        Show Preview...\n"
765     "        Show Histogram\n"
766     "        Show Matte\n"
767     "        Background...\n"
768     "        Slide Show\n"
769     "        Preferences...\n"
770     "      Help\n"
771     "        Overview\n"
772     "        Browse Documentation\n"
773     "        About Display\n"
774     "\n"
775     "  Menu items with a indented triangle have a sub-menu.  They\n"
776     "  are represented above as the indented items.  To access a\n"
777     "  sub-menu item, move the pointer to the appropriate menu and\n"
778     "  press a button and drag.  When you find the desired sub-menu\n"
779     "  item, release the button and the command is executed.  Move\n"
780     "  the pointer away from the sub-menu if you decide not to\n"
781     "  execute a particular command.\n"
782     "\n"
783     "KEYBOARD ACCELERATORS\n"
784     "  Accelerators are one or two key presses that effect a\n"
785     "  particular command.  The keyboard accelerators that\n"
786     "  display(1) understands is:\n"
787     "\n"
788     "  Ctl+O     Press to open an image from a file.\n"
789     "\n"
790     "  space     Press to display the next image.\n"
791     "\n"
792     "            If the image is a multi-paged document such as a Postscript\n"
793     "            document, you can skip ahead several pages by preceding\n"
794     "            this command with a number.  For example to display the\n"
795     "            third page beyond the current page, press 3<space>.\n"
796     "\n"
797     "  backspace Press to display the former image.\n"
798     "\n"
799     "            If the image is a multi-paged document such as a Postscript\n"
800     "            document, you can skip behind several pages by preceding\n"
801     "            this command with a number.  For example to display the\n"
802     "            third page preceding the current page, press 3<backspace>.\n"
803     "\n"
804     "  Ctl+S     Press to write the image to a file.\n"
805     "\n"
806     "  Ctl+P     Press to print the image to a Postscript printer.\n"
807     "\n"
808     "  Ctl+D     Press to delete an image file.\n"
809     "\n"
810     "  Ctl+N     Press to create a blank canvas.\n"
811     "\n"
812     "  Ctl+Q     Press to discard all images and exit program.\n"
813     "\n"
814     "  Ctl+Z     Press to undo last image transformation.\n"
815     "\n"
816     "  Ctl+R     Press to redo last image transformation.\n"
817     "\n"
818     "  Ctl+X     Press to cut a region of the image.\n"
819     "\n"
820     "  Ctl+C     Press to copy a region of the image.\n"
821     "\n"
822     "  Ctl+V     Press to paste a region to the image.\n"
823     "\n"
824     "  <         Press to half the image size.\n"
825     "\n"
826     "  -         Press to return to the original image size.\n"
827     "\n"
828     "  >         Press to double the image size.\n"
829     "\n"
830     "  %         Press to resize the image to a width and height you\n"
831     "            specify.\n"
832     "\n"
833     "Cmd-A       Press to make any image transformations permanent."
834     "\n"
835     "            By default, any image size transformations are applied\n"
836     "            to the original image to create the image displayed on\n"
837     "            the X server.  However, the transformations are not\n"
838     "            permanent (i.e. the original image does not change\n"
839     "            size only the X image does).  For example, if you\n"
840     "            press > the X image will appear to double in size,\n"
841     "            but the original image will in fact remain the same size.\n"
842     "            To force the original image to double in size, press >\n"
843     "            followed by Cmd-A.\n"
844     "\n"
845     "  @         Press to refresh the image window.\n"
846     "\n"
847     "  C         Press to cut out a rectangular region of the image.\n"
848     "\n"
849     "  [         Press to chop the image.\n"
850     "\n"
851     "  H         Press to flop image in the horizontal direction.\n"
852     "\n"
853     "  V         Press to flip image in the vertical direction.\n"
854     "\n"
855     "  /         Press to rotate the image 90 degrees clockwise.\n"
856     "\n"
857     " \\         Press to rotate the image 90 degrees counter-clockwise.\n"
858     "\n"
859     "  *         Press to rotate the image the number of degrees you\n"
860     "            specify.\n"
861     "\n"
862     "  S         Press to shear the image the number of degrees you\n"
863     "            specify.\n"
864     "\n"
865     "  R         Press to roll the image.\n"
866     "\n"
867     "  T         Press to trim the image edges.\n"
868     "\n"
869     "  Shft-H    Press to vary the image hue.\n"
870     "\n"
871     "  Shft-S    Press to vary the color saturation.\n"
872     "\n"
873     "  Shft-L    Press to vary the color brightness.\n"
874     "\n"
875     "  Shft-G    Press to gamma correct the image.\n"
876     "\n"
877     "  Shft-C    Press to sharpen the image contrast.\n"
878     "\n"
879     "  Shft-Z    Press to dull the image contrast.\n"
880     "\n"
881     "  =         Press to perform histogram equalization on the image.\n"
882     "\n"
883     "  Shft-N    Press to perform histogram normalization on the image.\n"
884     "\n"
885     "  Shft-~    Press to negate the colors of the image.\n"
886     "\n"
887     "  .         Press to convert the image colors to gray.\n"
888     "\n"
889     "  Shft-#    Press to set the maximum number of unique colors in the\n"
890     "            image.\n"
891     "\n"
892     "  F2        Press to reduce the speckles in an image.\n"
893     "\n"
894     "  F3        Press to eliminate peak noise from an image.\n"
895     "\n"
896     "  F4        Press to add noise to an image.\n"
897     "\n"
898     "  F5        Press to sharpen an image.\n"
899     "\n"
900     "  F6        Press to delete an image file.\n"
901     "\n"
902     "  F7        Press to threshold the image.\n"
903     "\n"
904     "  F8        Press to detect edges within an image.\n"
905     "\n"
906     "  F9        Press to emboss an image.\n"
907     "\n"
908     "  F10       Press to displace pixels by a random amount.\n"
909     "\n"
910     "  F11       Press to negate all pixels above the threshold level.\n"
911     "\n"
912     "  F12       Press to shade the image using a distant light source.\n"
913     "\n"
914     "  F13       Press to lighten or darken image edges to create a 3-D effect.\n"
915     "\n"
916     "  F14       Press to segment the image by color.\n"
917     "\n"
918     "  Meta-S    Press to swirl image pixels about the center.\n"
919     "\n"
920     "  Meta-I    Press to implode image pixels about the center.\n"
921     "\n"
922     "  Meta-W    Press to alter an image along a sine wave.\n"
923     "\n"
924     "  Meta-P    Press to simulate an oil painting.\n"
925     "\n"
926     "  Meta-C    Press to simulate a charcoal drawing.\n"
927     "\n"
928     "  Alt-A     Press to annotate the image with text.\n"
929     "\n"
930     "  Alt-D     Press to draw on an image.\n"
931     "\n"
932     "  Alt-P     Press to edit an image pixel color.\n"
933     "\n"
934     "  Alt-M     Press to edit the image matte information.\n"
935     "\n"
936     "  Alt-V     Press to composite the image with another.\n"
937     "\n"
938     "  Alt-B     Press to add a border to the image.\n"
939     "\n"
940     "  Alt-F     Press to add an ornamental border to the image.\n"
941     "\n"
942     "  Alt-Shft-!\n"
943     "            Press to add an image comment.\n"
944     "\n"
945     "  Ctl-A     Press to apply image processing techniques to a region\n"
946     "            of interest.\n"
947     "\n"
948     "  Shft-?    Press to display information about the image.\n"
949     "\n"
950     "  Shft-+    Press to map the zoom image window.\n"
951     "\n"
952     "  Shft-P    Press to preview an image enhancement, effect, or f/x.\n"
953     "\n"
954     "  F1        Press to display helpful information about display(1).\n"
955     "\n"
956     "  Find      Press to browse documentation about ImageMagick.\n"
957     "\n"
958     "  1-9       Press to change the level of magnification.\n"
959     "\n"
960     "  Use the arrow keys to move the image one pixel up, down,\n"
961     "  left, or right within the magnify window.  Be sure to first\n"
962     "  map the magnify window by pressing button 2.\n"
963     "\n"
964     "  Press ALT and one of the arrow keys to trim off one pixel\n"
965     "  from any side of the image.\n"
966   },
967   ImageMatteEditHelp[] =
968   {
969     "Matte information within an image is useful for some\n"
970     "operations such as image compositing (See IMAGE\n"
971     "COMPOSITING).  This extra channel usually defines a mask\n"
972     "which represents a sort of a cookie-cutter for the image.\n"
973     "This the case when matte is opaque (full coverage) for\n"
974     "pixels inside the shape, zero outside, and between 0 and\n"
975     "QuantumRange on the boundary.\n"
976     "\n"
977     "A small window appears showing the location of the cursor in\n"
978     "the image window. You are now in matte edit mode.  To exit\n"
979     "immediately, press Dismiss.  In matte edit mode, the Command\n"
980     "widget has these options:\n"
981     "\n"
982     "    Method\n"
983     "      point\n"
984     "      replace\n"
985     "      floodfill\n"
986     "      filltoborder\n"
987     "      reset\n"
988     "    Border Color\n"
989     "      black\n"
990     "      blue\n"
991     "      cyan\n"
992     "      green\n"
993     "      gray\n"
994     "      red\n"
995     "      magenta\n"
996     "      yellow\n"
997     "      white\n"
998     "      Browser...\n"
999     "    Fuzz\n"
1000     "      0%\n"
1001     "      2%\n"
1002     "      5%\n"
1003     "      10%\n"
1004     "      15%\n"
1005     "      Dialog...\n"
1006     "    Matte\n"
1007     "      Opaque\n"
1008     "      Transparent\n"
1009     "      Dialog...\n"
1010     "    Undo\n"
1011     "    Help\n"
1012     "    Dismiss\n"
1013     "\n"
1014     "Choose a matte editing method from the Method sub-menu of\n"
1015     "the Command widget.  The point method changes the matte value\n"
1016     "of any pixel selected with the pointer until the button is\n"
1017     "is released.  The replace method changes the matte value of\n"
1018     "any pixel that matches the color of the pixel you select with\n"
1019     "a button press.  Floodfill changes the matte value of any pixel\n"
1020     "that matches the color of the pixel you select with a button\n"
1021     "press and is a neighbor.  Whereas filltoborder changes the matte\n"
1022     "value any neighbor pixel that is not the border color.  Finally\n"
1023     "reset changes the entire image to the designated matte value.\n"
1024     "\n"
1025     "Choose Matte Value and pick Opaque or Transarent.  For other values\n"
1026     "select the Dialog entry.  Here a dialog appears requesting a matte\n"
1027     "value.  The value you select is assigned as the opacity value of the\n"
1028     "selected pixel or pixels.\n"
1029     "\n"
1030     "Now, press any button to select a pixel within the image\n"
1031     "window to change its matte value.\n"
1032     "\n"
1033     "If the Magnify widget is mapped, it can be helpful in positioning\n"
1034     "your pointer within the image (refer to button 2).\n"
1035     "\n"
1036     "Matte information is only valid in a DirectClass image.\n"
1037     "Therefore, any PseudoClass image is promoted to DirectClass\n"
1038     "(see miff(5)).  Note that matte information for PseudoClass\n"
1039     "is not retained for colormapped X server visuals (e.g.\n"
1040     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1041     "immediately save your image to a file (refer to Write).\n"
1042     "Correct matte editing behavior may require a TrueColor or\n"
1043     "DirectColor visual or a Standard Colormap.\n"
1044   },
1045   ImagePanHelp[] =
1046   {
1047     "When an image exceeds the width or height of the X server\n"
1048     "screen, display maps a small panning icon.  The rectangle\n"
1049     "within the panning icon shows the area that is currently\n"
1050     "displayed in the image window.  To pan about the image,\n"
1051     "press any button and drag the pointer within the panning\n"
1052     "icon.  The pan rectangle moves with the pointer and the\n"
1053     "image window is updated to reflect the location of the\n"
1054     "rectangle within the panning icon.  When you have selected\n"
1055     "the area of the image you wish to view, release the button.\n"
1056     "\n"
1057     "Use the arrow keys to pan the image one pixel up, down,\n"
1058     "left, or right within the image window.\n"
1059     "\n"
1060     "The panning icon is withdrawn if the image becomes smaller\n"
1061     "than the dimensions of the X server screen.\n"
1062   },
1063   ImagePasteHelp[] =
1064   {
1065     "A small window appears showing the location of the cursor in\n"
1066     "the image window. You are now in paste mode.  To exit\n"
1067     "immediately, press Dismiss.  In paste mode, the Command\n"
1068     "widget has these options:\n"
1069     "\n"
1070     "    Operators\n"
1071     "      over\n"
1072     "      in\n"
1073     "      out\n"
1074     "      atop\n"
1075     "      xor\n"
1076     "      plus\n"
1077     "      minus\n"
1078     "      add\n"
1079     "      subtract\n"
1080     "      difference\n"
1081     "      replace\n"
1082     "    Help\n"
1083     "    Dismiss\n"
1084     "\n"
1085     "Choose a composite operation from the Operators sub-menu of\n"
1086     "the Command widget.  How each operator behaves is described\n"
1087     "below.  Image window is the image currently displayed on\n"
1088     "your X server and image is the image obtained with the File\n"
1089     "Browser widget.\n"
1090     "\n"
1091     "Over     The result is the union of the two image shapes,\n"
1092     "         with image obscuring image window in the region of\n"
1093     "         overlap.\n"
1094     "\n"
1095     "In       The result is simply image cut by the shape of\n"
1096     "         image window.  None of the image data of image\n"
1097     "         window is in the result.\n"
1098     "\n"
1099     "Out      The resulting image is image with the shape of\n"
1100     "         image window cut out.\n"
1101     "\n"
1102     "Atop     The result is the same shape as image image window,\n"
1103     "         with image obscuring image window where the image\n"
1104     "         shapes overlap.  Note this differs from over\n"
1105     "         because the portion of image outside image window's\n"
1106     "         shape does not appear in the result.\n"
1107     "\n"
1108     "Xor      The result is the image data from both image and\n"
1109     "         image window that is outside the overlap region.\n"
1110     "         The overlap region is blank.\n"
1111     "\n"
1112     "Plus     The result is just the sum of the image data.\n"
1113     "         Output values are cropped to QuantumRange (no overflow).\n"
1114     "         This operation is independent of the matte\n"
1115     "         channels.\n"
1116     "\n"
1117     "Minus    The result of image - image window, with underflow\n"
1118     "         cropped to zero.\n"
1119     "\n"
1120     "Add      The result of image + image window, with overflow\n"
1121     "         wrapping around (mod 256).\n"
1122     "\n"
1123     "Subtract The result of image - image window, with underflow\n"
1124     "         wrapping around (mod 256).  The add and subtract\n"
1125     "         operators can be used to perform reversible\n"
1126     "         transformations.\n"
1127     "\n"
1128     "Difference\n"
1129     "         The result of abs(image - image window).  This\n"
1130     "         useful for comparing two very similar images.\n"
1131     "\n"
1132     "Copy     The resulting image is image window replaced with\n"
1133     "         image.  Here the matte information is ignored.\n"
1134     "\n"
1135     "CopyRed  The red layer of the image window is replace with\n"
1136     "         the red layer of the image.  The other layers are\n"
1137     "         untouched.\n"
1138     "\n"
1139     "CopyGreen\n"
1140     "         The green layer of the image window is replace with\n"
1141     "         the green layer of the image.  The other layers are\n"
1142     "         untouched.\n"
1143     "\n"
1144     "CopyBlue The blue layer of the image window is replace with\n"
1145     "         the blue layer of the image.  The other layers are\n"
1146     "         untouched.\n"
1147     "\n"
1148     "CopyOpacity\n"
1149     "         The matte layer of the image window is replace with\n"
1150     "         the matte layer of the image.  The other layers are\n"
1151     "         untouched.\n"
1152     "\n"
1153     "The image compositor requires a matte, or alpha channel in\n"
1154     "the image for some operations.  This extra channel usually\n"
1155     "defines a mask which represents a sort of a cookie-cutter\n"
1156     "for the image.  This the case when matte is opaque (full\n"
1157     "coverage) for pixels inside the shape, zero outside, and\n"
1158     "between 0 and QuantumRange on the boundary.  If image does not\n"
1159     "have a matte channel, it is initialized with 0 for any pixel\n"
1160     "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1161     "\n"
1162     "Note that matte information for image window is not retained\n"
1163     "for colormapped X server visuals (e.g. StaticColor,\n"
1164     "StaticColor, GrayScale, PseudoColor).  Correct compositing\n"
1165     "behavior may require a TrueColor or DirectColor visual or a\n"
1166     "Standard Colormap.\n"
1167     "\n"
1168     "Choosing a composite operator is optional.  The default\n"
1169     "operator is replace.  However, you must choose a location to\n"
1170     "paste your image and press button 1.  Press and hold the\n"
1171     "button before releasing and an outline of the image will\n"
1172     "appear to help you identify your location.\n"
1173     "\n"
1174     "The actual colors of the pasted image is saved.  However,\n"
1175     "the color that appears in image window may be different.\n"
1176     "For example, on a monochrome screen image window will appear\n"
1177     "black or white even though your pasted image may have\n"
1178     "many colors.  If the image is saved to a file it is written\n"
1179     "with the correct colors.  To assure the correct colors are\n"
1180     "saved in the final image, any PseudoClass image is promoted\n"
1181     "to DirectClass (see miff(5)).  To force a PseudoClass image\n"
1182     "to remain PseudoClass, use -colors.\n"
1183   },
1184   ImageROIHelp[] =
1185   {
1186     "In region of interest mode, the Command widget has these\n"
1187     "options:\n"
1188     "\n"
1189     "    Help\n"
1190     "    Dismiss\n"
1191     "\n"
1192     "To define a region of interest, press button 1 and drag.\n"
1193     "The region of interest is defined by a highlighted rectangle\n"
1194     "that expands or contracts as it follows the pointer.  Once\n"
1195     "you are satisfied with the region of interest, release the\n"
1196     "button.  You are now in apply mode.  In apply mode the\n"
1197     "Command widget has these options:\n"
1198     "\n"
1199     "      File\n"
1200     "        Save...\n"
1201     "        Print...\n"
1202     "      Edit\n"
1203     "        Undo\n"
1204     "        Redo\n"
1205     "      Transform\n"
1206     "        Flop\n"
1207     "        Flip\n"
1208     "        Rotate Right\n"
1209     "        Rotate Left\n"
1210     "      Enhance\n"
1211     "        Hue...\n"
1212     "        Saturation...\n"
1213     "        Brightness...\n"
1214     "        Gamma...\n"
1215     "        Spiff\n"
1216     "        Dull\n"
1217     "        Contrast Stretch\n"
1218     "        Sigmoidal Contrast...\n"
1219     "        Normalize\n"
1220     "        Equalize\n"
1221     "        Negate\n"
1222     "        Grayscale\n"
1223     "        Map...\n"
1224     "        Quantize...\n"
1225     "      Effects\n"
1226     "        Despeckle\n"
1227     "        Emboss\n"
1228     "        Reduce Noise\n"
1229     "        Sharpen...\n"
1230     "        Blur...\n"
1231     "        Threshold...\n"
1232     "        Edge Detect...\n"
1233     "        Spread...\n"
1234     "        Shade...\n"
1235     "        Raise...\n"
1236     "        Segment...\n"
1237     "      F/X\n"
1238     "        Solarize...\n"
1239     "        Sepia Tone...\n"
1240     "        Swirl...\n"
1241     "        Implode...\n"
1242     "        Vignette...\n"
1243     "        Wave...\n"
1244     "        Oil Painting...\n"
1245     "        Charcoal Drawing...\n"
1246     "      Miscellany\n"
1247     "        Image Info\n"
1248     "        Zoom Image\n"
1249     "        Show Preview...\n"
1250     "        Show Histogram\n"
1251     "        Show Matte\n"
1252     "      Help\n"
1253     "      Dismiss\n"
1254     "\n"
1255     "You can make adjustments to the region of interest by moving\n"
1256     "the pointer to one of the rectangle corners, pressing a\n"
1257     "button, and dragging.  Finally, choose an image processing\n"
1258     "technique from the Command widget.  You can choose more than\n"
1259     "one image processing technique to apply to an area.\n"
1260     "Alternatively, you can move the region of interest before\n"
1261     "applying another image processing technique.  To exit, press\n"
1262     "Dismiss.\n"
1263   },
1264   ImageRotateHelp[] =
1265   {
1266     "In rotate mode, the Command widget has these options:\n"
1267     "\n"
1268     "    Pixel Color\n"
1269     "      black\n"
1270     "      blue\n"
1271     "      cyan\n"
1272     "      green\n"
1273     "      gray\n"
1274     "      red\n"
1275     "      magenta\n"
1276     "      yellow\n"
1277     "      white\n"
1278     "      Browser...\n"
1279     "    Direction\n"
1280     "      horizontal\n"
1281     "      vertical\n"
1282     "    Help\n"
1283     "    Dismiss\n"
1284     "\n"
1285     "Choose a background color from the Pixel Color sub-menu.\n"
1286     "Additional background colors can be specified with the color\n"
1287     "browser.  You can change the menu colors by setting the X\n"
1288     "resources pen1 through pen9.\n"
1289     "\n"
1290     "If you choose the color browser and press Grab, you can\n"
1291     "select the background color by moving the pointer to the\n"
1292     "desired color on the screen and press any button.\n"
1293     "\n"
1294     "Choose a point in the image window and press this button and\n"
1295     "hold.  Next, move the pointer to another location in the\n"
1296     "image.  As you move a line connects the initial location and\n"
1297     "the pointer.  When you release the button, the degree of\n"
1298     "image rotation is determined by the slope of the line you\n"
1299     "just drew.  The slope is relative to the direction you\n"
1300     "choose from the Direction sub-menu of the Command widget.\n"
1301     "\n"
1302     "To cancel the image rotation, move the pointer back to the\n"
1303     "starting point of the line and release the button.\n"
1304   };
1305 
1306 /*
1307   Enumeration declarations.
1308 */
1309 typedef enum
1310 {
1311   CopyMode,
1312   CropMode,
1313   CutMode
1314 } ClipboardMode;
1315 
1316 typedef enum
1317 {
1318   OpenCommand,
1319   NextCommand,
1320   FormerCommand,
1321   SelectCommand,
1322   SaveCommand,
1323   PrintCommand,
1324   DeleteCommand,
1325   NewCommand,
1326   VisualDirectoryCommand,
1327   QuitCommand,
1328   UndoCommand,
1329   RedoCommand,
1330   CutCommand,
1331   CopyCommand,
1332   PasteCommand,
1333   HalfSizeCommand,
1334   OriginalSizeCommand,
1335   DoubleSizeCommand,
1336   ResizeCommand,
1337   ApplyCommand,
1338   RefreshCommand,
1339   RestoreCommand,
1340   CropCommand,
1341   ChopCommand,
1342   FlopCommand,
1343   FlipCommand,
1344   RotateRightCommand,
1345   RotateLeftCommand,
1346   RotateCommand,
1347   ShearCommand,
1348   RollCommand,
1349   TrimCommand,
1350   HueCommand,
1351   SaturationCommand,
1352   BrightnessCommand,
1353   GammaCommand,
1354   SpiffCommand,
1355   DullCommand,
1356   ContrastStretchCommand,
1357   SigmoidalContrastCommand,
1358   NormalizeCommand,
1359   EqualizeCommand,
1360   NegateCommand,
1361   GrayscaleCommand,
1362   MapCommand,
1363   QuantizeCommand,
1364   DespeckleCommand,
1365   EmbossCommand,
1366   ReduceNoiseCommand,
1367   AddNoiseCommand,
1368   SharpenCommand,
1369   BlurCommand,
1370   ThresholdCommand,
1371   EdgeDetectCommand,
1372   SpreadCommand,
1373   ShadeCommand,
1374   RaiseCommand,
1375   SegmentCommand,
1376   SolarizeCommand,
1377   SepiaToneCommand,
1378   SwirlCommand,
1379   ImplodeCommand,
1380   VignetteCommand,
1381   WaveCommand,
1382   OilPaintCommand,
1383   CharcoalDrawCommand,
1384   AnnotateCommand,
1385   DrawCommand,
1386   ColorCommand,
1387   MatteCommand,
1388   CompositeCommand,
1389   AddBorderCommand,
1390   AddFrameCommand,
1391   CommentCommand,
1392   LaunchCommand,
1393   RegionofInterestCommand,
1394   ROIHelpCommand,
1395   ROIDismissCommand,
1396   InfoCommand,
1397   ZoomCommand,
1398   ShowPreviewCommand,
1399   ShowHistogramCommand,
1400   ShowMatteCommand,
1401   BackgroundCommand,
1402   SlideShowCommand,
1403   PreferencesCommand,
1404   HelpCommand,
1405   BrowseDocumentationCommand,
1406   VersionCommand,
1407   SaveToUndoBufferCommand,
1408   FreeBuffersCommand,
1409   NullCommand
1410 } CommandType;
1411 
1412 typedef enum
1413 {
1414   AnnotateNameCommand,
1415   AnnotateFontColorCommand,
1416   AnnotateBackgroundColorCommand,
1417   AnnotateRotateCommand,
1418   AnnotateHelpCommand,
1419   AnnotateDismissCommand,
1420   TextHelpCommand,
1421   TextApplyCommand,
1422   ChopDirectionCommand,
1423   ChopHelpCommand,
1424   ChopDismissCommand,
1425   HorizontalChopCommand,
1426   VerticalChopCommand,
1427   ColorEditMethodCommand,
1428   ColorEditColorCommand,
1429   ColorEditBorderCommand,
1430   ColorEditFuzzCommand,
1431   ColorEditUndoCommand,
1432   ColorEditHelpCommand,
1433   ColorEditDismissCommand,
1434   CompositeOperatorsCommand,
1435   CompositeDissolveCommand,
1436   CompositeDisplaceCommand,
1437   CompositeHelpCommand,
1438   CompositeDismissCommand,
1439   CropHelpCommand,
1440   CropDismissCommand,
1441   RectifyCopyCommand,
1442   RectifyHelpCommand,
1443   RectifyDismissCommand,
1444   DrawElementCommand,
1445   DrawColorCommand,
1446   DrawStippleCommand,
1447   DrawWidthCommand,
1448   DrawUndoCommand,
1449   DrawHelpCommand,
1450   DrawDismissCommand,
1451   MatteEditMethod,
1452   MatteEditBorderCommand,
1453   MatteEditFuzzCommand,
1454   MatteEditValueCommand,
1455   MatteEditUndoCommand,
1456   MatteEditHelpCommand,
1457   MatteEditDismissCommand,
1458   PasteOperatorsCommand,
1459   PasteHelpCommand,
1460   PasteDismissCommand,
1461   RotateColorCommand,
1462   RotateDirectionCommand,
1463   RotateCropCommand,
1464   RotateSharpenCommand,
1465   RotateHelpCommand,
1466   RotateDismissCommand,
1467   HorizontalRotateCommand,
1468   VerticalRotateCommand,
1469   TileLoadCommand,
1470   TileNextCommand,
1471   TileFormerCommand,
1472   TileDeleteCommand,
1473   TileUpdateCommand
1474 } ModeType;
1475 
1476 /*
1477   Stipples.
1478 */
1479 #define BricksWidth  20
1480 #define BricksHeight  20
1481 #define DiagonalWidth  16
1482 #define DiagonalHeight  16
1483 #define HighlightWidth  8
1484 #define HighlightHeight  8
1485 #define OpaqueWidth  8
1486 #define OpaqueHeight  8
1487 #define ScalesWidth  16
1488 #define ScalesHeight  16
1489 #define ShadowWidth  8
1490 #define ShadowHeight  8
1491 #define VerticalWidth  16
1492 #define VerticalHeight  16
1493 #define WavyWidth  16
1494 #define WavyHeight  16
1495 
1496 /*
1497   Constant declaration.
1498 */
1499 static const int
1500   RoiDelta = 8;
1501 
1502 static const unsigned char
1503   BricksBitmap[] =
1504   {
1505     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1506     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1507     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1508     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1509     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1510   },
1511   DiagonalBitmap[] =
1512   {
1513     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1514     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1515     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1516   },
1517   ScalesBitmap[] =
1518   {
1519     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1520     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1521     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1522   },
1523   VerticalBitmap[] =
1524   {
1525     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1526     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1527     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1528   },
1529   WavyBitmap[] =
1530   {
1531     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1532     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1533     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1534   };
1535 
1536 /*
1537   Function prototypes.
1538 */
1539 static CommandType
1540   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1541     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1542 
1543 static Image
1544   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1545     Image **,ExceptionInfo *),
1546   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1547   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1548     ExceptionInfo *),
1549   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1550     ExceptionInfo *);
1551 
1552 static MagickBooleanType
1553   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1554     ExceptionInfo *),
1555   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1556     ExceptionInfo *),
1557   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1558     ExceptionInfo *),
1559   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1560     ExceptionInfo *),
1561   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1562     ExceptionInfo *),
1563   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1564     ExceptionInfo *),
1565   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1566   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1567     ExceptionInfo *),
1568   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1569     ExceptionInfo *),
1570   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1571   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1572   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1573     ExceptionInfo *),
1574   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1575   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1576   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1577 
1578 static void
1579   XDrawPanRectangle(Display *,XWindows *),
1580   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1581     ExceptionInfo *),
1582   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1583   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1584   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1585   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1586     const KeySym,ExceptionInfo *),
1587   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1588   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1589   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1590 
1591 /*
1592 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1593 %                                                                             %
1594 %                                                                             %
1595 %                                                                             %
1596 %   D i s p l a y I m a g e s                                                 %
1597 %                                                                             %
1598 %                                                                             %
1599 %                                                                             %
1600 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1601 %
1602 %  DisplayImages() displays an image sequence to any X window screen.  It
1603 %  returns a value other than 0 if successful.  Check the exception member
1604 %  of image to determine the reason for any failure.
1605 %
1606 %  The format of the DisplayImages method is:
1607 %
1608 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1609 %        Image *images,ExceptionInfo *exception)
1610 %
1611 %  A description of each parameter follows:
1612 %
1613 %    o image_info: the image info.
1614 %
1615 %    o image: the image.
1616 %
1617 %    o exception: return any errors or warnings in this structure.
1618 %
1619 */
DisplayImages(const ImageInfo * image_info,Image * images,ExceptionInfo * exception)1620 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1621   Image *images,ExceptionInfo *exception)
1622 {
1623   char
1624     *argv[1];
1625 
1626   Display
1627     *display;
1628 
1629   Image
1630     *image;
1631 
1632   ssize_t
1633     i;
1634 
1635   size_t
1636     state;
1637 
1638   XrmDatabase
1639     resource_database;
1640 
1641   XResourceInfo
1642     resource_info;
1643 
1644   assert(image_info != (const ImageInfo *) NULL);
1645   assert(image_info->signature == MagickCoreSignature);
1646   assert(images != (Image *) NULL);
1647   assert(images->signature == MagickCoreSignature);
1648   if (images->debug != MagickFalse)
1649     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1650   display=XOpenDisplay(image_info->server_name);
1651   if (display == (Display *) NULL)
1652     {
1653       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1654         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1655       return(MagickFalse);
1656     }
1657   if (exception->severity != UndefinedException)
1658     CatchException(exception);
1659   (void) XSetErrorHandler(XError);
1660   resource_database=XGetResourceDatabase(display,GetClientName());
1661   (void) memset(&resource_info,0,sizeof(resource_info));
1662   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1663   if (image_info->page != (char *) NULL)
1664     resource_info.image_geometry=AcquireString(image_info->page);
1665   resource_info.immutable=MagickTrue;
1666   argv[0]=AcquireString(GetClientName());
1667   state=DefaultState;
1668   for (i=0; (state & ExitState) == 0; i++)
1669   {
1670     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1671       break;
1672     image=GetImageFromList(images,i % GetImageListLength(images));
1673     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1674   }
1675   (void) SetErrorHandler((ErrorHandler) NULL);
1676   (void) SetWarningHandler((WarningHandler) NULL);
1677   argv[0]=DestroyString(argv[0]);
1678   (void) XCloseDisplay(display);
1679   XDestroyResourceInfo(&resource_info);
1680   if (exception->severity != UndefinedException)
1681     return(MagickFalse);
1682   return(MagickTrue);
1683 }
1684 
1685 /*
1686 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1687 %                                                                             %
1688 %                                                                             %
1689 %                                                                             %
1690 %   R e m o t e D i s p l a y C o m m a n d                                   %
1691 %                                                                             %
1692 %                                                                             %
1693 %                                                                             %
1694 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1695 %
1696 %  RemoteDisplayCommand() encourages a remote display program to display the
1697 %  specified image filename.
1698 %
1699 %  The format of the RemoteDisplayCommand method is:
1700 %
1701 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1702 %        const char *window,const char *filename,ExceptionInfo *exception)
1703 %
1704 %  A description of each parameter follows:
1705 %
1706 %    o image_info: the image info.
1707 %
1708 %    o window: Specifies the name or id of an X window.
1709 %
1710 %    o filename: the name of the image filename to display.
1711 %
1712 %    o exception: return any errors or warnings in this structure.
1713 %
1714 */
RemoteDisplayCommand(const ImageInfo * image_info,const char * window,const char * filename,ExceptionInfo * exception)1715 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1716   const char *window,const char *filename,ExceptionInfo *exception)
1717 {
1718   Display
1719     *display;
1720 
1721   MagickStatusType
1722     status;
1723 
1724   assert(image_info != (const ImageInfo *) NULL);
1725   assert(image_info->signature == MagickCoreSignature);
1726   assert(filename != (char *) NULL);
1727   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1728   display=XOpenDisplay(image_info->server_name);
1729   if (display == (Display *) NULL)
1730     {
1731       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1732         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1733       return(MagickFalse);
1734     }
1735   (void) XSetErrorHandler(XError);
1736   status=XRemoteCommand(display,window,filename);
1737   (void) XCloseDisplay(display);
1738   return(status != 0 ? MagickTrue : MagickFalse);
1739 }
1740 
1741 /*
1742 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1743 %                                                                             %
1744 %                                                                             %
1745 %                                                                             %
1746 +   X A n n o t a t e E d i t I m a g e                                       %
1747 %                                                                             %
1748 %                                                                             %
1749 %                                                                             %
1750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1751 %
1752 %  XAnnotateEditImage() annotates the image with text.
1753 %
1754 %  The format of the XAnnotateEditImage method is:
1755 %
1756 %      MagickBooleanType XAnnotateEditImage(Display *display,
1757 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1758 %        ExceptionInfo *exception)
1759 %
1760 %  A description of each parameter follows:
1761 %
1762 %    o display: Specifies a connection to an X server;  returned from
1763 %      XOpenDisplay.
1764 %
1765 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1766 %
1767 %    o windows: Specifies a pointer to a XWindows structure.
1768 %
1769 %    o image: the image; returned from ReadImage.
1770 %
1771 */
1772 
XAnnotateEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)1773 static MagickBooleanType XAnnotateEditImage(Display *display,
1774   XResourceInfo *resource_info,XWindows *windows,Image *image,
1775   ExceptionInfo *exception)
1776 {
1777   const char
1778     *const AnnotateMenu[] =
1779     {
1780       "Font Name",
1781       "Font Color",
1782       "Box Color",
1783       "Rotate Text",
1784       "Help",
1785       "Dismiss",
1786       (char *) NULL
1787     },
1788     *const TextMenu[] =
1789     {
1790       "Help",
1791       "Apply",
1792       (char *) NULL
1793     };
1794 
1795   static const ModeType
1796     AnnotateCommands[] =
1797     {
1798       AnnotateNameCommand,
1799       AnnotateFontColorCommand,
1800       AnnotateBackgroundColorCommand,
1801       AnnotateRotateCommand,
1802       AnnotateHelpCommand,
1803       AnnotateDismissCommand
1804     },
1805     TextCommands[] =
1806     {
1807       TextHelpCommand,
1808       TextApplyCommand
1809     };
1810 
1811   static MagickBooleanType
1812     transparent_box = MagickTrue,
1813     transparent_pen = MagickFalse;
1814 
1815   static double
1816     degrees = 0.0;
1817 
1818   static unsigned int
1819     box_id = MaxNumberPens-2,
1820     font_id = 0,
1821     pen_id = 0;
1822 
1823   char
1824     command[MagickPathExtent],
1825     text[MagickPathExtent];
1826 
1827   const char
1828     *ColorMenu[MaxNumberPens+1];
1829 
1830   Cursor
1831     cursor;
1832 
1833   GC
1834     annotate_context;
1835 
1836   int
1837     id,
1838     pen_number,
1839     status,
1840     x,
1841     y;
1842 
1843   KeySym
1844     key_symbol;
1845 
1846   char
1847     *p;
1848 
1849   ssize_t
1850     i;
1851 
1852   unsigned int
1853     height,
1854     width;
1855 
1856   size_t
1857     state;
1858 
1859   XAnnotateInfo
1860     *annotate_info,
1861     *previous_info;
1862 
1863   XColor
1864     color;
1865 
1866   XFontStruct
1867     *font_info;
1868 
1869   XEvent
1870     event,
1871     text_event;
1872 
1873   /*
1874     Map Command widget.
1875   */
1876   (void) CloneString(&windows->command.name,"Annotate");
1877   windows->command.data=4;
1878   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1879   (void) XMapRaised(display,windows->command.id);
1880   XClientMessage(display,windows->image.id,windows->im_protocols,
1881     windows->im_update_widget,CurrentTime);
1882   /*
1883     Track pointer until button 1 is pressed.
1884   */
1885   XQueryPosition(display,windows->image.id,&x,&y);
1886   (void) XSelectInput(display,windows->image.id,
1887     windows->image.attributes.event_mask | PointerMotionMask);
1888   cursor=XCreateFontCursor(display,XC_left_side);
1889   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1890   state=DefaultState;
1891   do
1892   {
1893     if (windows->info.mapped != MagickFalse)
1894       {
1895         /*
1896           Display pointer position.
1897         */
1898         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1899           x+windows->image.x,y+windows->image.y);
1900         XInfoWidget(display,windows,text);
1901       }
1902     /*
1903       Wait for next event.
1904     */
1905     XScreenEvent(display,windows,&event,exception);
1906     if (event.xany.window == windows->command.id)
1907       {
1908         /*
1909           Select a command from the Command widget.
1910         */
1911         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1912         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1913         if (id < 0)
1914           continue;
1915         switch (AnnotateCommands[id])
1916         {
1917           case AnnotateNameCommand:
1918           {
1919             const char
1920               *FontMenu[MaxNumberFonts];
1921 
1922             int
1923               font_number;
1924 
1925             /*
1926               Initialize menu selections.
1927             */
1928             for (i=0; i < MaxNumberFonts; i++)
1929               FontMenu[i]=resource_info->font_name[i];
1930             FontMenu[MaxNumberFonts-2]="Browser...";
1931             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1932             /*
1933               Select a font name from the pop-up menu.
1934             */
1935             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1936               (const char **) FontMenu,command);
1937             if (font_number < 0)
1938               break;
1939             if (font_number == (MaxNumberFonts-2))
1940               {
1941                 static char
1942                   font_name[MagickPathExtent] = "fixed";
1943 
1944                 /*
1945                   Select a font name from a browser.
1946                 */
1947                 resource_info->font_name[font_number]=font_name;
1948                 XFontBrowserWidget(display,windows,"Select",font_name);
1949                 if (*font_name == '\0')
1950                   break;
1951               }
1952             /*
1953               Initialize font info.
1954             */
1955             font_info=XLoadQueryFont(display,resource_info->font_name[
1956               font_number]);
1957             if (font_info == (XFontStruct *) NULL)
1958               {
1959                 XNoticeWidget(display,windows,"Unable to load font:",
1960                   resource_info->font_name[font_number]);
1961                 break;
1962               }
1963             font_id=(unsigned int) font_number;
1964             (void) XFreeFont(display,font_info);
1965             break;
1966           }
1967           case AnnotateFontColorCommand:
1968           {
1969             /*
1970               Initialize menu selections.
1971             */
1972             for (i=0; i < (int) (MaxNumberPens-2); i++)
1973               ColorMenu[i]=resource_info->pen_colors[i];
1974             ColorMenu[MaxNumberPens-2]="transparent";
1975             ColorMenu[MaxNumberPens-1]="Browser...";
1976             ColorMenu[MaxNumberPens]=(const char *) NULL;
1977             /*
1978               Select a pen color from the pop-up menu.
1979             */
1980             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1981               (const char **) ColorMenu,command);
1982             if (pen_number < 0)
1983               break;
1984             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1985               MagickFalse;
1986             if (transparent_pen != MagickFalse)
1987               break;
1988             if (pen_number == (MaxNumberPens-1))
1989               {
1990                 static char
1991                   color_name[MagickPathExtent] = "gray";
1992 
1993                 /*
1994                   Select a pen color from a dialog.
1995                 */
1996                 resource_info->pen_colors[pen_number]=color_name;
1997                 XColorBrowserWidget(display,windows,"Select",color_name);
1998                 if (*color_name == '\0')
1999                   break;
2000               }
2001             /*
2002               Set pen color.
2003             */
2004             (void) XParseColor(display,windows->map_info->colormap,
2005               resource_info->pen_colors[pen_number],&color);
2006             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2007               (unsigned int) MaxColors,&color);
2008             windows->pixel_info->pen_colors[pen_number]=color;
2009             pen_id=(unsigned int) pen_number;
2010             break;
2011           }
2012           case AnnotateBackgroundColorCommand:
2013           {
2014             /*
2015               Initialize menu selections.
2016             */
2017             for (i=0; i < (int) (MaxNumberPens-2); i++)
2018               ColorMenu[i]=resource_info->pen_colors[i];
2019             ColorMenu[MaxNumberPens-2]="transparent";
2020             ColorMenu[MaxNumberPens-1]="Browser...";
2021             ColorMenu[MaxNumberPens]=(const char *) NULL;
2022             /*
2023               Select a pen color from the pop-up menu.
2024             */
2025             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2026               (const char **) ColorMenu,command);
2027             if (pen_number < 0)
2028               break;
2029             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2030               MagickFalse;
2031             if (transparent_box != MagickFalse)
2032               break;
2033             if (pen_number == (MaxNumberPens-1))
2034               {
2035                 static char
2036                   color_name[MagickPathExtent] = "gray";
2037 
2038                 /*
2039                   Select a pen color from a dialog.
2040                 */
2041                 resource_info->pen_colors[pen_number]=color_name;
2042                 XColorBrowserWidget(display,windows,"Select",color_name);
2043                 if (*color_name == '\0')
2044                   break;
2045               }
2046             /*
2047               Set pen color.
2048             */
2049             (void) XParseColor(display,windows->map_info->colormap,
2050               resource_info->pen_colors[pen_number],&color);
2051             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2052               (unsigned int) MaxColors,&color);
2053             windows->pixel_info->pen_colors[pen_number]=color;
2054             box_id=(unsigned int) pen_number;
2055             break;
2056           }
2057           case AnnotateRotateCommand:
2058           {
2059             int
2060               entry;
2061 
2062             const char
2063               *const RotateMenu[] =
2064               {
2065                 "-90",
2066                 "-45",
2067                 "-30",
2068                 "0",
2069                 "30",
2070                 "45",
2071                 "90",
2072                 "180",
2073                 "Dialog...",
2074                 (char *) NULL,
2075               };
2076 
2077             static char
2078               angle[MagickPathExtent] = "30.0";
2079 
2080             /*
2081               Select a command from the pop-up menu.
2082             */
2083             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2084               command);
2085             if (entry < 0)
2086               break;
2087             if (entry != 8)
2088               {
2089                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2090                 break;
2091               }
2092             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2093               angle);
2094             if (*angle == '\0')
2095               break;
2096             degrees=StringToDouble(angle,(char **) NULL);
2097             break;
2098           }
2099           case AnnotateHelpCommand:
2100           {
2101             XTextViewHelp(display,resource_info,windows,MagickFalse,
2102               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2103             break;
2104           }
2105           case AnnotateDismissCommand:
2106           {
2107             /*
2108               Prematurely exit.
2109             */
2110             state|=EscapeState;
2111             state|=ExitState;
2112             break;
2113           }
2114           default:
2115             break;
2116         }
2117         continue;
2118       }
2119     switch (event.type)
2120     {
2121       case ButtonPress:
2122       {
2123         if (event.xbutton.button != Button1)
2124           break;
2125         if (event.xbutton.window != windows->image.id)
2126           break;
2127         /*
2128           Change to text entering mode.
2129         */
2130         x=event.xbutton.x;
2131         y=event.xbutton.y;
2132         state|=ExitState;
2133         break;
2134       }
2135       case ButtonRelease:
2136         break;
2137       case Expose:
2138         break;
2139       case KeyPress:
2140       {
2141         if (event.xkey.window != windows->image.id)
2142           break;
2143         /*
2144           Respond to a user key press.
2145         */
2146         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2147           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2148         switch ((int) key_symbol)
2149         {
2150           case XK_Escape:
2151           case XK_F20:
2152           {
2153             /*
2154               Prematurely exit.
2155             */
2156             state|=EscapeState;
2157             state|=ExitState;
2158             break;
2159           }
2160           case XK_F1:
2161           case XK_Help:
2162           {
2163             XTextViewHelp(display,resource_info,windows,MagickFalse,
2164               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2165             break;
2166           }
2167           default:
2168           {
2169             (void) XBell(display,0);
2170             break;
2171           }
2172         }
2173         break;
2174       }
2175       case MotionNotify:
2176       {
2177         /*
2178           Map and unmap Info widget as cursor crosses its boundaries.
2179         */
2180         x=event.xmotion.x;
2181         y=event.xmotion.y;
2182         if (windows->info.mapped != MagickFalse)
2183           {
2184             if ((x < (int) (windows->info.x+windows->info.width)) &&
2185                 (y < (int) (windows->info.y+windows->info.height)))
2186               (void) XWithdrawWindow(display,windows->info.id,
2187                 windows->info.screen);
2188           }
2189         else
2190           if ((x > (int) (windows->info.x+windows->info.width)) ||
2191               (y > (int) (windows->info.y+windows->info.height)))
2192             (void) XMapWindow(display,windows->info.id);
2193         break;
2194       }
2195       default:
2196         break;
2197     }
2198   } while ((state & ExitState) == 0);
2199   (void) XSelectInput(display,windows->image.id,
2200     windows->image.attributes.event_mask);
2201   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2202   if ((state & EscapeState) != 0)
2203     return(MagickTrue);
2204   /*
2205     Set font info and check boundary conditions.
2206   */
2207   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2208   if (font_info == (XFontStruct *) NULL)
2209     {
2210       XNoticeWidget(display,windows,"Unable to load font:",
2211         resource_info->font_name[font_id]);
2212       font_info=windows->font_info;
2213     }
2214   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2215     x=(int) windows->image.width-font_info->max_bounds.width;
2216   if (y < (int) (font_info->ascent+font_info->descent))
2217     y=(int) font_info->ascent+font_info->descent;
2218   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2219       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2220     return(MagickFalse);
2221   /*
2222     Initialize annotate structure.
2223   */
2224   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2225   if (annotate_info == (XAnnotateInfo *) NULL)
2226     return(MagickFalse);
2227   XGetAnnotateInfo(annotate_info);
2228   annotate_info->x=x;
2229   annotate_info->y=y;
2230   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2231     annotate_info->stencil=OpaqueStencil;
2232   else
2233     if (transparent_box == MagickFalse)
2234       annotate_info->stencil=BackgroundStencil;
2235     else
2236       annotate_info->stencil=ForegroundStencil;
2237   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2238   annotate_info->degrees=degrees;
2239   annotate_info->font_info=font_info;
2240   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2241     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2242     sizeof(*annotate_info->text));
2243   if (annotate_info->text == (char *) NULL)
2244     return(MagickFalse);
2245   /*
2246     Create cursor and set graphic context.
2247   */
2248   cursor=XCreateFontCursor(display,XC_pencil);
2249   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2250   annotate_context=windows->image.annotate_context;
2251   (void) XSetFont(display,annotate_context,font_info->fid);
2252   (void) XSetBackground(display,annotate_context,
2253     windows->pixel_info->pen_colors[box_id].pixel);
2254   (void) XSetForeground(display,annotate_context,
2255     windows->pixel_info->pen_colors[pen_id].pixel);
2256   /*
2257     Begin annotating the image with text.
2258   */
2259   (void) CloneString(&windows->command.name,"Text");
2260   windows->command.data=0;
2261   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2262   state=DefaultState;
2263   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2264   text_event.xexpose.width=(int) font_info->max_bounds.width;
2265   text_event.xexpose.height=font_info->max_bounds.ascent+
2266     font_info->max_bounds.descent;
2267   p=annotate_info->text;
2268   do
2269   {
2270     /*
2271       Display text cursor.
2272     */
2273     *p='\0';
2274     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2275     /*
2276       Wait for next event.
2277     */
2278     XScreenEvent(display,windows,&event,exception);
2279     if (event.xany.window == windows->command.id)
2280       {
2281         /*
2282           Select a command from the Command widget.
2283         */
2284         (void) XSetBackground(display,annotate_context,
2285           windows->pixel_info->background_color.pixel);
2286         (void) XSetForeground(display,annotate_context,
2287           windows->pixel_info->foreground_color.pixel);
2288         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2289         (void) XSetBackground(display,annotate_context,
2290           windows->pixel_info->pen_colors[box_id].pixel);
2291         (void) XSetForeground(display,annotate_context,
2292           windows->pixel_info->pen_colors[pen_id].pixel);
2293         if (id < 0)
2294           continue;
2295         switch (TextCommands[id])
2296         {
2297           case TextHelpCommand:
2298           {
2299             XTextViewHelp(display,resource_info,windows,MagickFalse,
2300               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2301             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2302             break;
2303           }
2304           case TextApplyCommand:
2305           {
2306             /*
2307               Finished annotating.
2308             */
2309             annotate_info->width=(unsigned int) XTextWidth(font_info,
2310               annotate_info->text,(int) strlen(annotate_info->text));
2311             XRefreshWindow(display,&windows->image,&text_event);
2312             state|=ExitState;
2313             break;
2314           }
2315           default:
2316             break;
2317         }
2318         continue;
2319       }
2320     /*
2321       Erase text cursor.
2322     */
2323     text_event.xexpose.x=x;
2324     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2325     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2326       (unsigned int) text_event.xexpose.width,(unsigned int)
2327       text_event.xexpose.height,MagickFalse);
2328     XRefreshWindow(display,&windows->image,&text_event);
2329     switch (event.type)
2330     {
2331       case ButtonPress:
2332       {
2333         if (event.xbutton.window != windows->image.id)
2334           break;
2335         if (event.xbutton.button == Button2)
2336           {
2337             /*
2338               Request primary selection.
2339             */
2340             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2341               windows->image.id,CurrentTime);
2342             break;
2343           }
2344         break;
2345       }
2346       case Expose:
2347       {
2348         if (event.xexpose.count == 0)
2349           {
2350             XAnnotateInfo
2351               *text_info;
2352 
2353             /*
2354               Refresh Image window.
2355             */
2356             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2357             text_info=annotate_info;
2358             while (text_info != (XAnnotateInfo *) NULL)
2359             {
2360               if (annotate_info->stencil == ForegroundStencil)
2361                 (void) XDrawString(display,windows->image.id,annotate_context,
2362                   text_info->x,text_info->y,text_info->text,
2363                   (int) strlen(text_info->text));
2364               else
2365                 (void) XDrawImageString(display,windows->image.id,
2366                   annotate_context,text_info->x,text_info->y,text_info->text,
2367                   (int) strlen(text_info->text));
2368               text_info=text_info->previous;
2369             }
2370             (void) XDrawString(display,windows->image.id,annotate_context,
2371               x,y,"_",1);
2372           }
2373         break;
2374       }
2375       case KeyPress:
2376       {
2377         int
2378           length;
2379 
2380         if (event.xkey.window != windows->image.id)
2381           break;
2382         /*
2383           Respond to a user key press.
2384         */
2385         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2386           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2387         *(command+length)='\0';
2388         if (((event.xkey.state & ControlMask) != 0) ||
2389             ((event.xkey.state & Mod1Mask) != 0))
2390           state|=ModifierState;
2391         if ((state & ModifierState) != 0)
2392           switch ((int) key_symbol)
2393           {
2394             case XK_u:
2395             case XK_U:
2396             {
2397               key_symbol=DeleteCommand;
2398               break;
2399             }
2400             default:
2401               break;
2402           }
2403         switch ((int) key_symbol)
2404         {
2405           case XK_BackSpace:
2406           {
2407             /*
2408               Erase one character.
2409             */
2410             if (p == annotate_info->text)
2411               {
2412                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2413                   break;
2414                 else
2415                   {
2416                     /*
2417                       Go to end of the previous line of text.
2418                     */
2419                     annotate_info=annotate_info->previous;
2420                     p=annotate_info->text;
2421                     x=annotate_info->x+annotate_info->width;
2422                     y=annotate_info->y;
2423                     if (annotate_info->width != 0)
2424                       p+=strlen(annotate_info->text);
2425                     break;
2426                   }
2427               }
2428             p--;
2429             x-=XTextWidth(font_info,p,1);
2430             text_event.xexpose.x=x;
2431             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2432             XRefreshWindow(display,&windows->image,&text_event);
2433             break;
2434           }
2435           case XK_bracketleft:
2436           {
2437             key_symbol=XK_Escape;
2438             break;
2439           }
2440           case DeleteCommand:
2441           {
2442             /*
2443               Erase the entire line of text.
2444             */
2445             while (p != annotate_info->text)
2446             {
2447               p--;
2448               x-=XTextWidth(font_info,p,1);
2449               text_event.xexpose.x=x;
2450               XRefreshWindow(display,&windows->image,&text_event);
2451             }
2452             break;
2453           }
2454           case XK_Escape:
2455           case XK_F20:
2456           {
2457             /*
2458               Finished annotating.
2459             */
2460             annotate_info->width=(unsigned int) XTextWidth(font_info,
2461               annotate_info->text,(int) strlen(annotate_info->text));
2462             XRefreshWindow(display,&windows->image,&text_event);
2463             state|=ExitState;
2464             break;
2465           }
2466           default:
2467           {
2468             /*
2469               Draw a single character on the Image window.
2470             */
2471             if ((state & ModifierState) != 0)
2472               break;
2473             if (*command == '\0')
2474               break;
2475             *p=(*command);
2476             if (annotate_info->stencil == ForegroundStencil)
2477               (void) XDrawString(display,windows->image.id,annotate_context,
2478                 x,y,p,1);
2479             else
2480               (void) XDrawImageString(display,windows->image.id,
2481                 annotate_context,x,y,p,1);
2482             x+=XTextWidth(font_info,p,1);
2483             p++;
2484             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2485               break;
2486           }
2487           case XK_Return:
2488           case XK_KP_Enter:
2489           {
2490             /*
2491               Advance to the next line of text.
2492             */
2493             *p='\0';
2494             annotate_info->width=(unsigned int) XTextWidth(font_info,
2495               annotate_info->text,(int) strlen(annotate_info->text));
2496             if (annotate_info->next != (XAnnotateInfo *) NULL)
2497               {
2498                 /*
2499                   Line of text already exists.
2500                 */
2501                 annotate_info=annotate_info->next;
2502                 x=annotate_info->x;
2503                 y=annotate_info->y;
2504                 p=annotate_info->text;
2505                 break;
2506               }
2507             annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2508               sizeof(*annotate_info->next));
2509             if (annotate_info->next == (XAnnotateInfo *) NULL)
2510               return(MagickFalse);
2511             *annotate_info->next=(*annotate_info);
2512             annotate_info->next->previous=annotate_info;
2513             annotate_info=annotate_info->next;
2514             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2515               windows->image.width/MagickMax((ssize_t)
2516               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2517             if (annotate_info->text == (char *) NULL)
2518               return(MagickFalse);
2519             annotate_info->y+=annotate_info->height;
2520             if (annotate_info->y > (int) windows->image.height)
2521               annotate_info->y=(int) annotate_info->height;
2522             annotate_info->next=(XAnnotateInfo *) NULL;
2523             x=annotate_info->x;
2524             y=annotate_info->y;
2525             p=annotate_info->text;
2526             break;
2527           }
2528         }
2529         break;
2530       }
2531       case KeyRelease:
2532       {
2533         /*
2534           Respond to a user key release.
2535         */
2536         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2537           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2538         state&=(~ModifierState);
2539         break;
2540       }
2541       case SelectionNotify:
2542       {
2543         Atom
2544           type;
2545 
2546         int
2547           format;
2548 
2549         unsigned char
2550           *data;
2551 
2552         unsigned long
2553           after,
2554           length;
2555 
2556         /*
2557           Obtain response from primary selection.
2558         */
2559         if (event.xselection.property == (Atom) None)
2560           break;
2561         status=XGetWindowProperty(display,event.xselection.requestor,
2562           event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2563           &type,&format,&length,&after,&data);
2564         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2565             (length == 0))
2566           break;
2567         /*
2568           Annotate Image window with primary selection.
2569         */
2570         for (i=0; i < (ssize_t) length; i++)
2571         {
2572           if ((char) data[i] != '\n')
2573             {
2574               /*
2575                 Draw a single character on the Image window.
2576               */
2577               *p=(char) data[i];
2578               (void) XDrawString(display,windows->image.id,annotate_context,
2579                 x,y,p,1);
2580               x+=XTextWidth(font_info,p,1);
2581               p++;
2582               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2583                 continue;
2584             }
2585           /*
2586             Advance to the next line of text.
2587           */
2588           *p='\0';
2589           annotate_info->width=(unsigned int) XTextWidth(font_info,
2590             annotate_info->text,(int) strlen(annotate_info->text));
2591           if (annotate_info->next != (XAnnotateInfo *) NULL)
2592             {
2593               /*
2594                 Line of text already exists.
2595               */
2596               annotate_info=annotate_info->next;
2597               x=annotate_info->x;
2598               y=annotate_info->y;
2599               p=annotate_info->text;
2600               continue;
2601             }
2602           annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2603             sizeof(*annotate_info->next));
2604           if (annotate_info->next == (XAnnotateInfo *) NULL)
2605             return(MagickFalse);
2606           *annotate_info->next=(*annotate_info);
2607           annotate_info->next->previous=annotate_info;
2608           annotate_info=annotate_info->next;
2609           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2610             windows->image.width/MagickMax((ssize_t)
2611             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2612           if (annotate_info->text == (char *) NULL)
2613             return(MagickFalse);
2614           annotate_info->y+=annotate_info->height;
2615           if (annotate_info->y > (int) windows->image.height)
2616             annotate_info->y=(int) annotate_info->height;
2617           annotate_info->next=(XAnnotateInfo *) NULL;
2618           x=annotate_info->x;
2619           y=annotate_info->y;
2620           p=annotate_info->text;
2621         }
2622         (void) XFree((void *) data);
2623         break;
2624       }
2625       default:
2626         break;
2627     }
2628   } while ((state & ExitState) == 0);
2629   (void) XFreeCursor(display,cursor);
2630   /*
2631     Annotation is relative to image configuration.
2632   */
2633   width=(unsigned int) image->columns;
2634   height=(unsigned int) image->rows;
2635   x=0;
2636   y=0;
2637   if (windows->image.crop_geometry != (char *) NULL)
2638     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2639   /*
2640     Initialize annotated image.
2641   */
2642   XSetCursorState(display,windows,MagickTrue);
2643   XCheckRefreshWindows(display,windows);
2644   while (annotate_info != (XAnnotateInfo *) NULL)
2645   {
2646     if (annotate_info->width == 0)
2647       {
2648         /*
2649           No text on this line--  go to the next line of text.
2650         */
2651         previous_info=annotate_info->previous;
2652         annotate_info->text=(char *)
2653           RelinquishMagickMemory(annotate_info->text);
2654         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2655         annotate_info=previous_info;
2656         continue;
2657       }
2658     /*
2659       Determine pixel index for box and pen color.
2660     */
2661     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2662     if (windows->pixel_info->colors != 0)
2663       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2664         if (windows->pixel_info->pixels[i] ==
2665             windows->pixel_info->pen_colors[box_id].pixel)
2666           {
2667             windows->pixel_info->box_index=(unsigned short) i;
2668             break;
2669           }
2670     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2671     if (windows->pixel_info->colors != 0)
2672       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2673         if (windows->pixel_info->pixels[i] ==
2674             windows->pixel_info->pen_colors[pen_id].pixel)
2675           {
2676             windows->pixel_info->pen_index=(unsigned short) i;
2677             break;
2678           }
2679     /*
2680       Define the annotate geometry string.
2681     */
2682     annotate_info->x=(int)
2683       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2684     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2685       windows->image.y)/windows->image.ximage->height;
2686     (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2687       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2688       height*annotate_info->height/windows->image.ximage->height,
2689       annotate_info->x+x,annotate_info->y+y);
2690     /*
2691       Annotate image with text.
2692     */
2693     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2694       exception);
2695     if (status == 0)
2696       return(MagickFalse);
2697     /*
2698       Free up memory.
2699     */
2700     previous_info=annotate_info->previous;
2701     annotate_info->text=DestroyString(annotate_info->text);
2702     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2703     annotate_info=previous_info;
2704   }
2705   (void) XSetForeground(display,annotate_context,
2706     windows->pixel_info->foreground_color.pixel);
2707   (void) XSetBackground(display,annotate_context,
2708     windows->pixel_info->background_color.pixel);
2709   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2710   XSetCursorState(display,windows,MagickFalse);
2711   (void) XFreeFont(display,font_info);
2712   /*
2713     Update image configuration.
2714   */
2715   XConfigureImageColormap(display,resource_info,windows,image,exception);
2716   (void) XConfigureImage(display,resource_info,windows,image,exception);
2717   return(MagickTrue);
2718 }
2719 
2720 /*
2721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2722 %                                                                             %
2723 %                                                                             %
2724 %                                                                             %
2725 +   X B a c k g r o u n d I m a g e                                           %
2726 %                                                                             %
2727 %                                                                             %
2728 %                                                                             %
2729 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2730 %
2731 %  XBackgroundImage() displays the image in the background of a window.
2732 %
2733 %  The format of the XBackgroundImage method is:
2734 %
2735 %      MagickBooleanType XBackgroundImage(Display *display,
2736 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2737 %        ExceptionInfo *exception)
2738 %
2739 %  A description of each parameter follows:
2740 %
2741 %    o display: Specifies a connection to an X server; returned from
2742 %      XOpenDisplay.
2743 %
2744 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2745 %
2746 %    o windows: Specifies a pointer to a XWindows structure.
2747 %
2748 %    o image: the image.
2749 %
2750 %    o exception: return any errors or warnings in this structure.
2751 %
2752 */
XBackgroundImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)2753 static MagickBooleanType XBackgroundImage(Display *display,
2754   XResourceInfo *resource_info,XWindows *windows,Image **image,
2755   ExceptionInfo *exception)
2756 {
2757 #define BackgroundImageTag  "Background/Image"
2758 
2759   int
2760     status;
2761 
2762   static char
2763     window_id[MagickPathExtent] = "root";
2764 
2765   XResourceInfo
2766     background_resources;
2767 
2768   /*
2769     Put image in background.
2770   */
2771   status=XDialogWidget(display,windows,"Background",
2772     "Enter window id (id 0x00 selects window with pointer):",window_id);
2773   if (*window_id == '\0')
2774     return(MagickFalse);
2775   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2776     exception);
2777   XInfoWidget(display,windows,BackgroundImageTag);
2778   XSetCursorState(display,windows,MagickTrue);
2779   XCheckRefreshWindows(display,windows);
2780   background_resources=(*resource_info);
2781   background_resources.window_id=window_id;
2782   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2783   status=XDisplayBackgroundImage(display,&background_resources,*image,
2784     exception);
2785   if (status != MagickFalse)
2786     XClientMessage(display,windows->image.id,windows->im_protocols,
2787       windows->im_retain_colors,CurrentTime);
2788   XSetCursorState(display,windows,MagickFalse);
2789   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2790     exception);
2791   return(MagickTrue);
2792 }
2793 
2794 /*
2795 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2796 %                                                                             %
2797 %                                                                             %
2798 %                                                                             %
2799 +   X C h o p I m a g e                                                       %
2800 %                                                                             %
2801 %                                                                             %
2802 %                                                                             %
2803 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2804 %
2805 %  XChopImage() chops the X image.
2806 %
2807 %  The format of the XChopImage method is:
2808 %
2809 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2810 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2811 %
2812 %  A description of each parameter follows:
2813 %
2814 %    o display: Specifies a connection to an X server; returned from
2815 %      XOpenDisplay.
2816 %
2817 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2818 %
2819 %    o windows: Specifies a pointer to a XWindows structure.
2820 %
2821 %    o image: the image.
2822 %
2823 %    o exception: return any errors or warnings in this structure.
2824 %
2825 */
XChopImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)2826 static MagickBooleanType XChopImage(Display *display,
2827   XResourceInfo *resource_info,XWindows *windows,Image **image,
2828   ExceptionInfo *exception)
2829 {
2830   const char
2831     *const ChopMenu[] =
2832     {
2833       "Direction",
2834       "Help",
2835       "Dismiss",
2836       (char *) NULL
2837     };
2838 
2839   static ModeType
2840     direction = HorizontalChopCommand;
2841 
2842   static const ModeType
2843     ChopCommands[] =
2844     {
2845       ChopDirectionCommand,
2846       ChopHelpCommand,
2847       ChopDismissCommand
2848     },
2849     DirectionCommands[] =
2850     {
2851       HorizontalChopCommand,
2852       VerticalChopCommand
2853     };
2854 
2855   char
2856     text[MagickPathExtent];
2857 
2858   Image
2859     *chop_image;
2860 
2861   int
2862     id,
2863     x,
2864     y;
2865 
2866   double
2867     scale_factor;
2868 
2869   RectangleInfo
2870     chop_info;
2871 
2872   unsigned int
2873     distance,
2874     height,
2875     width;
2876 
2877   size_t
2878     state;
2879 
2880   XEvent
2881     event;
2882 
2883   XSegment
2884     segment_info;
2885 
2886   /*
2887     Map Command widget.
2888   */
2889   (void) CloneString(&windows->command.name,"Chop");
2890   windows->command.data=1;
2891   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2892   (void) XMapRaised(display,windows->command.id);
2893   XClientMessage(display,windows->image.id,windows->im_protocols,
2894     windows->im_update_widget,CurrentTime);
2895   /*
2896     Track pointer until button 1 is pressed.
2897   */
2898   XQueryPosition(display,windows->image.id,&x,&y);
2899   (void) XSelectInput(display,windows->image.id,
2900     windows->image.attributes.event_mask | PointerMotionMask);
2901   state=DefaultState;
2902   (void) memset(&segment_info,0,sizeof(segment_info));
2903   do
2904   {
2905     if (windows->info.mapped != MagickFalse)
2906       {
2907         /*
2908           Display pointer position.
2909         */
2910         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2911           x+windows->image.x,y+windows->image.y);
2912         XInfoWidget(display,windows,text);
2913       }
2914     /*
2915       Wait for next event.
2916     */
2917     XScreenEvent(display,windows,&event,exception);
2918     if (event.xany.window == windows->command.id)
2919       {
2920         /*
2921           Select a command from the Command widget.
2922         */
2923         id=XCommandWidget(display,windows,ChopMenu,&event);
2924         if (id < 0)
2925           continue;
2926         switch (ChopCommands[id])
2927         {
2928           case ChopDirectionCommand:
2929           {
2930             char
2931               command[MagickPathExtent];
2932 
2933             const char
2934               *const Directions[] =
2935               {
2936                 "horizontal",
2937                 "vertical",
2938                 (char *) NULL,
2939               };
2940 
2941             /*
2942               Select a command from the pop-up menu.
2943             */
2944             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2945             if (id >= 0)
2946               direction=DirectionCommands[id];
2947             break;
2948           }
2949           case ChopHelpCommand:
2950           {
2951             XTextViewHelp(display,resource_info,windows,MagickFalse,
2952               "Help Viewer - Image Chop",ImageChopHelp);
2953             break;
2954           }
2955           case ChopDismissCommand:
2956           {
2957             /*
2958               Prematurely exit.
2959             */
2960             state|=EscapeState;
2961             state|=ExitState;
2962             break;
2963           }
2964           default:
2965             break;
2966         }
2967         continue;
2968       }
2969     switch (event.type)
2970     {
2971       case ButtonPress:
2972       {
2973         if (event.xbutton.button != Button1)
2974           break;
2975         if (event.xbutton.window != windows->image.id)
2976           break;
2977         /*
2978           User has committed to start point of chopping line.
2979         */
2980         segment_info.x1=(short int) event.xbutton.x;
2981         segment_info.x2=(short int) event.xbutton.x;
2982         segment_info.y1=(short int) event.xbutton.y;
2983         segment_info.y2=(short int) event.xbutton.y;
2984         state|=ExitState;
2985         break;
2986       }
2987       case ButtonRelease:
2988         break;
2989       case Expose:
2990         break;
2991       case KeyPress:
2992       {
2993         char
2994           command[MagickPathExtent];
2995 
2996         KeySym
2997           key_symbol;
2998 
2999         if (event.xkey.window != windows->image.id)
3000           break;
3001         /*
3002           Respond to a user key press.
3003         */
3004         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3005           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3006         switch ((int) key_symbol)
3007         {
3008           case XK_Escape:
3009           case XK_F20:
3010           {
3011             /*
3012               Prematurely exit.
3013             */
3014             state|=EscapeState;
3015             state|=ExitState;
3016             break;
3017           }
3018           case XK_F1:
3019           case XK_Help:
3020           {
3021             (void) XSetFunction(display,windows->image.highlight_context,
3022               GXcopy);
3023             XTextViewHelp(display,resource_info,windows,MagickFalse,
3024               "Help Viewer - Image Chop",ImageChopHelp);
3025             (void) XSetFunction(display,windows->image.highlight_context,
3026               GXinvert);
3027             break;
3028           }
3029           default:
3030           {
3031             (void) XBell(display,0);
3032             break;
3033           }
3034         }
3035         break;
3036       }
3037       case MotionNotify:
3038       {
3039         /*
3040           Map and unmap Info widget as text cursor crosses its boundaries.
3041         */
3042         x=event.xmotion.x;
3043         y=event.xmotion.y;
3044         if (windows->info.mapped != MagickFalse)
3045           {
3046             if ((x < (int) (windows->info.x+windows->info.width)) &&
3047                 (y < (int) (windows->info.y+windows->info.height)))
3048               (void) XWithdrawWindow(display,windows->info.id,
3049                 windows->info.screen);
3050           }
3051         else
3052           if ((x > (int) (windows->info.x+windows->info.width)) ||
3053               (y > (int) (windows->info.y+windows->info.height)))
3054             (void) XMapWindow(display,windows->info.id);
3055       }
3056     }
3057   } while ((state & ExitState) == 0);
3058   (void) XSelectInput(display,windows->image.id,
3059     windows->image.attributes.event_mask);
3060   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3061   if ((state & EscapeState) != 0)
3062     return(MagickTrue);
3063   /*
3064     Draw line as pointer moves until the mouse button is released.
3065   */
3066   chop_info.width=0;
3067   chop_info.height=0;
3068   chop_info.x=0;
3069   chop_info.y=0;
3070   distance=0;
3071   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3072   state=DefaultState;
3073   do
3074   {
3075     if (distance > 9)
3076       {
3077         /*
3078           Display info and draw chopping line.
3079         */
3080         if (windows->info.mapped == MagickFalse)
3081           (void) XMapWindow(display,windows->info.id);
3082         (void) FormatLocaleString(text,MagickPathExtent,
3083           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3084           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3085         XInfoWidget(display,windows,text);
3086         XHighlightLine(display,windows->image.id,
3087           windows->image.highlight_context,&segment_info);
3088       }
3089     else
3090       if (windows->info.mapped != MagickFalse)
3091         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3092     /*
3093       Wait for next event.
3094     */
3095     XScreenEvent(display,windows,&event,exception);
3096     if (distance > 9)
3097       XHighlightLine(display,windows->image.id,
3098         windows->image.highlight_context,&segment_info);
3099     switch (event.type)
3100     {
3101       case ButtonPress:
3102       {
3103         segment_info.x2=(short int) event.xmotion.x;
3104         segment_info.y2=(short int) event.xmotion.y;
3105         break;
3106       }
3107       case ButtonRelease:
3108       {
3109         /*
3110           User has committed to chopping line.
3111         */
3112         segment_info.x2=(short int) event.xbutton.x;
3113         segment_info.y2=(short int) event.xbutton.y;
3114         state|=ExitState;
3115         break;
3116       }
3117       case Expose:
3118         break;
3119       case MotionNotify:
3120       {
3121         segment_info.x2=(short int) event.xmotion.x;
3122         segment_info.y2=(short int) event.xmotion.y;
3123       }
3124       default:
3125         break;
3126     }
3127     /*
3128       Check boundary conditions.
3129     */
3130     if (segment_info.x2 < 0)
3131       segment_info.x2=0;
3132     else
3133       if (segment_info.x2 > windows->image.ximage->width)
3134         segment_info.x2=windows->image.ximage->width;
3135     if (segment_info.y2 < 0)
3136       segment_info.y2=0;
3137     else
3138       if (segment_info.y2 > windows->image.ximage->height)
3139         segment_info.y2=windows->image.ximage->height;
3140     distance=(unsigned int)
3141       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3142        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3143     /*
3144       Compute chopping geometry.
3145     */
3146     if (direction == HorizontalChopCommand)
3147       {
3148         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3149         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3150         chop_info.height=0;
3151         chop_info.y=0;
3152         if (segment_info.x1 > (int) segment_info.x2)
3153           {
3154             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3155             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3156           }
3157       }
3158     else
3159       {
3160         chop_info.width=0;
3161         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3162         chop_info.x=0;
3163         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3164         if (segment_info.y1 > segment_info.y2)
3165           {
3166             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3167             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3168           }
3169       }
3170   } while ((state & ExitState) == 0);
3171   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3172   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3173   if (distance <= 9)
3174     return(MagickTrue);
3175   /*
3176     Image chopping is relative to image configuration.
3177   */
3178   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3179     exception);
3180   XSetCursorState(display,windows,MagickTrue);
3181   XCheckRefreshWindows(display,windows);
3182   windows->image.window_changes.width=windows->image.ximage->width-
3183     (unsigned int) chop_info.width;
3184   windows->image.window_changes.height=windows->image.ximage->height-
3185     (unsigned int) chop_info.height;
3186   width=(unsigned int) (*image)->columns;
3187   height=(unsigned int) (*image)->rows;
3188   x=0;
3189   y=0;
3190   if (windows->image.crop_geometry != (char *) NULL)
3191     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3192   scale_factor=(double) width/windows->image.ximage->width;
3193   chop_info.x+=x;
3194   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3195   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3196   scale_factor=(double) height/windows->image.ximage->height;
3197   chop_info.y+=y;
3198   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3199   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3200   /*
3201     Chop image.
3202   */
3203   chop_image=ChopImage(*image,&chop_info,exception);
3204   XSetCursorState(display,windows,MagickFalse);
3205   if (chop_image == (Image *) NULL)
3206     return(MagickFalse);
3207   *image=DestroyImage(*image);
3208   *image=chop_image;
3209   /*
3210     Update image configuration.
3211   */
3212   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3213   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3214   return(MagickTrue);
3215 }
3216 
3217 /*
3218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3219 %                                                                             %
3220 %                                                                             %
3221 %                                                                             %
3222 +   X C o l o r E d i t I m a g e                                             %
3223 %                                                                             %
3224 %                                                                             %
3225 %                                                                             %
3226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3227 %
3228 %  XColorEditImage() allows the user to interactively change the color of one
3229 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3230 %
3231 %  The format of the XColorEditImage method is:
3232 %
3233 %      MagickBooleanType XColorEditImage(Display *display,
3234 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3235 %          ExceptionInfo *exception)
3236 %
3237 %  A description of each parameter follows:
3238 %
3239 %    o display: Specifies a connection to an X server;  returned from
3240 %      XOpenDisplay.
3241 %
3242 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3243 %
3244 %    o windows: Specifies a pointer to a XWindows structure.
3245 %
3246 %    o image: the image; returned from ReadImage.
3247 %
3248 %    o exception: return any errors or warnings in this structure.
3249 %
3250 */
XColorEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)3251 static MagickBooleanType XColorEditImage(Display *display,
3252   XResourceInfo *resource_info,XWindows *windows,Image **image,
3253   ExceptionInfo *exception)
3254 {
3255   const char
3256     *const ColorEditMenu[] =
3257     {
3258       "Method",
3259       "Pixel Color",
3260       "Border Color",
3261       "Fuzz",
3262       "Undo",
3263       "Help",
3264       "Dismiss",
3265       (char *) NULL
3266     };
3267 
3268   static const ModeType
3269     ColorEditCommands[] =
3270     {
3271       ColorEditMethodCommand,
3272       ColorEditColorCommand,
3273       ColorEditBorderCommand,
3274       ColorEditFuzzCommand,
3275       ColorEditUndoCommand,
3276       ColorEditHelpCommand,
3277       ColorEditDismissCommand
3278     };
3279 
3280   static PaintMethod
3281     method = PointMethod;
3282 
3283   static unsigned int
3284     pen_id = 0;
3285 
3286   static XColor
3287     border_color = { 0, 0, 0, 0, 0, 0 };
3288 
3289   char
3290     command[MagickPathExtent],
3291     text[MagickPathExtent];
3292 
3293   Cursor
3294     cursor;
3295 
3296   int
3297     entry,
3298     id,
3299     x,
3300     x_offset,
3301     y,
3302     y_offset;
3303 
3304   Quantum
3305     *q;
3306 
3307   ssize_t
3308     i;
3309 
3310   unsigned int
3311     height,
3312     width;
3313 
3314   size_t
3315     state;
3316 
3317   XColor
3318     color;
3319 
3320   XEvent
3321     event;
3322 
3323   /*
3324     Map Command widget.
3325   */
3326   (void) CloneString(&windows->command.name,"Color Edit");
3327   windows->command.data=4;
3328   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3329   (void) XMapRaised(display,windows->command.id);
3330   XClientMessage(display,windows->image.id,windows->im_protocols,
3331     windows->im_update_widget,CurrentTime);
3332   /*
3333     Make cursor.
3334   */
3335   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3336     resource_info->background_color,resource_info->foreground_color);
3337   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3338   /*
3339     Track pointer until button 1 is pressed.
3340   */
3341   XQueryPosition(display,windows->image.id,&x,&y);
3342   (void) XSelectInput(display,windows->image.id,
3343     windows->image.attributes.event_mask | PointerMotionMask);
3344   state=DefaultState;
3345   do
3346   {
3347     if (windows->info.mapped != MagickFalse)
3348       {
3349         /*
3350           Display pointer position.
3351         */
3352         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3353           x+windows->image.x,y+windows->image.y);
3354         XInfoWidget(display,windows,text);
3355       }
3356     /*
3357       Wait for next event.
3358     */
3359     XScreenEvent(display,windows,&event,exception);
3360     if (event.xany.window == windows->command.id)
3361       {
3362         /*
3363           Select a command from the Command widget.
3364         */
3365         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3366         if (id < 0)
3367           {
3368             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3369             continue;
3370           }
3371         switch (ColorEditCommands[id])
3372         {
3373           case ColorEditMethodCommand:
3374           {
3375             char
3376               **methods;
3377 
3378             /*
3379               Select a method from the pop-up menu.
3380             */
3381             methods=(char **) GetCommandOptions(MagickMethodOptions);
3382             if (methods == (char **) NULL)
3383               break;
3384             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3385               (const char **) methods,command);
3386             if (entry >= 0)
3387               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3388                 MagickFalse,methods[entry]);
3389             methods=DestroyStringList(methods);
3390             break;
3391           }
3392           case ColorEditColorCommand:
3393           {
3394             const char
3395               *ColorMenu[MaxNumberPens];
3396 
3397             int
3398               pen_number;
3399 
3400             /*
3401               Initialize menu selections.
3402             */
3403             for (i=0; i < (int) (MaxNumberPens-2); i++)
3404               ColorMenu[i]=resource_info->pen_colors[i];
3405             ColorMenu[MaxNumberPens-2]="Browser...";
3406             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3407             /*
3408               Select a pen color from the pop-up menu.
3409             */
3410             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3411               (const char **) ColorMenu,command);
3412             if (pen_number < 0)
3413               break;
3414             if (pen_number == (MaxNumberPens-2))
3415               {
3416                 static char
3417                   color_name[MagickPathExtent] = "gray";
3418 
3419                 /*
3420                   Select a pen color from a dialog.
3421                 */
3422                 resource_info->pen_colors[pen_number]=color_name;
3423                 XColorBrowserWidget(display,windows,"Select",color_name);
3424                 if (*color_name == '\0')
3425                   break;
3426               }
3427             /*
3428               Set pen color.
3429             */
3430             (void) XParseColor(display,windows->map_info->colormap,
3431               resource_info->pen_colors[pen_number],&color);
3432             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3433               (unsigned int) MaxColors,&color);
3434             windows->pixel_info->pen_colors[pen_number]=color;
3435             pen_id=(unsigned int) pen_number;
3436             break;
3437           }
3438           case ColorEditBorderCommand:
3439           {
3440             const char
3441               *ColorMenu[MaxNumberPens];
3442 
3443             int
3444               pen_number;
3445 
3446             /*
3447               Initialize menu selections.
3448             */
3449             for (i=0; i < (int) (MaxNumberPens-2); i++)
3450               ColorMenu[i]=resource_info->pen_colors[i];
3451             ColorMenu[MaxNumberPens-2]="Browser...";
3452             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3453             /*
3454               Select a pen color from the pop-up menu.
3455             */
3456             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3457               (const char **) ColorMenu,command);
3458             if (pen_number < 0)
3459               break;
3460             if (pen_number == (MaxNumberPens-2))
3461               {
3462                 static char
3463                   color_name[MagickPathExtent] = "gray";
3464 
3465                 /*
3466                   Select a pen color from a dialog.
3467                 */
3468                 resource_info->pen_colors[pen_number]=color_name;
3469                 XColorBrowserWidget(display,windows,"Select",color_name);
3470                 if (*color_name == '\0')
3471                   break;
3472               }
3473             /*
3474               Set border color.
3475             */
3476             (void) XParseColor(display,windows->map_info->colormap,
3477               resource_info->pen_colors[pen_number],&border_color);
3478             break;
3479           }
3480           case ColorEditFuzzCommand:
3481           {
3482             const char
3483               *const FuzzMenu[] =
3484               {
3485                 "0%",
3486                 "2%",
3487                 "5%",
3488                 "10%",
3489                 "15%",
3490                 "Dialog...",
3491                 (char *) NULL,
3492               };
3493 
3494             static char
3495               fuzz[MagickPathExtent];
3496 
3497             /*
3498               Select a command from the pop-up menu.
3499             */
3500             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3501               command);
3502             if (entry < 0)
3503               break;
3504             if (entry != 5)
3505               {
3506                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3507                   QuantumRange+1.0);
3508                 break;
3509               }
3510             (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3511             (void) XDialogWidget(display,windows,"Ok",
3512               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3513             if (*fuzz == '\0')
3514               break;
3515             (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3516             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3517               1.0);
3518             break;
3519           }
3520           case ColorEditUndoCommand:
3521           {
3522             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3523               image,exception);
3524             break;
3525           }
3526           case ColorEditHelpCommand:
3527           default:
3528           {
3529             XTextViewHelp(display,resource_info,windows,MagickFalse,
3530               "Help Viewer - Image Annotation",ImageColorEditHelp);
3531             break;
3532           }
3533           case ColorEditDismissCommand:
3534           {
3535             /*
3536               Prematurely exit.
3537             */
3538             state|=EscapeState;
3539             state|=ExitState;
3540             break;
3541           }
3542         }
3543         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3544         continue;
3545       }
3546     switch (event.type)
3547     {
3548       case ButtonPress:
3549       {
3550         if (event.xbutton.button != Button1)
3551           break;
3552         if ((event.xbutton.window != windows->image.id) &&
3553             (event.xbutton.window != windows->magnify.id))
3554           break;
3555         /*
3556           exit loop.
3557         */
3558         x=event.xbutton.x;
3559         y=event.xbutton.y;
3560         (void) XMagickCommand(display,resource_info,windows,
3561           SaveToUndoBufferCommand,image,exception);
3562         state|=UpdateConfigurationState;
3563         break;
3564       }
3565       case ButtonRelease:
3566       {
3567         if (event.xbutton.button != Button1)
3568           break;
3569         if ((event.xbutton.window != windows->image.id) &&
3570             (event.xbutton.window != windows->magnify.id))
3571           break;
3572         /*
3573           Update colormap information.
3574         */
3575         x=event.xbutton.x;
3576         y=event.xbutton.y;
3577         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3578         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3579         XInfoWidget(display,windows,text);
3580         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581         state&=(~UpdateConfigurationState);
3582         break;
3583       }
3584       case Expose:
3585         break;
3586       case KeyPress:
3587       {
3588         KeySym
3589           key_symbol;
3590 
3591         if (event.xkey.window == windows->magnify.id)
3592           {
3593             Window
3594               window;
3595 
3596             window=windows->magnify.id;
3597             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3598           }
3599         if (event.xkey.window != windows->image.id)
3600           break;
3601         /*
3602           Respond to a user key press.
3603         */
3604         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3605           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3606         switch ((int) key_symbol)
3607         {
3608           case XK_Escape:
3609           case XK_F20:
3610           {
3611             /*
3612               Prematurely exit.
3613             */
3614             state|=ExitState;
3615             break;
3616           }
3617           case XK_F1:
3618           case XK_Help:
3619           {
3620             XTextViewHelp(display,resource_info,windows,MagickFalse,
3621               "Help Viewer - Image Annotation",ImageColorEditHelp);
3622             break;
3623           }
3624           default:
3625           {
3626             (void) XBell(display,0);
3627             break;
3628           }
3629         }
3630         break;
3631       }
3632       case MotionNotify:
3633       {
3634         /*
3635           Map and unmap Info widget as cursor crosses its boundaries.
3636         */
3637         x=event.xmotion.x;
3638         y=event.xmotion.y;
3639         if (windows->info.mapped != MagickFalse)
3640           {
3641             if ((x < (int) (windows->info.x+windows->info.width)) &&
3642                 (y < (int) (windows->info.y+windows->info.height)))
3643               (void) XWithdrawWindow(display,windows->info.id,
3644                 windows->info.screen);
3645           }
3646         else
3647           if ((x > (int) (windows->info.x+windows->info.width)) ||
3648               (y > (int) (windows->info.y+windows->info.height)))
3649             (void) XMapWindow(display,windows->info.id);
3650         break;
3651       }
3652       default:
3653         break;
3654     }
3655     if (event.xany.window == windows->magnify.id)
3656       {
3657         x=windows->magnify.x-windows->image.x;
3658         y=windows->magnify.y-windows->image.y;
3659       }
3660     x_offset=x;
3661     y_offset=y;
3662     if ((state & UpdateConfigurationState) != 0)
3663       {
3664         CacheView
3665           *image_view;
3666 
3667         int
3668           x,
3669           y;
3670 
3671         /*
3672           Pixel edit is relative to image configuration.
3673         */
3674         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3675           MagickTrue);
3676         color=windows->pixel_info->pen_colors[pen_id];
3677         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3678         width=(unsigned int) (*image)->columns;
3679         height=(unsigned int) (*image)->rows;
3680         x=0;
3681         y=0;
3682         if (windows->image.crop_geometry != (char *) NULL)
3683           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3684             &width,&height);
3685         x_offset=(int)
3686           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3687         y_offset=(int)
3688           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3689         if ((x_offset < 0) || (y_offset < 0))
3690           continue;
3691         if ((x_offset >= (int) (*image)->columns) ||
3692             (y_offset >= (int) (*image)->rows))
3693           continue;
3694         image_view=AcquireAuthenticCacheView(*image,exception);
3695         switch (method)
3696         {
3697           case PointMethod:
3698           default:
3699           {
3700             /*
3701               Update color information using point algorithm.
3702             */
3703             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3704               return(MagickFalse);
3705             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3706               (ssize_t) y_offset,1,1,exception);
3707             if (q == (Quantum *) NULL)
3708               break;
3709             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3710             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3711             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3712             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3713             break;
3714           }
3715           case ReplaceMethod:
3716           {
3717             PixelInfo
3718               pixel,
3719               target;
3720 
3721             /*
3722               Update color information using replace algorithm.
3723             */
3724             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3725               x_offset,(ssize_t) y_offset,&target,exception);
3726             if ((*image)->storage_class == DirectClass)
3727               {
3728                 for (y=0; y < (int) (*image)->rows; y++)
3729                 {
3730                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3731                     (*image)->columns,1,exception);
3732                   if (q == (Quantum *) NULL)
3733                     break;
3734                   for (x=0; x < (int) (*image)->columns; x++)
3735                   {
3736                     GetPixelInfoPixel(*image,q,&pixel);
3737                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3738                       {
3739                         SetPixelRed(*image,ScaleShortToQuantum(
3740                           color.red),q);
3741                         SetPixelGreen(*image,ScaleShortToQuantum(
3742                           color.green),q);
3743                         SetPixelBlue(*image,ScaleShortToQuantum(
3744                           color.blue),q);
3745                       }
3746                     q+=GetPixelChannels(*image);
3747                   }
3748                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3749                     break;
3750                 }
3751               }
3752             else
3753               {
3754                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3755                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3756                     {
3757                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3758                         color.red);
3759                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3760                         color.green);
3761                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3762                         color.blue);
3763                     }
3764                 (void) SyncImage(*image,exception);
3765               }
3766             break;
3767           }
3768           case FloodfillMethod:
3769           case FillToBorderMethod:
3770           {
3771             DrawInfo
3772               *draw_info;
3773 
3774             PixelInfo
3775               target;
3776 
3777             /*
3778               Update color information using floodfill algorithm.
3779             */
3780             (void) GetOneVirtualPixelInfo(*image,
3781               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3782               y_offset,&target,exception);
3783             if (method == FillToBorderMethod)
3784               {
3785                 target.red=(double)
3786                   ScaleShortToQuantum(border_color.red);
3787                 target.green=(double)
3788                   ScaleShortToQuantum(border_color.green);
3789                 target.blue=(double)
3790                   ScaleShortToQuantum(border_color.blue);
3791               }
3792             draw_info=CloneDrawInfo(resource_info->image_info,
3793               (DrawInfo *) NULL);
3794             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3795               AllCompliance,&draw_info->fill,exception);
3796             (void) FloodfillPaintImage(*image,draw_info,&target,
3797               (ssize_t)x_offset,(ssize_t)y_offset,
3798               method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3799             draw_info=DestroyDrawInfo(draw_info);
3800             break;
3801           }
3802           case ResetMethod:
3803           {
3804             /*
3805               Update color information using reset algorithm.
3806             */
3807             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3808               return(MagickFalse);
3809             for (y=0; y < (int) (*image)->rows; y++)
3810             {
3811               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3812                 (*image)->columns,1,exception);
3813               if (q == (Quantum *) NULL)
3814                 break;
3815               for (x=0; x < (int) (*image)->columns; x++)
3816               {
3817                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3818                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3819                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3820                 q+=GetPixelChannels(*image);
3821               }
3822               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3823                 break;
3824             }
3825             break;
3826           }
3827         }
3828         image_view=DestroyCacheView(image_view);
3829         state&=(~UpdateConfigurationState);
3830       }
3831   } while ((state & ExitState) == 0);
3832   (void) XSelectInput(display,windows->image.id,
3833     windows->image.attributes.event_mask);
3834   XSetCursorState(display,windows,MagickFalse);
3835   (void) XFreeCursor(display,cursor);
3836   return(MagickTrue);
3837 }
3838 
3839 /*
3840 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3841 %                                                                             %
3842 %                                                                             %
3843 %                                                                             %
3844 +   X C o m p o s i t e I m a g e                                             %
3845 %                                                                             %
3846 %                                                                             %
3847 %                                                                             %
3848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3849 %
3850 %  XCompositeImage() requests an image name from the user, reads the image and
3851 %  composites it with the X window image at a location the user chooses with
3852 %  the pointer.
3853 %
3854 %  The format of the XCompositeImage method is:
3855 %
3856 %      MagickBooleanType XCompositeImage(Display *display,
3857 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3858 %        ExceptionInfo *exception)
3859 %
3860 %  A description of each parameter follows:
3861 %
3862 %    o display: Specifies a connection to an X server;  returned from
3863 %      XOpenDisplay.
3864 %
3865 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3866 %
3867 %    o windows: Specifies a pointer to a XWindows structure.
3868 %
3869 %    o image: the image; returned from ReadImage.
3870 %
3871 %    o exception: return any errors or warnings in this structure.
3872 %
3873 */
XCompositeImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)3874 static MagickBooleanType XCompositeImage(Display *display,
3875   XResourceInfo *resource_info,XWindows *windows,Image *image,
3876   ExceptionInfo *exception)
3877 {
3878   const char
3879     *const CompositeMenu[] =
3880     {
3881       "Operators",
3882       "Dissolve",
3883       "Displace",
3884       "Help",
3885       "Dismiss",
3886       (char *) NULL
3887     };
3888 
3889   static char
3890     displacement_geometry[MagickPathExtent] = "30x30",
3891     filename[MagickPathExtent] = "\0";
3892 
3893   static CompositeOperator
3894     compose = CopyCompositeOp;
3895 
3896   static const ModeType
3897     CompositeCommands[] =
3898     {
3899       CompositeOperatorsCommand,
3900       CompositeDissolveCommand,
3901       CompositeDisplaceCommand,
3902       CompositeHelpCommand,
3903       CompositeDismissCommand
3904     };
3905 
3906   char
3907     text[MagickPathExtent];
3908 
3909   Cursor
3910     cursor;
3911 
3912   Image
3913     *composite_image;
3914 
3915   int
3916     entry,
3917     id,
3918     x,
3919     y;
3920 
3921   double
3922     blend,
3923     scale_factor;
3924 
3925   RectangleInfo
3926     highlight_info,
3927     composite_info;
3928 
3929   unsigned int
3930     height,
3931     width;
3932 
3933   size_t
3934     state;
3935 
3936   XEvent
3937     event;
3938 
3939   /*
3940     Request image file name from user.
3941   */
3942   XFileBrowserWidget(display,windows,"Composite",filename);
3943   if (*filename == '\0')
3944     return(MagickTrue);
3945   /*
3946     Read image.
3947   */
3948   XSetCursorState(display,windows,MagickTrue);
3949   XCheckRefreshWindows(display,windows);
3950   (void) CopyMagickString(resource_info->image_info->filename,filename,
3951     MagickPathExtent);
3952   composite_image=ReadImage(resource_info->image_info,exception);
3953   CatchException(exception);
3954   XSetCursorState(display,windows,MagickFalse);
3955   if (composite_image == (Image *) NULL)
3956     return(MagickFalse);
3957   /*
3958     Map Command widget.
3959   */
3960   (void) CloneString(&windows->command.name,"Composite");
3961   windows->command.data=1;
3962   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3963   (void) XMapRaised(display,windows->command.id);
3964   XClientMessage(display,windows->image.id,windows->im_protocols,
3965     windows->im_update_widget,CurrentTime);
3966   /*
3967     Track pointer until button 1 is pressed.
3968   */
3969   XQueryPosition(display,windows->image.id,&x,&y);
3970   (void) XSelectInput(display,windows->image.id,
3971     windows->image.attributes.event_mask | PointerMotionMask);
3972   composite_info.x=(ssize_t) windows->image.x+x;
3973   composite_info.y=(ssize_t) windows->image.y+y;
3974   composite_info.width=0;
3975   composite_info.height=0;
3976   cursor=XCreateFontCursor(display,XC_ul_angle);
3977   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3978   blend=0.0;
3979   state=DefaultState;
3980   do
3981   {
3982     if (windows->info.mapped != MagickFalse)
3983       {
3984         /*
3985           Display pointer position.
3986         */
3987         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
3988           (long) composite_info.x,(long) composite_info.y);
3989         XInfoWidget(display,windows,text);
3990       }
3991     highlight_info=composite_info;
3992     highlight_info.x=composite_info.x-windows->image.x;
3993     highlight_info.y=composite_info.y-windows->image.y;
3994     XHighlightRectangle(display,windows->image.id,
3995       windows->image.highlight_context,&highlight_info);
3996     /*
3997       Wait for next event.
3998     */
3999     XScreenEvent(display,windows,&event,exception);
4000     XHighlightRectangle(display,windows->image.id,
4001       windows->image.highlight_context,&highlight_info);
4002     if (event.xany.window == windows->command.id)
4003       {
4004         /*
4005           Select a command from the Command widget.
4006         */
4007         id=XCommandWidget(display,windows,CompositeMenu,&event);
4008         if (id < 0)
4009           continue;
4010         switch (CompositeCommands[id])
4011         {
4012           case CompositeOperatorsCommand:
4013           {
4014             char
4015               command[MagickPathExtent],
4016               **operators;
4017 
4018             /*
4019               Select a command from the pop-up menu.
4020             */
4021             operators=GetCommandOptions(MagickComposeOptions);
4022             if (operators == (char **) NULL)
4023               break;
4024             entry=XMenuWidget(display,windows,CompositeMenu[id],
4025               (const char **) operators,command);
4026             if (entry >= 0)
4027               compose=(CompositeOperator) ParseCommandOption(
4028                 MagickComposeOptions,MagickFalse,operators[entry]);
4029             operators=DestroyStringList(operators);
4030             break;
4031           }
4032           case CompositeDissolveCommand:
4033           {
4034             static char
4035               factor[MagickPathExtent] = "20.0";
4036 
4037             /*
4038               Dissolve the two images a given percent.
4039             */
4040             (void) XSetFunction(display,windows->image.highlight_context,
4041               GXcopy);
4042             (void) XDialogWidget(display,windows,"Dissolve",
4043               "Enter the blend factor (0.0 - 99.9%):",factor);
4044             (void) XSetFunction(display,windows->image.highlight_context,
4045               GXinvert);
4046             if (*factor == '\0')
4047               break;
4048             blend=StringToDouble(factor,(char **) NULL);
4049             compose=DissolveCompositeOp;
4050             break;
4051           }
4052           case CompositeDisplaceCommand:
4053           {
4054             /*
4055               Get horizontal and vertical scale displacement geometry.
4056             */
4057             (void) XSetFunction(display,windows->image.highlight_context,
4058               GXcopy);
4059             (void) XDialogWidget(display,windows,"Displace",
4060               "Enter the horizontal and vertical scale:",displacement_geometry);
4061             (void) XSetFunction(display,windows->image.highlight_context,
4062               GXinvert);
4063             if (*displacement_geometry == '\0')
4064               break;
4065             compose=DisplaceCompositeOp;
4066             break;
4067           }
4068           case CompositeHelpCommand:
4069           {
4070             (void) XSetFunction(display,windows->image.highlight_context,
4071               GXcopy);
4072             XTextViewHelp(display,resource_info,windows,MagickFalse,
4073               "Help Viewer - Image Composite",ImageCompositeHelp);
4074             (void) XSetFunction(display,windows->image.highlight_context,
4075               GXinvert);
4076             break;
4077           }
4078           case CompositeDismissCommand:
4079           {
4080             /*
4081               Prematurely exit.
4082             */
4083             state|=EscapeState;
4084             state|=ExitState;
4085             break;
4086           }
4087           default:
4088             break;
4089         }
4090         continue;
4091       }
4092     switch (event.type)
4093     {
4094       case ButtonPress:
4095       {
4096         if (image->debug != MagickFalse)
4097           (void) LogMagickEvent(X11Event,GetMagickModule(),
4098             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4099             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4100         if (event.xbutton.button != Button1)
4101           break;
4102         if (event.xbutton.window != windows->image.id)
4103           break;
4104         /*
4105           Change cursor.
4106         */
4107         composite_info.width=composite_image->columns;
4108         composite_info.height=composite_image->rows;
4109         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4110         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4111         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4112         break;
4113       }
4114       case ButtonRelease:
4115       {
4116         if (image->debug != MagickFalse)
4117           (void) LogMagickEvent(X11Event,GetMagickModule(),
4118             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4119             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4120         if (event.xbutton.button != Button1)
4121           break;
4122         if (event.xbutton.window != windows->image.id)
4123           break;
4124         if ((composite_info.width != 0) && (composite_info.height != 0))
4125           {
4126             /*
4127               User has selected the location of the composite image.
4128             */
4129             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4130             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4131             state|=ExitState;
4132           }
4133         break;
4134       }
4135       case Expose:
4136         break;
4137       case KeyPress:
4138       {
4139         char
4140           command[MagickPathExtent];
4141 
4142         KeySym
4143           key_symbol;
4144 
4145         int
4146           length;
4147 
4148         if (event.xkey.window != windows->image.id)
4149           break;
4150         /*
4151           Respond to a user key press.
4152         */
4153         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4154           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4155         *(command+length)='\0';
4156         if (image->debug != MagickFalse)
4157           (void) LogMagickEvent(X11Event,GetMagickModule(),
4158             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4159         switch ((int) key_symbol)
4160         {
4161           case XK_Escape:
4162           case XK_F20:
4163           {
4164             /*
4165               Prematurely exit.
4166             */
4167             composite_image=DestroyImage(composite_image);
4168             state|=EscapeState;
4169             state|=ExitState;
4170             break;
4171           }
4172           case XK_F1:
4173           case XK_Help:
4174           {
4175             (void) XSetFunction(display,windows->image.highlight_context,
4176               GXcopy);
4177             XTextViewHelp(display,resource_info,windows,MagickFalse,
4178               "Help Viewer - Image Composite",ImageCompositeHelp);
4179             (void) XSetFunction(display,windows->image.highlight_context,
4180               GXinvert);
4181             break;
4182           }
4183           default:
4184           {
4185             (void) XBell(display,0);
4186             break;
4187           }
4188         }
4189         break;
4190       }
4191       case MotionNotify:
4192       {
4193         /*
4194           Map and unmap Info widget as text cursor crosses its boundaries.
4195         */
4196         x=event.xmotion.x;
4197         y=event.xmotion.y;
4198         if (windows->info.mapped != MagickFalse)
4199           {
4200             if ((x < (int) (windows->info.x+windows->info.width)) &&
4201                 (y < (int) (windows->info.y+windows->info.height)))
4202               (void) XWithdrawWindow(display,windows->info.id,
4203                 windows->info.screen);
4204           }
4205         else
4206           if ((x > (int) (windows->info.x+windows->info.width)) ||
4207               (y > (int) (windows->info.y+windows->info.height)))
4208             (void) XMapWindow(display,windows->info.id);
4209         composite_info.x=(ssize_t) windows->image.x+x;
4210         composite_info.y=(ssize_t) windows->image.y+y;
4211         break;
4212       }
4213       default:
4214       {
4215         if (image->debug != MagickFalse)
4216           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4217             event.type);
4218         break;
4219       }
4220     }
4221   } while ((state & ExitState) == 0);
4222   (void) XSelectInput(display,windows->image.id,
4223     windows->image.attributes.event_mask);
4224   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4225   XSetCursorState(display,windows,MagickFalse);
4226   (void) XFreeCursor(display,cursor);
4227   if ((state & EscapeState) != 0)
4228     return(MagickTrue);
4229   /*
4230     Image compositing is relative to image configuration.
4231   */
4232   XSetCursorState(display,windows,MagickTrue);
4233   XCheckRefreshWindows(display,windows);
4234   width=(unsigned int) image->columns;
4235   height=(unsigned int) image->rows;
4236   x=0;
4237   y=0;
4238   if (windows->image.crop_geometry != (char *) NULL)
4239     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4240   scale_factor=(double) width/windows->image.ximage->width;
4241   composite_info.x+=x;
4242   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4243   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4244   scale_factor=(double) height/windows->image.ximage->height;
4245   composite_info.y+=y;
4246   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4247   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4248   if ((composite_info.width != composite_image->columns) ||
4249       (composite_info.height != composite_image->rows))
4250     {
4251       Image
4252         *resize_image;
4253 
4254       /*
4255         Scale composite image.
4256       */
4257       resize_image=ResizeImage(composite_image,composite_info.width,
4258         composite_info.height,composite_image->filter,exception);
4259       composite_image=DestroyImage(composite_image);
4260       if (resize_image == (Image *) NULL)
4261         {
4262           XSetCursorState(display,windows,MagickFalse);
4263           return(MagickFalse);
4264         }
4265       composite_image=resize_image;
4266     }
4267   if (compose == DisplaceCompositeOp)
4268     (void) SetImageArtifact(composite_image,"compose:args",
4269       displacement_geometry);
4270   if (blend != 0.0)
4271     {
4272       CacheView
4273         *image_view;
4274 
4275       int
4276         y;
4277 
4278       Quantum
4279         opacity;
4280 
4281       int
4282         x;
4283 
4284       Quantum
4285         *q;
4286 
4287       /*
4288         Create mattes for blending.
4289       */
4290       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4291       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4292         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4293       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4294         return(MagickFalse);
4295       image->alpha_trait=BlendPixelTrait;
4296       image_view=AcquireAuthenticCacheView(image,exception);
4297       for (y=0; y < (int) image->rows; y++)
4298       {
4299         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4300           exception);
4301         if (q == (Quantum *) NULL)
4302           break;
4303         for (x=0; x < (int) image->columns; x++)
4304         {
4305           SetPixelAlpha(image,opacity,q);
4306           q+=GetPixelChannels(image);
4307         }
4308         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4309           break;
4310       }
4311       image_view=DestroyCacheView(image_view);
4312     }
4313   /*
4314     Composite image with X Image window.
4315   */
4316   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4317     composite_info.x,composite_info.y,exception);
4318   composite_image=DestroyImage(composite_image);
4319   XSetCursorState(display,windows,MagickFalse);
4320   /*
4321     Update image configuration.
4322   */
4323   XConfigureImageColormap(display,resource_info,windows,image,exception);
4324   (void) XConfigureImage(display,resource_info,windows,image,exception);
4325   return(MagickTrue);
4326 }
4327 
4328 /*
4329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4330 %                                                                             %
4331 %                                                                             %
4332 %                                                                             %
4333 +   X C o n f i g u r e I m a g e                                             %
4334 %                                                                             %
4335 %                                                                             %
4336 %                                                                             %
4337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4338 %
4339 %  XConfigureImage() creates a new X image.  It also notifies the window
4340 %  manager of the new image size and configures the transient widows.
4341 %
4342 %  The format of the XConfigureImage method is:
4343 %
4344 %      MagickBooleanType XConfigureImage(Display *display,
4345 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4346 %        ExceptionInfo *exception)
4347 %
4348 %  A description of each parameter follows:
4349 %
4350 %    o display: Specifies a connection to an X server; returned from
4351 %      XOpenDisplay.
4352 %
4353 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4354 %
4355 %    o windows: Specifies a pointer to a XWindows structure.
4356 %
4357 %    o image: the image.
4358 %
4359 %    o exception: return any errors or warnings in this structure.
4360 %
4361 %    o exception: return any errors or warnings in this structure.
4362 %
4363 */
XConfigureImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)4364 static MagickBooleanType XConfigureImage(Display *display,
4365   XResourceInfo *resource_info,XWindows *windows,Image *image,
4366   ExceptionInfo *exception)
4367 {
4368   char
4369     geometry[MagickPathExtent];
4370 
4371   MagickStatusType
4372     status;
4373 
4374   size_t
4375     mask,
4376     height,
4377     width;
4378 
4379   ssize_t
4380     x,
4381     y;
4382 
4383   XSizeHints
4384     *size_hints;
4385 
4386   XWindowChanges
4387     window_changes;
4388 
4389   /*
4390     Dismiss if window dimensions are zero.
4391   */
4392   width=(unsigned int) windows->image.window_changes.width;
4393   height=(unsigned int) windows->image.window_changes.height;
4394   if (image->debug != MagickFalse)
4395     (void) LogMagickEvent(X11Event,GetMagickModule(),
4396       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4397       windows->image.ximage->height,(double) width,(double) height);
4398   if ((width*height) == 0)
4399     return(MagickTrue);
4400   x=0;
4401   y=0;
4402   /*
4403     Resize image to fit Image window dimensions.
4404   */
4405   XSetCursorState(display,windows,MagickTrue);
4406   (void) XFlush(display);
4407   if (((int) width != windows->image.ximage->width) ||
4408       ((int) height != windows->image.ximage->height))
4409     image->taint=MagickTrue;
4410   windows->magnify.x=(int)
4411     width*windows->magnify.x/windows->image.ximage->width;
4412   windows->magnify.y=(int)
4413     height*windows->magnify.y/windows->image.ximage->height;
4414   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4415   windows->image.y=(int)
4416     (height*windows->image.y/windows->image.ximage->height);
4417   status=XMakeImage(display,resource_info,&windows->image,image,
4418     (unsigned int) width,(unsigned int) height,exception);
4419   if (status == MagickFalse)
4420     XNoticeWidget(display,windows,"Unable to configure X image:",
4421       windows->image.name);
4422   /*
4423     Notify window manager of the new configuration.
4424   */
4425   if (resource_info->image_geometry != (char *) NULL)
4426     (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4427       resource_info->image_geometry);
4428   else
4429     (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4430       XDisplayWidth(display,windows->image.screen),
4431       XDisplayHeight(display,windows->image.screen));
4432   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4433   window_changes.width=(int) width;
4434   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4435     window_changes.width=XDisplayWidth(display,windows->image.screen);
4436   window_changes.height=(int) height;
4437   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4438     window_changes.height=XDisplayHeight(display,windows->image.screen);
4439   mask=(size_t) (CWWidth | CWHeight);
4440   if (resource_info->backdrop)
4441     {
4442       mask|=CWX | CWY;
4443       window_changes.x=(int)
4444         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4445       window_changes.y=(int)
4446         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4447     }
4448   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4449     (unsigned int) mask,&window_changes);
4450   (void) XClearWindow(display,windows->image.id);
4451   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4452   /*
4453     Update Magnify window configuration.
4454   */
4455   if (windows->magnify.mapped != MagickFalse)
4456     XMakeMagnifyImage(display,windows,exception);
4457   windows->pan.crop_geometry=windows->image.crop_geometry;
4458   XBestIconSize(display,&windows->pan,image);
4459   while (((windows->pan.width << 1) < MaxIconSize) &&
4460          ((windows->pan.height << 1) < MaxIconSize))
4461   {
4462     windows->pan.width<<=1;
4463     windows->pan.height<<=1;
4464   }
4465   if (windows->pan.geometry != (char *) NULL)
4466     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4467       &windows->pan.width,&windows->pan.height);
4468   window_changes.width=(int) windows->pan.width;
4469   window_changes.height=(int) windows->pan.height;
4470   size_hints=XAllocSizeHints();
4471   if (size_hints != (XSizeHints *) NULL)
4472     {
4473       /*
4474         Set new size hints.
4475       */
4476       size_hints->flags=PSize | PMinSize | PMaxSize;
4477       size_hints->width=window_changes.width;
4478       size_hints->height=window_changes.height;
4479       size_hints->min_width=size_hints->width;
4480       size_hints->min_height=size_hints->height;
4481       size_hints->max_width=size_hints->width;
4482       size_hints->max_height=size_hints->height;
4483       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4484       (void) XFree((void *) size_hints);
4485     }
4486   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4487     (unsigned int) (CWWidth | CWHeight),&window_changes);
4488   /*
4489     Update icon window configuration.
4490   */
4491   windows->icon.crop_geometry=windows->image.crop_geometry;
4492   XBestIconSize(display,&windows->icon,image);
4493   window_changes.width=(int) windows->icon.width;
4494   window_changes.height=(int) windows->icon.height;
4495   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4496     (unsigned int) (CWWidth | CWHeight),&window_changes);
4497   XSetCursorState(display,windows,MagickFalse);
4498   return(status != 0 ? MagickTrue : MagickFalse);
4499 }
4500 
4501 /*
4502 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4503 %                                                                             %
4504 %                                                                             %
4505 %                                                                             %
4506 +   X C r o p I m a g e                                                       %
4507 %                                                                             %
4508 %                                                                             %
4509 %                                                                             %
4510 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4511 %
4512 %  XCropImage() allows the user to select a region of the image and crop, copy,
4513 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4514 %  the image with XPasteImage.
4515 %
4516 %  The format of the XCropImage method is:
4517 %
4518 %      MagickBooleanType XCropImage(Display *display,
4519 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4520 %        const ClipboardMode mode,ExceptionInfo *exception)
4521 %
4522 %  A description of each parameter follows:
4523 %
4524 %    o display: Specifies a connection to an X server; returned from
4525 %      XOpenDisplay.
4526 %
4527 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4528 %
4529 %    o windows: Specifies a pointer to a XWindows structure.
4530 %
4531 %    o image: the image; returned from ReadImage.
4532 %
4533 %    o mode: This unsigned value specified whether the image should be
4534 %      cropped, copied, or cut.
4535 %
4536 %    o exception: return any errors or warnings in this structure.
4537 %
4538 */
XCropImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,const ClipboardMode mode,ExceptionInfo * exception)4539 static MagickBooleanType XCropImage(Display *display,
4540   XResourceInfo *resource_info,XWindows *windows,Image *image,
4541   const ClipboardMode mode,ExceptionInfo *exception)
4542 {
4543   const char
4544     *const CropModeMenu[] =
4545     {
4546       "Help",
4547       "Dismiss",
4548       (char *) NULL
4549     },
4550     *RectifyModeMenu[] =
4551     {
4552       "Crop",
4553       "Help",
4554       "Dismiss",
4555       (char *) NULL
4556     };
4557 
4558   static const ModeType
4559     CropCommands[] =
4560     {
4561       CropHelpCommand,
4562       CropDismissCommand
4563     },
4564     RectifyCommands[] =
4565     {
4566       RectifyCopyCommand,
4567       RectifyHelpCommand,
4568       RectifyDismissCommand
4569     };
4570 
4571   CacheView
4572     *image_view;
4573 
4574   char
4575     command[MagickPathExtent],
4576     text[MagickPathExtent];
4577 
4578   Cursor
4579     cursor;
4580 
4581   int
4582     id,
4583     x,
4584     y;
4585 
4586   KeySym
4587     key_symbol;
4588 
4589   Image
4590     *crop_image;
4591 
4592   double
4593     scale_factor;
4594 
4595   RectangleInfo
4596     crop_info,
4597     highlight_info;
4598 
4599   Quantum
4600     *q;
4601 
4602   unsigned int
4603     height,
4604     width;
4605 
4606   size_t
4607     state;
4608 
4609   XEvent
4610     event;
4611 
4612   /*
4613     Map Command widget.
4614   */
4615   switch (mode)
4616   {
4617     case CopyMode:
4618     {
4619       (void) CloneString(&windows->command.name,"Copy");
4620       break;
4621     }
4622     case CropMode:
4623     {
4624       (void) CloneString(&windows->command.name,"Crop");
4625       break;
4626     }
4627     case CutMode:
4628     {
4629       (void) CloneString(&windows->command.name,"Cut");
4630       break;
4631     }
4632   }
4633   RectifyModeMenu[0]=windows->command.name;
4634   windows->command.data=0;
4635   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4636   (void) XMapRaised(display,windows->command.id);
4637   XClientMessage(display,windows->image.id,windows->im_protocols,
4638     windows->im_update_widget,CurrentTime);
4639   /*
4640     Track pointer until button 1 is pressed.
4641   */
4642   XQueryPosition(display,windows->image.id,&x,&y);
4643   (void) XSelectInput(display,windows->image.id,
4644     windows->image.attributes.event_mask | PointerMotionMask);
4645   crop_info.x=(ssize_t) windows->image.x+x;
4646   crop_info.y=(ssize_t) windows->image.y+y;
4647   crop_info.width=0;
4648   crop_info.height=0;
4649   cursor=XCreateFontCursor(display,XC_fleur);
4650   state=DefaultState;
4651   do
4652   {
4653     if (windows->info.mapped != MagickFalse)
4654       {
4655         /*
4656           Display pointer position.
4657         */
4658         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4659           (long) crop_info.x,(long) crop_info.y);
4660         XInfoWidget(display,windows,text);
4661       }
4662     /*
4663       Wait for next event.
4664     */
4665     XScreenEvent(display,windows,&event,exception);
4666     if (event.xany.window == windows->command.id)
4667       {
4668         /*
4669           Select a command from the Command widget.
4670         */
4671         id=XCommandWidget(display,windows,CropModeMenu,&event);
4672         if (id < 0)
4673           continue;
4674         switch (CropCommands[id])
4675         {
4676           case CropHelpCommand:
4677           {
4678             switch (mode)
4679             {
4680               case CopyMode:
4681               {
4682                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4683                   "Help Viewer - Image Copy",ImageCopyHelp);
4684                 break;
4685               }
4686               case CropMode:
4687               {
4688                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4689                   "Help Viewer - Image Crop",ImageCropHelp);
4690                 break;
4691               }
4692               case CutMode:
4693               {
4694                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4695                   "Help Viewer - Image Cut",ImageCutHelp);
4696                 break;
4697               }
4698             }
4699             break;
4700           }
4701           case CropDismissCommand:
4702           {
4703             /*
4704               Prematurely exit.
4705             */
4706             state|=EscapeState;
4707             state|=ExitState;
4708             break;
4709           }
4710           default:
4711             break;
4712         }
4713         continue;
4714       }
4715     switch (event.type)
4716     {
4717       case ButtonPress:
4718       {
4719         if (event.xbutton.button != Button1)
4720           break;
4721         if (event.xbutton.window != windows->image.id)
4722           break;
4723         /*
4724           Note first corner of cropping rectangle-- exit loop.
4725         */
4726         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4727         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4728         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4729         state|=ExitState;
4730         break;
4731       }
4732       case ButtonRelease:
4733         break;
4734       case Expose:
4735         break;
4736       case KeyPress:
4737       {
4738         if (event.xkey.window != windows->image.id)
4739           break;
4740         /*
4741           Respond to a user key press.
4742         */
4743         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4744           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4745         switch ((int) key_symbol)
4746         {
4747           case XK_Escape:
4748           case XK_F20:
4749           {
4750             /*
4751               Prematurely exit.
4752             */
4753             state|=EscapeState;
4754             state|=ExitState;
4755             break;
4756           }
4757           case XK_F1:
4758           case XK_Help:
4759           {
4760             switch (mode)
4761             {
4762               case CopyMode:
4763               {
4764                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4765                   "Help Viewer - Image Copy",ImageCopyHelp);
4766                 break;
4767               }
4768               case CropMode:
4769               {
4770                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4771                   "Help Viewer - Image Crop",ImageCropHelp);
4772                 break;
4773               }
4774               case CutMode:
4775               {
4776                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4777                   "Help Viewer - Image Cut",ImageCutHelp);
4778                 break;
4779               }
4780             }
4781             break;
4782           }
4783           default:
4784           {
4785             (void) XBell(display,0);
4786             break;
4787           }
4788         }
4789         break;
4790       }
4791       case MotionNotify:
4792       {
4793         if (event.xmotion.window != windows->image.id)
4794           break;
4795         /*
4796           Map and unmap Info widget as text cursor crosses its boundaries.
4797         */
4798         x=event.xmotion.x;
4799         y=event.xmotion.y;
4800         if (windows->info.mapped != MagickFalse)
4801           {
4802             if ((x < (int) (windows->info.x+windows->info.width)) &&
4803                 (y < (int) (windows->info.y+windows->info.height)))
4804               (void) XWithdrawWindow(display,windows->info.id,
4805                 windows->info.screen);
4806           }
4807         else
4808           if ((x > (int) (windows->info.x+windows->info.width)) ||
4809               (y > (int) (windows->info.y+windows->info.height)))
4810             (void) XMapWindow(display,windows->info.id);
4811         crop_info.x=(ssize_t) windows->image.x+x;
4812         crop_info.y=(ssize_t) windows->image.y+y;
4813         break;
4814       }
4815       default:
4816         break;
4817     }
4818   } while ((state & ExitState) == 0);
4819   (void) XSelectInput(display,windows->image.id,
4820     windows->image.attributes.event_mask);
4821   if ((state & EscapeState) != 0)
4822     {
4823       /*
4824         User want to exit without cropping.
4825       */
4826       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4827       (void) XFreeCursor(display,cursor);
4828       return(MagickTrue);
4829     }
4830   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4831   do
4832   {
4833     /*
4834       Size rectangle as pointer moves until the mouse button is released.
4835     */
4836     x=(int) crop_info.x;
4837     y=(int) crop_info.y;
4838     crop_info.width=0;
4839     crop_info.height=0;
4840     state=DefaultState;
4841     do
4842     {
4843       highlight_info=crop_info;
4844       highlight_info.x=crop_info.x-windows->image.x;
4845       highlight_info.y=crop_info.y-windows->image.y;
4846       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4847         {
4848           /*
4849             Display info and draw cropping rectangle.
4850           */
4851           if (windows->info.mapped == MagickFalse)
4852             (void) XMapWindow(display,windows->info.id);
4853           (void) FormatLocaleString(text,MagickPathExtent,
4854             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4855             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4856           XInfoWidget(display,windows,text);
4857           XHighlightRectangle(display,windows->image.id,
4858             windows->image.highlight_context,&highlight_info);
4859         }
4860       else
4861         if (windows->info.mapped != MagickFalse)
4862           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4863       /*
4864         Wait for next event.
4865       */
4866       XScreenEvent(display,windows,&event,exception);
4867       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4868         XHighlightRectangle(display,windows->image.id,
4869           windows->image.highlight_context,&highlight_info);
4870       switch (event.type)
4871       {
4872         case ButtonPress:
4873         {
4874           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4875           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4876           break;
4877         }
4878         case ButtonRelease:
4879         {
4880           /*
4881             User has committed to cropping rectangle.
4882           */
4883           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4884           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4885           XSetCursorState(display,windows,MagickFalse);
4886           state|=ExitState;
4887           windows->command.data=0;
4888           (void) XCommandWidget(display,windows,RectifyModeMenu,
4889             (XEvent *) NULL);
4890           break;
4891         }
4892         case Expose:
4893           break;
4894         case MotionNotify:
4895         {
4896           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4897           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4898         }
4899         default:
4900           break;
4901       }
4902       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4903           ((state & ExitState) != 0))
4904         {
4905           /*
4906             Check boundary conditions.
4907           */
4908           if (crop_info.x < 0)
4909             crop_info.x=0;
4910           else
4911             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4912               crop_info.x=(ssize_t) windows->image.ximage->width;
4913           if ((int) crop_info.x < x)
4914             crop_info.width=(unsigned int) (x-crop_info.x);
4915           else
4916             {
4917               crop_info.width=(unsigned int) (crop_info.x-x);
4918               crop_info.x=(ssize_t) x;
4919             }
4920           if (crop_info.y < 0)
4921             crop_info.y=0;
4922           else
4923             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4924               crop_info.y=(ssize_t) windows->image.ximage->height;
4925           if ((int) crop_info.y < y)
4926             crop_info.height=(unsigned int) (y-crop_info.y);
4927           else
4928             {
4929               crop_info.height=(unsigned int) (crop_info.y-y);
4930               crop_info.y=(ssize_t) y;
4931             }
4932         }
4933     } while ((state & ExitState) == 0);
4934     /*
4935       Wait for user to grab a corner of the rectangle or press return.
4936     */
4937     state=DefaultState;
4938     (void) XMapWindow(display,windows->info.id);
4939     do
4940     {
4941       if (windows->info.mapped != MagickFalse)
4942         {
4943           /*
4944             Display pointer position.
4945           */
4946           (void) FormatLocaleString(text,MagickPathExtent,
4947             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4948             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4949           XInfoWidget(display,windows,text);
4950         }
4951       highlight_info=crop_info;
4952       highlight_info.x=crop_info.x-windows->image.x;
4953       highlight_info.y=crop_info.y-windows->image.y;
4954       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4955         {
4956           state|=EscapeState;
4957           state|=ExitState;
4958           break;
4959         }
4960       XHighlightRectangle(display,windows->image.id,
4961         windows->image.highlight_context,&highlight_info);
4962       XScreenEvent(display,windows,&event,exception);
4963       if (event.xany.window == windows->command.id)
4964         {
4965           /*
4966             Select a command from the Command widget.
4967           */
4968           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4969           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4970           (void) XSetFunction(display,windows->image.highlight_context,
4971             GXinvert);
4972           XHighlightRectangle(display,windows->image.id,
4973             windows->image.highlight_context,&highlight_info);
4974           if (id >= 0)
4975             switch (RectifyCommands[id])
4976             {
4977               case RectifyCopyCommand:
4978               {
4979                 state|=ExitState;
4980                 break;
4981               }
4982               case RectifyHelpCommand:
4983               {
4984                 (void) XSetFunction(display,windows->image.highlight_context,
4985                   GXcopy);
4986                 switch (mode)
4987                 {
4988                   case CopyMode:
4989                   {
4990                     XTextViewHelp(display,resource_info,windows,MagickFalse,
4991                       "Help Viewer - Image Copy",ImageCopyHelp);
4992                     break;
4993                   }
4994                   case CropMode:
4995                   {
4996                     XTextViewHelp(display,resource_info,windows,MagickFalse,
4997                       "Help Viewer - Image Crop",ImageCropHelp);
4998                     break;
4999                   }
5000                   case CutMode:
5001                   {
5002                     XTextViewHelp(display,resource_info,windows,MagickFalse,
5003                       "Help Viewer - Image Cut",ImageCutHelp);
5004                     break;
5005                   }
5006                 }
5007                 (void) XSetFunction(display,windows->image.highlight_context,
5008                   GXinvert);
5009                 break;
5010               }
5011               case RectifyDismissCommand:
5012               {
5013                 /*
5014                   Prematurely exit.
5015                 */
5016                 state|=EscapeState;
5017                 state|=ExitState;
5018                 break;
5019               }
5020               default:
5021                 break;
5022             }
5023           continue;
5024         }
5025       XHighlightRectangle(display,windows->image.id,
5026         windows->image.highlight_context,&highlight_info);
5027       switch (event.type)
5028       {
5029         case ButtonPress:
5030         {
5031           if (event.xbutton.button != Button1)
5032             break;
5033           if (event.xbutton.window != windows->image.id)
5034             break;
5035           x=windows->image.x+event.xbutton.x;
5036           y=windows->image.y+event.xbutton.y;
5037           if ((x < (int) (crop_info.x+RoiDelta)) &&
5038               (x > (int) (crop_info.x-RoiDelta)) &&
5039               (y < (int) (crop_info.y+RoiDelta)) &&
5040               (y > (int) (crop_info.y-RoiDelta)))
5041             {
5042               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5043               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5044               state|=UpdateConfigurationState;
5045               break;
5046             }
5047           if ((x < (int) (crop_info.x+RoiDelta)) &&
5048               (x > (int) (crop_info.x-RoiDelta)) &&
5049               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5050               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5051             {
5052               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5053               state|=UpdateConfigurationState;
5054               break;
5055             }
5056           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5057               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5058               (y < (int) (crop_info.y+RoiDelta)) &&
5059               (y > (int) (crop_info.y-RoiDelta)))
5060             {
5061               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5062               state|=UpdateConfigurationState;
5063               break;
5064             }
5065           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5066               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5067               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5068               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5069             {
5070               state|=UpdateConfigurationState;
5071               break;
5072             }
5073         }
5074         case ButtonRelease:
5075         {
5076           if (event.xbutton.window == windows->pan.id)
5077             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5078                 (highlight_info.y != crop_info.y-windows->image.y))
5079               XHighlightRectangle(display,windows->image.id,
5080                 windows->image.highlight_context,&highlight_info);
5081           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5082             event.xbutton.time);
5083           break;
5084         }
5085         case Expose:
5086         {
5087           if (event.xexpose.window == windows->image.id)
5088             if (event.xexpose.count == 0)
5089               {
5090                 event.xexpose.x=(int) highlight_info.x;
5091                 event.xexpose.y=(int) highlight_info.y;
5092                 event.xexpose.width=(int) highlight_info.width;
5093                 event.xexpose.height=(int) highlight_info.height;
5094                 XRefreshWindow(display,&windows->image,&event);
5095               }
5096           if (event.xexpose.window == windows->info.id)
5097             if (event.xexpose.count == 0)
5098               XInfoWidget(display,windows,text);
5099           break;
5100         }
5101         case KeyPress:
5102         {
5103           if (event.xkey.window != windows->image.id)
5104             break;
5105           /*
5106             Respond to a user key press.
5107           */
5108           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5109             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5110           switch ((int) key_symbol)
5111           {
5112             case XK_Escape:
5113             case XK_F20:
5114               state|=EscapeState;
5115             case XK_Return:
5116             {
5117               state|=ExitState;
5118               break;
5119             }
5120             case XK_Home:
5121             case XK_KP_Home:
5122             {
5123               crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5124                 2L);
5125               crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5126                 2L);
5127               break;
5128             }
5129             case XK_Left:
5130             case XK_KP_Left:
5131             {
5132               crop_info.x--;
5133               break;
5134             }
5135             case XK_Up:
5136             case XK_KP_Up:
5137             case XK_Next:
5138             {
5139               crop_info.y--;
5140               break;
5141             }
5142             case XK_Right:
5143             case XK_KP_Right:
5144             {
5145               crop_info.x++;
5146               break;
5147             }
5148             case XK_Prior:
5149             case XK_Down:
5150             case XK_KP_Down:
5151             {
5152               crop_info.y++;
5153               break;
5154             }
5155             case XK_F1:
5156             case XK_Help:
5157             {
5158               (void) XSetFunction(display,windows->image.highlight_context,
5159                 GXcopy);
5160               switch (mode)
5161               {
5162                 case CopyMode:
5163                 {
5164                   XTextViewHelp(display,resource_info,windows,MagickFalse,
5165                     "Help Viewer - Image Copy",ImageCopyHelp);
5166                   break;
5167                 }
5168                 case CropMode:
5169                 {
5170                   XTextViewHelp(display,resource_info,windows,MagickFalse,
5171                     "Help Viewer - Image Cropg",ImageCropHelp);
5172                   break;
5173                 }
5174                 case CutMode:
5175                 {
5176                   XTextViewHelp(display,resource_info,windows,MagickFalse,
5177                     "Help Viewer - Image Cutg",ImageCutHelp);
5178                   break;
5179                 }
5180               }
5181               (void) XSetFunction(display,windows->image.highlight_context,
5182                 GXinvert);
5183               break;
5184             }
5185             default:
5186             {
5187               (void) XBell(display,0);
5188               break;
5189             }
5190           }
5191           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5192             event.xkey.time);
5193           break;
5194         }
5195         case KeyRelease:
5196           break;
5197         case MotionNotify:
5198         {
5199           if (event.xmotion.window != windows->image.id)
5200             break;
5201           /*
5202             Map and unmap Info widget as text cursor crosses its boundaries.
5203           */
5204           x=event.xmotion.x;
5205           y=event.xmotion.y;
5206           if (windows->info.mapped != MagickFalse)
5207             {
5208               if ((x < (int) (windows->info.x+windows->info.width)) &&
5209                   (y < (int) (windows->info.y+windows->info.height)))
5210                 (void) XWithdrawWindow(display,windows->info.id,
5211                   windows->info.screen);
5212             }
5213           else
5214             if ((x > (int) (windows->info.x+windows->info.width)) ||
5215                 (y > (int) (windows->info.y+windows->info.height)))
5216               (void) XMapWindow(display,windows->info.id);
5217           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5218           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5219           break;
5220         }
5221         case SelectionRequest:
5222         {
5223           XSelectionEvent
5224             notify;
5225 
5226           XSelectionRequestEvent
5227             *request;
5228 
5229           /*
5230             Set primary selection.
5231           */
5232           (void) FormatLocaleString(text,MagickPathExtent,
5233             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5234             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5235           request=(&(event.xselectionrequest));
5236           (void) XChangeProperty(request->display,request->requestor,
5237             request->property,request->target,8,PropModeReplace,
5238             (unsigned char *) text,(int) strlen(text));
5239           notify.type=SelectionNotify;
5240           notify.display=request->display;
5241           notify.requestor=request->requestor;
5242           notify.selection=request->selection;
5243           notify.target=request->target;
5244           notify.time=request->time;
5245           if (request->property == None)
5246             notify.property=request->target;
5247           else
5248             notify.property=request->property;
5249           (void) XSendEvent(request->display,request->requestor,False,0,
5250             (XEvent *) &notify);
5251         }
5252         default:
5253           break;
5254       }
5255       if ((state & UpdateConfigurationState) != 0)
5256         {
5257           (void) XPutBackEvent(display,&event);
5258           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5259           break;
5260         }
5261     } while ((state & ExitState) == 0);
5262   } while ((state & ExitState) == 0);
5263   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5264   XSetCursorState(display,windows,MagickFalse);
5265   if ((state & EscapeState) != 0)
5266     return(MagickTrue);
5267   if (mode == CropMode)
5268     if (((int) crop_info.width != windows->image.ximage->width) ||
5269         ((int) crop_info.height != windows->image.ximage->height))
5270       {
5271         /*
5272           Reconfigure Image window as defined by cropping rectangle.
5273         */
5274         XSetCropGeometry(display,windows,&crop_info,image);
5275         windows->image.window_changes.width=(int) crop_info.width;
5276         windows->image.window_changes.height=(int) crop_info.height;
5277         (void) XConfigureImage(display,resource_info,windows,image,exception);
5278         return(MagickTrue);
5279       }
5280   /*
5281     Copy image before applying image transforms.
5282   */
5283   XSetCursorState(display,windows,MagickTrue);
5284   XCheckRefreshWindows(display,windows);
5285   width=(unsigned int) image->columns;
5286   height=(unsigned int) image->rows;
5287   x=0;
5288   y=0;
5289   if (windows->image.crop_geometry != (char *) NULL)
5290     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5291   scale_factor=(double) width/windows->image.ximage->width;
5292   crop_info.x+=x;
5293   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5294   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5295   scale_factor=(double) height/windows->image.ximage->height;
5296   crop_info.y+=y;
5297   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5298   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5299   crop_info.x+=image->page.x;
5300   crop_info.y+=image->page.y;
5301   crop_image=CropImage(image,&crop_info,exception);
5302   XSetCursorState(display,windows,MagickFalse);
5303   if (crop_image == (Image *) NULL)
5304     return(MagickFalse);
5305   if (resource_info->copy_image != (Image *) NULL)
5306     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5307   resource_info->copy_image=crop_image;
5308   if (mode == CopyMode)
5309     {
5310       (void) XConfigureImage(display,resource_info,windows,image,exception);
5311       return(MagickTrue);
5312     }
5313   /*
5314     Cut image.
5315   */
5316   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5317     return(MagickFalse);
5318   image->alpha_trait=BlendPixelTrait;
5319   image_view=AcquireAuthenticCacheView(image,exception);
5320   for (y=0; y < (int) crop_info.height; y++)
5321   {
5322     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5323       crop_info.width,1,exception);
5324     if (q == (Quantum *) NULL)
5325       break;
5326     for (x=0; x < (int) crop_info.width; x++)
5327     {
5328       SetPixelAlpha(image,TransparentAlpha,q);
5329       q+=GetPixelChannels(image);
5330     }
5331     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5332       break;
5333   }
5334   image_view=DestroyCacheView(image_view);
5335   /*
5336     Update image configuration.
5337   */
5338   XConfigureImageColormap(display,resource_info,windows,image,exception);
5339   (void) XConfigureImage(display,resource_info,windows,image,exception);
5340   return(MagickTrue);
5341 }
5342 
5343 /*
5344 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5345 %                                                                             %
5346 %                                                                             %
5347 %                                                                             %
5348 +   X D r a w I m a g e                                                       %
5349 %                                                                             %
5350 %                                                                             %
5351 %                                                                             %
5352 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5353 %
5354 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5355 %  the image.
5356 %
5357 %  The format of the XDrawEditImage method is:
5358 %
5359 %      MagickBooleanType XDrawEditImage(Display *display,
5360 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5361 %        ExceptionInfo *exception)
5362 %
5363 %  A description of each parameter follows:
5364 %
5365 %    o display: Specifies a connection to an X server; returned from
5366 %      XOpenDisplay.
5367 %
5368 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5369 %
5370 %    o windows: Specifies a pointer to a XWindows structure.
5371 %
5372 %    o image: the image.
5373 %
5374 %    o exception: return any errors or warnings in this structure.
5375 %
5376 */
XDrawEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)5377 static MagickBooleanType XDrawEditImage(Display *display,
5378   XResourceInfo *resource_info,XWindows *windows,Image **image,
5379   ExceptionInfo *exception)
5380 {
5381   const char
5382     *const DrawMenu[] =
5383     {
5384       "Element",
5385       "Color",
5386       "Stipple",
5387       "Width",
5388       "Undo",
5389       "Help",
5390       "Dismiss",
5391       (char *) NULL
5392     };
5393 
5394   static ElementType
5395     element = PointElement;
5396 
5397   static const ModeType
5398     DrawCommands[] =
5399     {
5400       DrawElementCommand,
5401       DrawColorCommand,
5402       DrawStippleCommand,
5403       DrawWidthCommand,
5404       DrawUndoCommand,
5405       DrawHelpCommand,
5406       DrawDismissCommand
5407     };
5408 
5409   static Pixmap
5410     stipple = (Pixmap) NULL;
5411 
5412   static unsigned int
5413     pen_id = 0,
5414     line_width = 1;
5415 
5416   char
5417     command[MagickPathExtent],
5418     text[MagickPathExtent];
5419 
5420   Cursor
5421     cursor;
5422 
5423   int
5424     entry,
5425     id,
5426     number_coordinates,
5427     x,
5428     y;
5429 
5430   double
5431     degrees;
5432 
5433   MagickStatusType
5434     status;
5435 
5436   RectangleInfo
5437     rectangle_info;
5438 
5439   int
5440     i;
5441 
5442   unsigned int
5443     distance,
5444     height,
5445     max_coordinates,
5446     width;
5447 
5448   size_t
5449     state;
5450 
5451   Window
5452     root_window;
5453 
5454   XDrawInfo
5455     draw_info;
5456 
5457   XEvent
5458     event;
5459 
5460   XPoint
5461     *coordinate_info;
5462 
5463   XSegment
5464     line_info;
5465 
5466   /*
5467     Allocate polygon info.
5468   */
5469   max_coordinates=2048;
5470   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5471     sizeof(*coordinate_info));
5472   if (coordinate_info == (XPoint *) NULL)
5473     {
5474       (void) ThrowMagickException(exception,GetMagickModule(),
5475         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5476       return(MagickFalse);
5477     }
5478   /*
5479     Map Command widget.
5480   */
5481   (void) CloneString(&windows->command.name,"Draw");
5482   windows->command.data=4;
5483   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5484   (void) XMapRaised(display,windows->command.id);
5485   XClientMessage(display,windows->image.id,windows->im_protocols,
5486     windows->im_update_widget,CurrentTime);
5487   /*
5488     Wait for first button press.
5489   */
5490   root_window=XRootWindow(display,XDefaultScreen(display));
5491   draw_info.stencil=OpaqueStencil;
5492   status=MagickTrue;
5493   cursor=XCreateFontCursor(display,XC_tcross);
5494   for ( ; ; )
5495   {
5496     XQueryPosition(display,windows->image.id,&x,&y);
5497     (void) XSelectInput(display,windows->image.id,
5498       windows->image.attributes.event_mask | PointerMotionMask);
5499     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5500     state=DefaultState;
5501     do
5502     {
5503       if (windows->info.mapped != MagickFalse)
5504         {
5505           /*
5506             Display pointer position.
5507           */
5508           (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5509             x+windows->image.x,y+windows->image.y);
5510           XInfoWidget(display,windows,text);
5511         }
5512       /*
5513         Wait for next event.
5514       */
5515       XScreenEvent(display,windows,&event,exception);
5516       if (event.xany.window == windows->command.id)
5517         {
5518           /*
5519             Select a command from the Command widget.
5520           */
5521           id=XCommandWidget(display,windows,DrawMenu,&event);
5522           if (id < 0)
5523             continue;
5524           switch (DrawCommands[id])
5525           {
5526             case DrawElementCommand:
5527             {
5528               const char
5529                 *const Elements[] =
5530                 {
5531                   "point",
5532                   "line",
5533                   "rectangle",
5534                   "fill rectangle",
5535                   "circle",
5536                   "fill circle",
5537                   "ellipse",
5538                   "fill ellipse",
5539                   "polygon",
5540                   "fill polygon",
5541                   (char *) NULL,
5542                 };
5543 
5544               /*
5545                 Select a command from the pop-up menu.
5546               */
5547               element=(ElementType) (XMenuWidget(display,windows,
5548                 DrawMenu[id],Elements,command)+1);
5549               break;
5550             }
5551             case DrawColorCommand:
5552             {
5553               const char
5554                 *ColorMenu[MaxNumberPens+1];
5555 
5556               int
5557                 pen_number;
5558 
5559               MagickBooleanType
5560                 transparent;
5561 
5562               XColor
5563                 color;
5564 
5565               /*
5566                 Initialize menu selections.
5567               */
5568               for (i=0; i < (int) (MaxNumberPens-2); i++)
5569                 ColorMenu[i]=resource_info->pen_colors[i];
5570               ColorMenu[MaxNumberPens-2]="transparent";
5571               ColorMenu[MaxNumberPens-1]="Browser...";
5572               ColorMenu[MaxNumberPens]=(char *) NULL;
5573               /*
5574                 Select a pen color from the pop-up menu.
5575               */
5576               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5577                 (const char **) ColorMenu,command);
5578               if (pen_number < 0)
5579                 break;
5580               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5581                 MagickFalse;
5582               if (transparent != MagickFalse)
5583                 {
5584                   draw_info.stencil=TransparentStencil;
5585                   break;
5586                 }
5587               if (pen_number == (MaxNumberPens-1))
5588                 {
5589                   static char
5590                     color_name[MagickPathExtent] = "gray";
5591 
5592                   /*
5593                     Select a pen color from a dialog.
5594                   */
5595                   resource_info->pen_colors[pen_number]=color_name;
5596                   XColorBrowserWidget(display,windows,"Select",color_name);
5597                   if (*color_name == '\0')
5598                     break;
5599                 }
5600               /*
5601                 Set pen color.
5602               */
5603               (void) XParseColor(display,windows->map_info->colormap,
5604                 resource_info->pen_colors[pen_number],&color);
5605               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5606                 (unsigned int) MaxColors,&color);
5607               windows->pixel_info->pen_colors[pen_number]=color;
5608               pen_id=(unsigned int) pen_number;
5609               draw_info.stencil=OpaqueStencil;
5610               break;
5611             }
5612             case DrawStippleCommand:
5613             {
5614               const char
5615                 *StipplesMenu[] =
5616                 {
5617                   "Brick",
5618                   "Diagonal",
5619                   "Scales",
5620                   "Vertical",
5621                   "Wavy",
5622                   "Translucent",
5623                   "Opaque",
5624                   (char *) NULL,
5625                   (char *) NULL,
5626                 };
5627 
5628               Image
5629                 *stipple_image;
5630 
5631               ImageInfo
5632                 *image_info;
5633 
5634               int
5635                 status;
5636 
5637               static char
5638                 filename[MagickPathExtent] = "\0";
5639 
5640               /*
5641                 Select a command from the pop-up menu.
5642               */
5643               StipplesMenu[7]="Open...";
5644               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5645                 command);
5646               if (entry < 0)
5647                 break;
5648               if (stipple != (Pixmap) NULL)
5649                 (void) XFreePixmap(display,stipple);
5650               stipple=(Pixmap) NULL;
5651               if (entry != 7)
5652                 {
5653                   switch (entry)
5654                   {
5655                     case 0:
5656                     {
5657                       stipple=XCreateBitmapFromData(display,root_window,
5658                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5659                       break;
5660                     }
5661                     case 1:
5662                     {
5663                       stipple=XCreateBitmapFromData(display,root_window,
5664                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5665                       break;
5666                     }
5667                     case 2:
5668                     {
5669                       stipple=XCreateBitmapFromData(display,root_window,
5670                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5671                       break;
5672                     }
5673                     case 3:
5674                     {
5675                       stipple=XCreateBitmapFromData(display,root_window,
5676                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5677                       break;
5678                     }
5679                     case 4:
5680                     {
5681                       stipple=XCreateBitmapFromData(display,root_window,
5682                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5683                       break;
5684                     }
5685                     case 5:
5686                     {
5687                       stipple=XCreateBitmapFromData(display,root_window,
5688                         (char *) HighlightBitmap,HighlightWidth,
5689                         HighlightHeight);
5690                       break;
5691                     }
5692                     case 6:
5693                     default:
5694                     {
5695                       stipple=XCreateBitmapFromData(display,root_window,
5696                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5697                       break;
5698                     }
5699                   }
5700                   break;
5701                 }
5702               XFileBrowserWidget(display,windows,"Stipple",filename);
5703               if (*filename == '\0')
5704                 break;
5705               /*
5706                 Read image.
5707               */
5708               XSetCursorState(display,windows,MagickTrue);
5709               XCheckRefreshWindows(display,windows);
5710               image_info=AcquireImageInfo();
5711               (void) CopyMagickString(image_info->filename,filename,
5712                 MagickPathExtent);
5713               stipple_image=ReadImage(image_info,exception);
5714               CatchException(exception);
5715               XSetCursorState(display,windows,MagickFalse);
5716               if (stipple_image == (Image *) NULL)
5717                 break;
5718               (void) AcquireUniqueFileResource(filename);
5719               (void) FormatLocaleString(stipple_image->filename,
5720                 MagickPathExtent,"xbm:%s",filename);
5721               (void) WriteImage(image_info,stipple_image,exception);
5722               stipple_image=DestroyImage(stipple_image);
5723               image_info=DestroyImageInfo(image_info);
5724               status=XReadBitmapFile(display,root_window,filename,&width,
5725                 &height,&stipple,&x,&y);
5726               (void) RelinquishUniqueFileResource(filename);
5727               if ((status != BitmapSuccess) != 0)
5728                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5729                   filename);
5730               break;
5731             }
5732             case DrawWidthCommand:
5733             {
5734               const char
5735                 *const WidthsMenu[] =
5736                 {
5737                   "1",
5738                   "2",
5739                   "4",
5740                   "8",
5741                   "16",
5742                   "Dialog...",
5743                   (char *) NULL,
5744                 };
5745 
5746               static char
5747                 width[MagickPathExtent] = "0";
5748 
5749               /*
5750                 Select a command from the pop-up menu.
5751               */
5752               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5753                 command);
5754               if (entry < 0)
5755                 break;
5756               if (entry != 5)
5757                 {
5758                   line_width=(unsigned int) StringToUnsignedLong(
5759                     WidthsMenu[entry]);
5760                   break;
5761                 }
5762               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5763                 width);
5764               if (*width == '\0')
5765                 break;
5766               line_width=(unsigned int) StringToUnsignedLong(width);
5767               break;
5768             }
5769             case DrawUndoCommand:
5770             {
5771               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5772                 image,exception);
5773               break;
5774             }
5775             case DrawHelpCommand:
5776             {
5777               XTextViewHelp(display,resource_info,windows,MagickFalse,
5778                 "Help Viewer - Image Rotation",ImageDrawHelp);
5779               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5780               break;
5781             }
5782             case DrawDismissCommand:
5783             {
5784               /*
5785                 Prematurely exit.
5786               */
5787               state|=EscapeState;
5788               state|=ExitState;
5789               break;
5790             }
5791             default:
5792               break;
5793           }
5794           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5795           continue;
5796         }
5797       switch (event.type)
5798       {
5799         case ButtonPress:
5800         {
5801           if (event.xbutton.button != Button1)
5802             break;
5803           if (event.xbutton.window != windows->image.id)
5804             break;
5805           /*
5806             exit loop.
5807           */
5808           x=event.xbutton.x;
5809           y=event.xbutton.y;
5810           state|=ExitState;
5811           break;
5812         }
5813         case ButtonRelease:
5814           break;
5815         case Expose:
5816           break;
5817         case KeyPress:
5818         {
5819           KeySym
5820             key_symbol;
5821 
5822           if (event.xkey.window != windows->image.id)
5823             break;
5824           /*
5825             Respond to a user key press.
5826           */
5827           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5828             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5829           switch ((int) key_symbol)
5830           {
5831             case XK_Escape:
5832             case XK_F20:
5833             {
5834               /*
5835                 Prematurely exit.
5836               */
5837               state|=EscapeState;
5838               state|=ExitState;
5839               break;
5840             }
5841             case XK_F1:
5842             case XK_Help:
5843             {
5844               XTextViewHelp(display,resource_info,windows,MagickFalse,
5845                 "Help Viewer - Image Rotation",ImageDrawHelp);
5846               break;
5847             }
5848             default:
5849             {
5850               (void) XBell(display,0);
5851               break;
5852             }
5853           }
5854           break;
5855         }
5856         case MotionNotify:
5857         {
5858           /*
5859             Map and unmap Info widget as text cursor crosses its boundaries.
5860           */
5861           x=event.xmotion.x;
5862           y=event.xmotion.y;
5863           if (windows->info.mapped != MagickFalse)
5864             {
5865               if ((x < (int) (windows->info.x+windows->info.width)) &&
5866                   (y < (int) (windows->info.y+windows->info.height)))
5867                 (void) XWithdrawWindow(display,windows->info.id,
5868                   windows->info.screen);
5869             }
5870           else
5871             if ((x > (int) (windows->info.x+windows->info.width)) ||
5872                 (y > (int) (windows->info.y+windows->info.height)))
5873               (void) XMapWindow(display,windows->info.id);
5874           break;
5875         }
5876       }
5877     } while ((state & ExitState) == 0);
5878     (void) XSelectInput(display,windows->image.id,
5879       windows->image.attributes.event_mask);
5880     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5881     if ((state & EscapeState) != 0)
5882       break;
5883     /*
5884       Draw element as pointer moves until the button is released.
5885     */
5886     distance=0;
5887     degrees=0.0;
5888     line_info.x1=x;
5889     line_info.y1=y;
5890     line_info.x2=x;
5891     line_info.y2=y;
5892     rectangle_info.x=(ssize_t) x;
5893     rectangle_info.y=(ssize_t) y;
5894     rectangle_info.width=0;
5895     rectangle_info.height=0;
5896     number_coordinates=1;
5897     coordinate_info->x=x;
5898     coordinate_info->y=y;
5899     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5900     state=DefaultState;
5901     do
5902     {
5903       switch (element)
5904       {
5905         case PointElement:
5906         default:
5907         {
5908           if (number_coordinates > 1)
5909             {
5910               (void) XDrawLines(display,windows->image.id,
5911                 windows->image.highlight_context,coordinate_info,
5912                 number_coordinates,CoordModeOrigin);
5913               (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5914                 coordinate_info[number_coordinates-1].x,
5915                 coordinate_info[number_coordinates-1].y);
5916               XInfoWidget(display,windows,text);
5917             }
5918           break;
5919         }
5920         case LineElement:
5921         {
5922           if (distance > 9)
5923             {
5924               /*
5925                 Display angle of the line.
5926               */
5927               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5928                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5929               (void) FormatLocaleString(text,MagickPathExtent," %g",
5930                 (double) degrees);
5931               XInfoWidget(display,windows,text);
5932               XHighlightLine(display,windows->image.id,
5933                 windows->image.highlight_context,&line_info);
5934             }
5935           else
5936             if (windows->info.mapped != MagickFalse)
5937               (void) XWithdrawWindow(display,windows->info.id,
5938                 windows->info.screen);
5939           break;
5940         }
5941         case RectangleElement:
5942         case FillRectangleElement:
5943         {
5944           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5945             {
5946               /*
5947                 Display info and draw drawing rectangle.
5948               */
5949               (void) FormatLocaleString(text,MagickPathExtent,
5950                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5951                 (double) rectangle_info.height,(double) rectangle_info.x,
5952                 (double) rectangle_info.y);
5953               XInfoWidget(display,windows,text);
5954               XHighlightRectangle(display,windows->image.id,
5955                 windows->image.highlight_context,&rectangle_info);
5956             }
5957           else
5958             if (windows->info.mapped != MagickFalse)
5959               (void) XWithdrawWindow(display,windows->info.id,
5960                 windows->info.screen);
5961           break;
5962         }
5963         case CircleElement:
5964         case FillCircleElement:
5965         case EllipseElement:
5966         case FillEllipseElement:
5967         {
5968           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5969             {
5970               /*
5971                 Display info and draw drawing rectangle.
5972               */
5973               (void) FormatLocaleString(text,MagickPathExtent,
5974                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5975                 (double) rectangle_info.height,(double) rectangle_info.x,
5976                 (double) rectangle_info.y);
5977               XInfoWidget(display,windows,text);
5978               XHighlightEllipse(display,windows->image.id,
5979                 windows->image.highlight_context,&rectangle_info);
5980             }
5981           else
5982             if (windows->info.mapped != MagickFalse)
5983               (void) XWithdrawWindow(display,windows->info.id,
5984                 windows->info.screen);
5985           break;
5986         }
5987         case PolygonElement:
5988         case FillPolygonElement:
5989         {
5990           if (number_coordinates > 1)
5991             (void) XDrawLines(display,windows->image.id,
5992               windows->image.highlight_context,coordinate_info,
5993               number_coordinates,CoordModeOrigin);
5994           if (distance > 9)
5995             {
5996               /*
5997                 Display angle of the line.
5998               */
5999               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6000                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6001               (void) FormatLocaleString(text,MagickPathExtent," %g",
6002                 (double) degrees);
6003               XInfoWidget(display,windows,text);
6004               XHighlightLine(display,windows->image.id,
6005                 windows->image.highlight_context,&line_info);
6006             }
6007           else
6008             if (windows->info.mapped != MagickFalse)
6009               (void) XWithdrawWindow(display,windows->info.id,
6010                 windows->info.screen);
6011           break;
6012         }
6013       }
6014       /*
6015         Wait for next event.
6016       */
6017       XScreenEvent(display,windows,&event,exception);
6018       switch (element)
6019       {
6020         case PointElement:
6021         default:
6022         {
6023           if (number_coordinates > 1)
6024             (void) XDrawLines(display,windows->image.id,
6025               windows->image.highlight_context,coordinate_info,
6026               number_coordinates,CoordModeOrigin);
6027           break;
6028         }
6029         case LineElement:
6030         {
6031           if (distance > 9)
6032             XHighlightLine(display,windows->image.id,
6033               windows->image.highlight_context,&line_info);
6034           break;
6035         }
6036         case RectangleElement:
6037         case FillRectangleElement:
6038         {
6039           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6040             XHighlightRectangle(display,windows->image.id,
6041               windows->image.highlight_context,&rectangle_info);
6042           break;
6043         }
6044         case CircleElement:
6045         case FillCircleElement:
6046         case EllipseElement:
6047         case FillEllipseElement:
6048         {
6049           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6050             XHighlightEllipse(display,windows->image.id,
6051               windows->image.highlight_context,&rectangle_info);
6052           break;
6053         }
6054         case PolygonElement:
6055         case FillPolygonElement:
6056         {
6057           if (number_coordinates > 1)
6058             (void) XDrawLines(display,windows->image.id,
6059               windows->image.highlight_context,coordinate_info,
6060               number_coordinates,CoordModeOrigin);
6061           if (distance > 9)
6062             XHighlightLine(display,windows->image.id,
6063               windows->image.highlight_context,&line_info);
6064           break;
6065         }
6066       }
6067       switch (event.type)
6068       {
6069         case ButtonPress:
6070           break;
6071         case ButtonRelease:
6072         {
6073           /*
6074             User has committed to element.
6075           */
6076           line_info.x2=event.xbutton.x;
6077           line_info.y2=event.xbutton.y;
6078           rectangle_info.x=(ssize_t) event.xbutton.x;
6079           rectangle_info.y=(ssize_t) event.xbutton.y;
6080           coordinate_info[number_coordinates].x=event.xbutton.x;
6081           coordinate_info[number_coordinates].y=event.xbutton.y;
6082           if (((element != PolygonElement) &&
6083                (element != FillPolygonElement)) || (distance <= 9))
6084             {
6085               state|=ExitState;
6086               break;
6087             }
6088           number_coordinates++;
6089           if (number_coordinates < (int) max_coordinates)
6090             {
6091               line_info.x1=event.xbutton.x;
6092               line_info.y1=event.xbutton.y;
6093               break;
6094             }
6095           max_coordinates<<=1;
6096           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6097             max_coordinates,sizeof(*coordinate_info));
6098           if (coordinate_info == (XPoint *) NULL)
6099             (void) ThrowMagickException(exception,GetMagickModule(),
6100               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6101           break;
6102         }
6103         case Expose:
6104           break;
6105         case MotionNotify:
6106         {
6107           if (event.xmotion.window != windows->image.id)
6108             break;
6109           if (element != PointElement)
6110             {
6111               line_info.x2=event.xmotion.x;
6112               line_info.y2=event.xmotion.y;
6113               rectangle_info.x=(ssize_t) event.xmotion.x;
6114               rectangle_info.y=(ssize_t) event.xmotion.y;
6115               break;
6116             }
6117           coordinate_info[number_coordinates].x=event.xbutton.x;
6118           coordinate_info[number_coordinates].y=event.xbutton.y;
6119           number_coordinates++;
6120           if (number_coordinates < (int) max_coordinates)
6121             break;
6122           max_coordinates<<=1;
6123           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6124             max_coordinates,sizeof(*coordinate_info));
6125           if (coordinate_info == (XPoint *) NULL)
6126             (void) ThrowMagickException(exception,GetMagickModule(),
6127               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6128           break;
6129         }
6130         default:
6131           break;
6132       }
6133       /*
6134         Check boundary conditions.
6135       */
6136       if (line_info.x2 < 0)
6137         line_info.x2=0;
6138       else
6139         if (line_info.x2 > (int) windows->image.width)
6140           line_info.x2=(short) windows->image.width;
6141       if (line_info.y2 < 0)
6142         line_info.y2=0;
6143       else
6144         if (line_info.y2 > (int) windows->image.height)
6145           line_info.y2=(short) windows->image.height;
6146       distance=(unsigned int)
6147         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6148          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6149       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6150           ((state & ExitState) != 0))
6151         {
6152           if (rectangle_info.x < 0)
6153             rectangle_info.x=0;
6154           else
6155             if (rectangle_info.x > (ssize_t) windows->image.width)
6156               rectangle_info.x=(ssize_t) windows->image.width;
6157           if ((int) rectangle_info.x < x)
6158             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6159           else
6160             {
6161               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6162               rectangle_info.x=(ssize_t) x;
6163             }
6164           if (rectangle_info.y < 0)
6165             rectangle_info.y=0;
6166           else
6167             if (rectangle_info.y > (ssize_t) windows->image.height)
6168               rectangle_info.y=(ssize_t) windows->image.height;
6169           if ((int) rectangle_info.y < y)
6170             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6171           else
6172             {
6173               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6174               rectangle_info.y=(ssize_t) y;
6175             }
6176         }
6177     } while ((state & ExitState) == 0);
6178     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6179     if ((element == PointElement) || (element == PolygonElement) ||
6180         (element == FillPolygonElement))
6181       {
6182         /*
6183           Determine polygon bounding box.
6184         */
6185         rectangle_info.x=(ssize_t) coordinate_info->x;
6186         rectangle_info.y=(ssize_t) coordinate_info->y;
6187         x=coordinate_info->x;
6188         y=coordinate_info->y;
6189         for (i=1; i < number_coordinates; i++)
6190         {
6191           if (coordinate_info[i].x > x)
6192             x=coordinate_info[i].x;
6193           if (coordinate_info[i].y > y)
6194             y=coordinate_info[i].y;
6195           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6196             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6197           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6198             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6199         }
6200         rectangle_info.width=(size_t) (x-rectangle_info.x);
6201         rectangle_info.height=(size_t) (y-rectangle_info.y);
6202         for (i=0; i < number_coordinates; i++)
6203         {
6204           coordinate_info[i].x-=rectangle_info.x;
6205           coordinate_info[i].y-=rectangle_info.y;
6206         }
6207       }
6208     else
6209       if (distance <= 9)
6210         continue;
6211       else
6212         if ((element == RectangleElement) ||
6213             (element == CircleElement) || (element == EllipseElement))
6214           {
6215             rectangle_info.width--;
6216             rectangle_info.height--;
6217           }
6218     /*
6219       Drawing is relative to image configuration.
6220     */
6221     draw_info.x=(int) rectangle_info.x;
6222     draw_info.y=(int) rectangle_info.y;
6223     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6224       image,exception);
6225     width=(unsigned int) (*image)->columns;
6226     height=(unsigned int) (*image)->rows;
6227     x=0;
6228     y=0;
6229     if (windows->image.crop_geometry != (char *) NULL)
6230       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6231     draw_info.x+=windows->image.x-(line_width/2);
6232     if (draw_info.x < 0)
6233       draw_info.x=0;
6234     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6235     draw_info.y+=windows->image.y-(line_width/2);
6236     if (draw_info.y < 0)
6237       draw_info.y=0;
6238     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6239     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6240     if (draw_info.width > (unsigned int) (*image)->columns)
6241       draw_info.width=(unsigned int) (*image)->columns;
6242     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6243     if (draw_info.height > (unsigned int) (*image)->rows)
6244       draw_info.height=(unsigned int) (*image)->rows;
6245     (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6246       width*draw_info.width/windows->image.ximage->width,
6247       height*draw_info.height/windows->image.ximage->height,
6248       draw_info.x+x,draw_info.y+y);
6249     /*
6250       Initialize drawing attributes.
6251     */
6252     draw_info.degrees=0.0;
6253     draw_info.element=element;
6254     draw_info.stipple=stipple;
6255     draw_info.line_width=line_width;
6256     draw_info.line_info=line_info;
6257     if (line_info.x1 > (int) (line_width/2))
6258       draw_info.line_info.x1=(short) line_width/2;
6259     if (line_info.y1 > (int) (line_width/2))
6260       draw_info.line_info.y1=(short) line_width/2;
6261     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6262     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6263     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6264       {
6265         draw_info.line_info.x2=(-draw_info.line_info.x2);
6266         draw_info.line_info.y2=(-draw_info.line_info.y2);
6267       }
6268     if (draw_info.line_info.x2 < 0)
6269       {
6270         draw_info.line_info.x2=(-draw_info.line_info.x2);
6271         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6272       }
6273     if (draw_info.line_info.y2 < 0)
6274       {
6275         draw_info.line_info.y2=(-draw_info.line_info.y2);
6276         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6277       }
6278     draw_info.rectangle_info=rectangle_info;
6279     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6280       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6281     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6282       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6283     draw_info.number_coordinates=(unsigned int) number_coordinates;
6284     draw_info.coordinate_info=coordinate_info;
6285     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6286     /*
6287       Draw element on image.
6288     */
6289     XSetCursorState(display,windows,MagickTrue);
6290     XCheckRefreshWindows(display,windows);
6291     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6292     XSetCursorState(display,windows,MagickFalse);
6293     /*
6294       Update image colormap and return to image drawing.
6295     */
6296     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6297     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6298   }
6299   XSetCursorState(display,windows,MagickFalse);
6300   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6301   return(status != 0 ? MagickTrue : MagickFalse);
6302 }
6303 
6304 /*
6305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6306 %                                                                             %
6307 %                                                                             %
6308 %                                                                             %
6309 +   X D r a w P a n R e c t a n g l e                                         %
6310 %                                                                             %
6311 %                                                                             %
6312 %                                                                             %
6313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6314 %
6315 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6316 %  displays a zoom image and the rectangle shows which portion of the image is
6317 %  displayed in the Image window.
6318 %
6319 %  The format of the XDrawPanRectangle method is:
6320 %
6321 %      XDrawPanRectangle(Display *display,XWindows *windows)
6322 %
6323 %  A description of each parameter follows:
6324 %
6325 %    o display: Specifies a connection to an X server;  returned from
6326 %      XOpenDisplay.
6327 %
6328 %    o windows: Specifies a pointer to a XWindows structure.
6329 %
6330 */
XDrawPanRectangle(Display * display,XWindows * windows)6331 static void XDrawPanRectangle(Display *display,XWindows *windows)
6332 {
6333   double
6334     scale_factor;
6335 
6336   RectangleInfo
6337     highlight_info;
6338 
6339   /*
6340     Determine dimensions of the panning rectangle.
6341   */
6342   scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6343   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6344   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6345   scale_factor=(double)
6346     windows->pan.height/windows->image.ximage->height;
6347   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6348   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6349   /*
6350     Display the panning rectangle.
6351   */
6352   (void) XClearWindow(display,windows->pan.id);
6353   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6354     &highlight_info);
6355 }
6356 
6357 /*
6358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6359 %                                                                             %
6360 %                                                                             %
6361 %                                                                             %
6362 +   X I m a g e C a c h e                                                     %
6363 %                                                                             %
6364 %                                                                             %
6365 %                                                                             %
6366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6367 %
6368 %  XImageCache() handles the creation, manipulation, and destruction of the
6369 %  image cache (undo and redo buffers).
6370 %
6371 %  The format of the XImageCache method is:
6372 %
6373 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6374 %        XWindows *windows,const CommandType command,Image **image,
6375 %        ExceptionInfo *exception)
6376 %
6377 %  A description of each parameter follows:
6378 %
6379 %    o display: Specifies a connection to an X server; returned from
6380 %      XOpenDisplay.
6381 %
6382 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6383 %
6384 %    o windows: Specifies a pointer to a XWindows structure.
6385 %
6386 %    o command: Specifies a command to perform.
6387 %
6388 %    o image: the image;  XImageCache may transform the image and return a new
6389 %      image pointer.
6390 %
6391 %    o exception: return any errors or warnings in this structure.
6392 %
6393 */
XImageCache(Display * display,XResourceInfo * resource_info,XWindows * windows,const CommandType command,Image ** image,ExceptionInfo * exception)6394 static void XImageCache(Display *display,XResourceInfo *resource_info,
6395   XWindows *windows,const CommandType command,Image **image,
6396   ExceptionInfo *exception)
6397 {
6398   Image
6399     *cache_image;
6400 
6401   static Image
6402     *redo_image = (Image *) NULL,
6403     *undo_image = (Image *) NULL;
6404 
6405   switch (command)
6406   {
6407     case FreeBuffersCommand:
6408     {
6409       /*
6410         Free memory from the undo and redo cache.
6411       */
6412       while (undo_image != (Image *) NULL)
6413       {
6414         cache_image=undo_image;
6415         undo_image=GetPreviousImageInList(undo_image);
6416         cache_image->list=DestroyImage(cache_image->list);
6417         cache_image=DestroyImage(cache_image);
6418       }
6419       undo_image=NewImageList();
6420       if (redo_image != (Image *) NULL)
6421         redo_image=DestroyImage(redo_image);
6422       redo_image=NewImageList();
6423       return;
6424     }
6425     case UndoCommand:
6426     {
6427       char
6428         image_geometry[MagickPathExtent];
6429 
6430       /*
6431         Undo the last image transformation.
6432       */
6433       if (undo_image == (Image *) NULL)
6434         {
6435           (void) XBell(display,0);
6436           return;
6437         }
6438       cache_image=undo_image;
6439       undo_image=GetPreviousImageInList(undo_image);
6440       windows->image.window_changes.width=(int) cache_image->columns;
6441       windows->image.window_changes.height=(int) cache_image->rows;
6442       (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6443         windows->image.ximage->width,windows->image.ximage->height);
6444       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6445         exception);
6446       if (windows->image.crop_geometry != (char *) NULL)
6447         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6448           windows->image.crop_geometry);
6449       windows->image.crop_geometry=cache_image->geometry;
6450       if (redo_image != (Image *) NULL)
6451         redo_image=DestroyImage(redo_image);
6452       redo_image=(*image);
6453       *image=cache_image->list;
6454       cache_image=DestroyImage(cache_image);
6455       if (windows->image.orphan != MagickFalse)
6456         return;
6457       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6458       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6459       return;
6460     }
6461     case CutCommand:
6462     case PasteCommand:
6463     case ApplyCommand:
6464     case HalfSizeCommand:
6465     case OriginalSizeCommand:
6466     case DoubleSizeCommand:
6467     case ResizeCommand:
6468     case TrimCommand:
6469     case CropCommand:
6470     case ChopCommand:
6471     case FlipCommand:
6472     case FlopCommand:
6473     case RotateRightCommand:
6474     case RotateLeftCommand:
6475     case RotateCommand:
6476     case ShearCommand:
6477     case RollCommand:
6478     case NegateCommand:
6479     case ContrastStretchCommand:
6480     case SigmoidalContrastCommand:
6481     case NormalizeCommand:
6482     case EqualizeCommand:
6483     case HueCommand:
6484     case SaturationCommand:
6485     case BrightnessCommand:
6486     case GammaCommand:
6487     case SpiffCommand:
6488     case DullCommand:
6489     case GrayscaleCommand:
6490     case MapCommand:
6491     case QuantizeCommand:
6492     case DespeckleCommand:
6493     case EmbossCommand:
6494     case ReduceNoiseCommand:
6495     case AddNoiseCommand:
6496     case SharpenCommand:
6497     case BlurCommand:
6498     case ThresholdCommand:
6499     case EdgeDetectCommand:
6500     case SpreadCommand:
6501     case ShadeCommand:
6502     case RaiseCommand:
6503     case SegmentCommand:
6504     case SolarizeCommand:
6505     case SepiaToneCommand:
6506     case SwirlCommand:
6507     case ImplodeCommand:
6508     case VignetteCommand:
6509     case WaveCommand:
6510     case OilPaintCommand:
6511     case CharcoalDrawCommand:
6512     case AnnotateCommand:
6513     case AddBorderCommand:
6514     case AddFrameCommand:
6515     case CompositeCommand:
6516     case CommentCommand:
6517     case LaunchCommand:
6518     case RegionofInterestCommand:
6519     case SaveToUndoBufferCommand:
6520     case RedoCommand:
6521     {
6522       Image
6523         *previous_image;
6524 
6525       ssize_t
6526         bytes;
6527 
6528       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6529       if (undo_image != (Image *) NULL)
6530         {
6531           /*
6532             Ensure the undo cache has enough memory available.
6533           */
6534           previous_image=undo_image;
6535           while (previous_image != (Image *) NULL)
6536           {
6537             bytes+=previous_image->list->columns*previous_image->list->rows*
6538               sizeof(PixelInfo);
6539             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6540               {
6541                 previous_image=GetPreviousImageInList(previous_image);
6542                 continue;
6543               }
6544             bytes-=previous_image->list->columns*previous_image->list->rows*
6545               sizeof(PixelInfo);
6546             if (previous_image == undo_image)
6547               undo_image=NewImageList();
6548             else
6549               previous_image->next->previous=NewImageList();
6550             break;
6551           }
6552           while (previous_image != (Image *) NULL)
6553           {
6554             /*
6555               Delete any excess memory from undo cache.
6556             */
6557             cache_image=previous_image;
6558             previous_image=GetPreviousImageInList(previous_image);
6559             cache_image->list=DestroyImage(cache_image->list);
6560             cache_image=DestroyImage(cache_image);
6561           }
6562         }
6563       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6564         break;
6565       /*
6566         Save image before transformations are applied.
6567       */
6568       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6569       if (cache_image == (Image *) NULL)
6570         break;
6571       XSetCursorState(display,windows,MagickTrue);
6572       XCheckRefreshWindows(display,windows);
6573       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6574       XSetCursorState(display,windows,MagickFalse);
6575       if (cache_image->list == (Image *) NULL)
6576         {
6577           cache_image=DestroyImage(cache_image);
6578           break;
6579         }
6580       cache_image->columns=(size_t) windows->image.ximage->width;
6581       cache_image->rows=(size_t) windows->image.ximage->height;
6582       cache_image->geometry=windows->image.crop_geometry;
6583       if (windows->image.crop_geometry != (char *) NULL)
6584         {
6585           cache_image->geometry=AcquireString((char *) NULL);
6586           (void) CopyMagickString(cache_image->geometry,
6587             windows->image.crop_geometry,MagickPathExtent);
6588         }
6589       if (undo_image == (Image *) NULL)
6590         {
6591           undo_image=cache_image;
6592           break;
6593         }
6594       undo_image->next=cache_image;
6595       undo_image->next->previous=undo_image;
6596       undo_image=undo_image->next;
6597       break;
6598     }
6599     default:
6600       break;
6601   }
6602   if (command == RedoCommand)
6603     {
6604       /*
6605         Redo the last image transformation.
6606       */
6607       if (redo_image == (Image *) NULL)
6608         {
6609           (void) XBell(display,0);
6610           return;
6611         }
6612       windows->image.window_changes.width=(int) redo_image->columns;
6613       windows->image.window_changes.height=(int) redo_image->rows;
6614       if (windows->image.crop_geometry != (char *) NULL)
6615         windows->image.crop_geometry=(char *)
6616           RelinquishMagickMemory(windows->image.crop_geometry);
6617       windows->image.crop_geometry=redo_image->geometry;
6618       *image=DestroyImage(*image);
6619       *image=redo_image;
6620       redo_image=NewImageList();
6621       if (windows->image.orphan != MagickFalse)
6622         return;
6623       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6624       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6625       return;
6626     }
6627   if (command != InfoCommand)
6628     return;
6629   /*
6630     Display image info.
6631   */
6632   XSetCursorState(display,windows,MagickTrue);
6633   XCheckRefreshWindows(display,windows);
6634   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6635   XSetCursorState(display,windows,MagickFalse);
6636 }
6637 
6638 /*
6639 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6640 %                                                                             %
6641 %                                                                             %
6642 %                                                                             %
6643 +   X I m a g e W i n d o w C o m m a n d                                     %
6644 %                                                                             %
6645 %                                                                             %
6646 %                                                                             %
6647 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6648 %
6649 %  XImageWindowCommand() makes a transform to the image or Image window as
6650 %  specified by a user menu button or keyboard command.
6651 %
6652 %  The format of the XImageWindowCommand method is:
6653 %
6654 %      CommandType XImageWindowCommand(Display *display,
6655 %        XResourceInfo *resource_info,XWindows *windows,
6656 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6657 %        ExceptionInfo *exception)
6658 %
6659 %  A description of each parameter follows:
6660 %
6661 %    o nexus:  Method XImageWindowCommand returns an image when the
6662 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6663 %      image is returned.
6664 %
6665 %    o display: Specifies a connection to an X server; returned from
6666 %      XOpenDisplay.
6667 %
6668 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6669 %
6670 %    o windows: Specifies a pointer to a XWindows structure.
6671 %
6672 %    o state: key mask.
6673 %
6674 %    o key_symbol: Specifies a command to perform.
6675 %
6676 %    o image: the image;  XImageWIndowCommand may transform the image and
6677 %      return a new image pointer.
6678 %
6679 %    o exception: return any errors or warnings in this structure.
6680 %
6681 */
XImageWindowCommand(Display * display,XResourceInfo * resource_info,XWindows * windows,const MagickStatusType state,KeySym key_symbol,Image ** image,ExceptionInfo * exception)6682 static CommandType XImageWindowCommand(Display *display,
6683   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6684   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6685 {
6686   static char
6687     delta[MagickPathExtent] = "";
6688 
6689   static const char
6690     Digits[] = "01234567890";
6691 
6692   static KeySym
6693     last_symbol = XK_0;
6694 
6695   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6696     {
6697       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6698         {
6699           *delta='\0';
6700           resource_info->quantum=1;
6701         }
6702       last_symbol=key_symbol;
6703       delta[strlen(delta)+1]='\0';
6704       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6705       resource_info->quantum=StringToLong(delta);
6706       return(NullCommand);
6707     }
6708   last_symbol=key_symbol;
6709   if (resource_info->immutable)
6710     {
6711       /*
6712         Virtual image window has a restricted command set.
6713       */
6714       switch (key_symbol)
6715       {
6716         case XK_question:
6717           return(InfoCommand);
6718         case XK_p:
6719         case XK_Print:
6720           return(PrintCommand);
6721         case XK_space:
6722           return(NextCommand);
6723         case XK_q:
6724         case XK_Escape:
6725           return(QuitCommand);
6726         default:
6727           break;
6728       }
6729       return(NullCommand);
6730     }
6731   switch ((int) key_symbol)
6732   {
6733     case XK_o:
6734     {
6735       if ((state & ControlMask) == 0)
6736         break;
6737       return(OpenCommand);
6738     }
6739     case XK_space:
6740       return(NextCommand);
6741     case XK_BackSpace:
6742       return(FormerCommand);
6743     case XK_s:
6744     {
6745       if ((state & Mod1Mask) != 0)
6746         return(SwirlCommand);
6747       if ((state & ControlMask) == 0)
6748         return(ShearCommand);
6749       return(SaveCommand);
6750     }
6751     case XK_p:
6752     case XK_Print:
6753     {
6754       if ((state & Mod1Mask) != 0)
6755         return(OilPaintCommand);
6756       if ((state & Mod4Mask) != 0)
6757         return(ColorCommand);
6758       if ((state & ControlMask) == 0)
6759         return(NullCommand);
6760       return(PrintCommand);
6761     }
6762     case XK_d:
6763     {
6764       if ((state & Mod4Mask) != 0)
6765         return(DrawCommand);
6766       if ((state & ControlMask) == 0)
6767         return(NullCommand);
6768       return(DeleteCommand);
6769     }
6770     case XK_Select:
6771     {
6772       if ((state & ControlMask) == 0)
6773         return(NullCommand);
6774       return(SelectCommand);
6775     }
6776     case XK_n:
6777     {
6778       if ((state & ControlMask) == 0)
6779         return(NullCommand);
6780       return(NewCommand);
6781     }
6782     case XK_q:
6783     case XK_Escape:
6784       return(QuitCommand);
6785     case XK_z:
6786     case XK_Undo:
6787     {
6788       if ((state & ControlMask) == 0)
6789         return(NullCommand);
6790       return(UndoCommand);
6791     }
6792     case XK_r:
6793     case XK_Redo:
6794     {
6795       if ((state & ControlMask) == 0)
6796         return(RollCommand);
6797       return(RedoCommand);
6798     }
6799     case XK_x:
6800     {
6801       if ((state & ControlMask) == 0)
6802         return(NullCommand);
6803       return(CutCommand);
6804     }
6805     case XK_c:
6806     {
6807       if ((state & Mod1Mask) != 0)
6808         return(CharcoalDrawCommand);
6809       if ((state & ControlMask) == 0)
6810         return(CropCommand);
6811       return(CopyCommand);
6812     }
6813     case XK_v:
6814     case XK_Insert:
6815     {
6816       if ((state & Mod4Mask) != 0)
6817         return(CompositeCommand);
6818       if ((state & ControlMask) == 0)
6819         return(FlipCommand);
6820       return(PasteCommand);
6821     }
6822     case XK_less:
6823       return(HalfSizeCommand);
6824     case XK_minus:
6825       return(OriginalSizeCommand);
6826     case XK_greater:
6827       return(DoubleSizeCommand);
6828     case XK_percent:
6829       return(ResizeCommand);
6830     case XK_at:
6831       return(RefreshCommand);
6832     case XK_bracketleft:
6833       return(ChopCommand);
6834     case XK_h:
6835       return(FlopCommand);
6836     case XK_slash:
6837       return(RotateRightCommand);
6838     case XK_backslash:
6839       return(RotateLeftCommand);
6840     case XK_asterisk:
6841       return(RotateCommand);
6842     case XK_t:
6843       return(TrimCommand);
6844     case XK_H:
6845       return(HueCommand);
6846     case XK_S:
6847       return(SaturationCommand);
6848     case XK_L:
6849       return(BrightnessCommand);
6850     case XK_G:
6851       return(GammaCommand);
6852     case XK_C:
6853       return(SpiffCommand);
6854     case XK_Z:
6855       return(DullCommand);
6856     case XK_N:
6857       return(NormalizeCommand);
6858     case XK_equal:
6859       return(EqualizeCommand);
6860     case XK_asciitilde:
6861       return(NegateCommand);
6862     case XK_period:
6863       return(GrayscaleCommand);
6864     case XK_numbersign:
6865       return(QuantizeCommand);
6866     case XK_F2:
6867       return(DespeckleCommand);
6868     case XK_F3:
6869       return(EmbossCommand);
6870     case XK_F4:
6871       return(ReduceNoiseCommand);
6872     case XK_F5:
6873       return(AddNoiseCommand);
6874     case XK_F6:
6875       return(SharpenCommand);
6876     case XK_F7:
6877       return(BlurCommand);
6878     case XK_F8:
6879       return(ThresholdCommand);
6880     case XK_F9:
6881       return(EdgeDetectCommand);
6882     case XK_F10:
6883       return(SpreadCommand);
6884     case XK_F11:
6885       return(ShadeCommand);
6886     case XK_F12:
6887       return(RaiseCommand);
6888     case XK_F13:
6889       return(SegmentCommand);
6890     case XK_i:
6891     {
6892       if ((state & Mod1Mask) == 0)
6893         return(NullCommand);
6894       return(ImplodeCommand);
6895     }
6896     case XK_w:
6897     {
6898       if ((state & Mod1Mask) == 0)
6899         return(NullCommand);
6900       return(WaveCommand);
6901     }
6902     case XK_m:
6903     {
6904       if ((state & Mod4Mask) == 0)
6905         return(NullCommand);
6906       return(MatteCommand);
6907     }
6908     case XK_b:
6909     {
6910       if ((state & Mod4Mask) == 0)
6911         return(NullCommand);
6912       return(AddBorderCommand);
6913     }
6914     case XK_f:
6915     {
6916       if ((state & Mod4Mask) == 0)
6917         return(NullCommand);
6918       return(AddFrameCommand);
6919     }
6920     case XK_exclam:
6921     {
6922       if ((state & Mod4Mask) == 0)
6923         return(NullCommand);
6924       return(CommentCommand);
6925     }
6926     case XK_a:
6927     {
6928       if ((state & Mod1Mask) != 0)
6929         return(ApplyCommand);
6930       if ((state & Mod4Mask) != 0)
6931         return(AnnotateCommand);
6932       if ((state & ControlMask) == 0)
6933         return(NullCommand);
6934       return(RegionofInterestCommand);
6935     }
6936     case XK_question:
6937       return(InfoCommand);
6938     case XK_plus:
6939       return(ZoomCommand);
6940     case XK_P:
6941     {
6942       if ((state & ShiftMask) == 0)
6943         return(NullCommand);
6944       return(ShowPreviewCommand);
6945     }
6946     case XK_Execute:
6947       return(LaunchCommand);
6948     case XK_F1:
6949       return(HelpCommand);
6950     case XK_Find:
6951       return(BrowseDocumentationCommand);
6952     case XK_Menu:
6953     {
6954       (void) XMapRaised(display,windows->command.id);
6955       return(NullCommand);
6956     }
6957     case XK_Next:
6958     case XK_Prior:
6959     case XK_Home:
6960     case XK_KP_Home:
6961     {
6962       XTranslateImage(display,windows,*image,key_symbol);
6963       return(NullCommand);
6964     }
6965     case XK_Up:
6966     case XK_KP_Up:
6967     case XK_Down:
6968     case XK_KP_Down:
6969     case XK_Left:
6970     case XK_KP_Left:
6971     case XK_Right:
6972     case XK_KP_Right:
6973     {
6974       if ((state & Mod1Mask) != 0)
6975         {
6976           RectangleInfo
6977             crop_info;
6978 
6979           /*
6980             Trim one pixel from edge of image.
6981           */
6982           crop_info.x=0;
6983           crop_info.y=0;
6984           crop_info.width=(size_t) windows->image.ximage->width;
6985           crop_info.height=(size_t) windows->image.ximage->height;
6986           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6987             {
6988               if (resource_info->quantum >= (int) crop_info.height)
6989                 resource_info->quantum=(int) crop_info.height-1;
6990               crop_info.height-=resource_info->quantum;
6991             }
6992           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6993             {
6994               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6995                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6996               crop_info.y+=resource_info->quantum;
6997               crop_info.height-=resource_info->quantum;
6998             }
6999           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7000             {
7001               if (resource_info->quantum >= (int) crop_info.width)
7002                 resource_info->quantum=(int) crop_info.width-1;
7003               crop_info.width-=resource_info->quantum;
7004             }
7005           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7006             {
7007               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7008                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7009               crop_info.x+=resource_info->quantum;
7010               crop_info.width-=resource_info->quantum;
7011             }
7012           if ((int) (windows->image.x+windows->image.width) >
7013               (int) crop_info.width)
7014             windows->image.x=(int) (crop_info.width-windows->image.width);
7015           if ((int) (windows->image.y+windows->image.height) >
7016               (int) crop_info.height)
7017             windows->image.y=(int) (crop_info.height-windows->image.height);
7018           XSetCropGeometry(display,windows,&crop_info,*image);
7019           windows->image.window_changes.width=(int) crop_info.width;
7020           windows->image.window_changes.height=(int) crop_info.height;
7021           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7022           (void) XConfigureImage(display,resource_info,windows,*image,
7023             exception);
7024           return(NullCommand);
7025         }
7026       XTranslateImage(display,windows,*image,key_symbol);
7027       return(NullCommand);
7028     }
7029     default:
7030       return(NullCommand);
7031   }
7032   return(NullCommand);
7033 }
7034 
7035 /*
7036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7037 %                                                                             %
7038 %                                                                             %
7039 %                                                                             %
7040 +   X M a g i c k C o m m a n d                                               %
7041 %                                                                             %
7042 %                                                                             %
7043 %                                                                             %
7044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7045 %
7046 %  XMagickCommand() makes a transform to the image or Image window as
7047 %  specified by a user menu button or keyboard command.
7048 %
7049 %  The format of the XMagickCommand method is:
7050 %
7051 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7052 %        XWindows *windows,const CommandType command,Image **image,
7053 %        ExceptionInfo *exception)
7054 %
7055 %  A description of each parameter follows:
7056 %
7057 %    o display: Specifies a connection to an X server; returned from
7058 %      XOpenDisplay.
7059 %
7060 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7061 %
7062 %    o windows: Specifies a pointer to a XWindows structure.
7063 %
7064 %    o command: Specifies a command to perform.
7065 %
7066 %    o image: the image;  XMagickCommand may transform the image and return a
7067 %      new image pointer.
7068 %
7069 %    o exception: return any errors or warnings in this structure.
7070 %
7071 */
XMagickCommand(Display * display,XResourceInfo * resource_info,XWindows * windows,const CommandType command,Image ** image,ExceptionInfo * exception)7072 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7073   XWindows *windows,const CommandType command,Image **image,
7074   ExceptionInfo *exception)
7075 {
7076   char
7077     filename[MagickPathExtent],
7078     geometry[MagickPathExtent],
7079     modulate_factors[MagickPathExtent];
7080 
7081   GeometryInfo
7082     geometry_info;
7083 
7084   Image
7085     *nexus;
7086 
7087   ImageInfo
7088     *image_info;
7089 
7090   int
7091     x,
7092     y;
7093 
7094   MagickStatusType
7095     flags,
7096     status;
7097 
7098   QuantizeInfo
7099     quantize_info;
7100 
7101   RectangleInfo
7102     page_geometry;
7103 
7104   int
7105     i;
7106 
7107   static char
7108     color[MagickPathExtent] = "gray";
7109 
7110   unsigned int
7111     height,
7112     width;
7113 
7114   /*
7115     Process user command.
7116   */
7117   XCheckRefreshWindows(display,windows);
7118   XImageCache(display,resource_info,windows,command,image,exception);
7119   nexus=NewImageList();
7120   windows->image.window_changes.width=windows->image.ximage->width;
7121   windows->image.window_changes.height=windows->image.ximage->height;
7122   image_info=CloneImageInfo(resource_info->image_info);
7123   SetGeometryInfo(&geometry_info);
7124   GetQuantizeInfo(&quantize_info);
7125   switch (command)
7126   {
7127     case OpenCommand:
7128     {
7129       /*
7130         Load image.
7131       */
7132       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7133       break;
7134     }
7135     case NextCommand:
7136     {
7137       /*
7138         Display next image.
7139       */
7140       for (i=0; i < resource_info->quantum; i++)
7141         XClientMessage(display,windows->image.id,windows->im_protocols,
7142           windows->im_next_image,CurrentTime);
7143       break;
7144     }
7145     case FormerCommand:
7146     {
7147       /*
7148         Display former image.
7149       */
7150       for (i=0; i < resource_info->quantum; i++)
7151         XClientMessage(display,windows->image.id,windows->im_protocols,
7152           windows->im_former_image,CurrentTime);
7153       break;
7154     }
7155     case SelectCommand:
7156     {
7157       int
7158         status;
7159 
7160       /*
7161         Select image.
7162       */
7163       if (*resource_info->home_directory == '\0')
7164         (void) CopyMagickString(resource_info->home_directory,".",
7165           MagickPathExtent);
7166       status=chdir(resource_info->home_directory);
7167       if (status == -1)
7168         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7169           "UnableToOpenFile","%s",resource_info->home_directory);
7170       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7171       break;
7172     }
7173     case SaveCommand:
7174     {
7175       /*
7176         Save image.
7177       */
7178       status=XSaveImage(display,resource_info,windows,*image,exception);
7179       if (status == MagickFalse)
7180         {
7181           char
7182             message[MagickPathExtent];
7183 
7184           (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7185             exception->reason != (char *) NULL ? exception->reason : "",
7186             exception->description != (char *) NULL ? exception->description :
7187             "");
7188           XNoticeWidget(display,windows,"Unable to save file:",message);
7189           break;
7190         }
7191       break;
7192     }
7193     case PrintCommand:
7194     {
7195       /*
7196         Print image.
7197       */
7198       status=XPrintImage(display,resource_info,windows,*image,exception);
7199       if (status == MagickFalse)
7200         {
7201           char
7202             message[MagickPathExtent];
7203 
7204           (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7205             exception->reason != (char *) NULL ? exception->reason : "",
7206             exception->description != (char *) NULL ? exception->description :
7207             "");
7208           XNoticeWidget(display,windows,"Unable to print file:",message);
7209           break;
7210         }
7211       break;
7212     }
7213     case DeleteCommand:
7214     {
7215       static char
7216         filename[MagickPathExtent] = "\0";
7217 
7218       /*
7219         Delete image file.
7220       */
7221       XFileBrowserWidget(display,windows,"Delete",filename);
7222       if (*filename == '\0')
7223         break;
7224       status=ShredFile(filename);
7225       if (status != MagickFalse)
7226         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7227       break;
7228     }
7229     case NewCommand:
7230     {
7231       int
7232         status;
7233 
7234       static char
7235         color[MagickPathExtent] = "gray",
7236         geometry[MagickPathExtent] = "640x480";
7237 
7238       static const char
7239         *format = "gradient";
7240 
7241       /*
7242         Query user for canvas geometry.
7243       */
7244       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7245         geometry);
7246       if (*geometry == '\0')
7247         break;
7248       if (status == 0)
7249         format="xc";
7250       XColorBrowserWidget(display,windows,"Select",color);
7251       if (*color == '\0')
7252         break;
7253       /*
7254         Create canvas.
7255       */
7256       (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7257         "%s:%s",format,color);
7258       (void) CloneString(&image_info->size,geometry);
7259       nexus=ReadImage(image_info,exception);
7260       CatchException(exception);
7261       XClientMessage(display,windows->image.id,windows->im_protocols,
7262         windows->im_next_image,CurrentTime);
7263       break;
7264     }
7265     case VisualDirectoryCommand:
7266     {
7267       /*
7268         Visual Image directory.
7269       */
7270       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7271       break;
7272     }
7273     case QuitCommand:
7274     {
7275       /*
7276         exit program.
7277       */
7278       if (resource_info->confirm_exit == MagickFalse)
7279         XClientMessage(display,windows->image.id,windows->im_protocols,
7280           windows->im_exit,CurrentTime);
7281       else
7282         {
7283           int
7284             status;
7285 
7286           /*
7287             Confirm program exit.
7288           */
7289           status=XConfirmWidget(display,windows,"Do you really want to exit",
7290             resource_info->client_name);
7291           if (status > 0)
7292             XClientMessage(display,windows->image.id,windows->im_protocols,
7293               windows->im_exit,CurrentTime);
7294         }
7295       break;
7296     }
7297     case CutCommand:
7298     {
7299       /*
7300         Cut image.
7301       */
7302       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7303       break;
7304     }
7305     case CopyCommand:
7306     {
7307       /*
7308         Copy image.
7309       */
7310       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7311         exception);
7312       break;
7313     }
7314     case PasteCommand:
7315     {
7316       /*
7317         Paste image.
7318       */
7319       status=XPasteImage(display,resource_info,windows,*image,exception);
7320       if (status == MagickFalse)
7321         {
7322           XNoticeWidget(display,windows,"Unable to paste X image",
7323             (*image)->filename);
7324           break;
7325         }
7326       break;
7327     }
7328     case HalfSizeCommand:
7329     {
7330       /*
7331         Half image size.
7332       */
7333       windows->image.window_changes.width=windows->image.ximage->width/2;
7334       windows->image.window_changes.height=windows->image.ximage->height/2;
7335       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7336       break;
7337     }
7338     case OriginalSizeCommand:
7339     {
7340       /*
7341         Original image size.
7342       */
7343       windows->image.window_changes.width=(int) (*image)->columns;
7344       windows->image.window_changes.height=(int) (*image)->rows;
7345       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7346       break;
7347     }
7348     case DoubleSizeCommand:
7349     {
7350       /*
7351         Double the image size.
7352       */
7353       windows->image.window_changes.width=windows->image.ximage->width << 1;
7354       windows->image.window_changes.height=windows->image.ximage->height << 1;
7355       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7356       break;
7357     }
7358     case ResizeCommand:
7359     {
7360       int
7361         status;
7362 
7363       size_t
7364         height,
7365         width;
7366 
7367       ssize_t
7368         x,
7369         y;
7370 
7371       /*
7372         Resize image.
7373       */
7374       width=(size_t) windows->image.ximage->width;
7375       height=(size_t) windows->image.ximage->height;
7376       x=0;
7377       y=0;
7378       (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
7379         (double) width,(double) height);
7380       status=XDialogWidget(display,windows,"Resize",
7381         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7382       if (*geometry == '\0')
7383         break;
7384       if (status == 0)
7385         (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7386       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7387       windows->image.window_changes.width=(int) width;
7388       windows->image.window_changes.height=(int) height;
7389       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7390       break;
7391     }
7392     case ApplyCommand:
7393     {
7394       char
7395         image_geometry[MagickPathExtent];
7396 
7397       if ((windows->image.crop_geometry == (char *) NULL) &&
7398           ((int) (*image)->columns == windows->image.ximage->width) &&
7399           ((int) (*image)->rows == windows->image.ximage->height))
7400         break;
7401       /*
7402         Apply size transforms to image.
7403       */
7404       XSetCursorState(display,windows,MagickTrue);
7405       XCheckRefreshWindows(display,windows);
7406       /*
7407         Crop and/or scale displayed image.
7408       */
7409       (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7410         windows->image.ximage->width,windows->image.ximage->height);
7411       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7412         exception);
7413       if (windows->image.crop_geometry != (char *) NULL)
7414         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7415           windows->image.crop_geometry);
7416       windows->image.x=0;
7417       windows->image.y=0;
7418       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7419       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7420       break;
7421     }
7422     case RefreshCommand:
7423     {
7424       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7425       break;
7426     }
7427     case RestoreCommand:
7428     {
7429       /*
7430         Restore Image window to its original size.
7431       */
7432       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7433           (windows->image.height == (unsigned int) (*image)->rows) &&
7434           (windows->image.crop_geometry == (char *) NULL))
7435         {
7436           (void) XBell(display,0);
7437           break;
7438         }
7439       windows->image.window_changes.width=(int) (*image)->columns;
7440       windows->image.window_changes.height=(int) (*image)->rows;
7441       if (windows->image.crop_geometry != (char *) NULL)
7442         {
7443           windows->image.crop_geometry=(char *)
7444             RelinquishMagickMemory(windows->image.crop_geometry);
7445           windows->image.crop_geometry=(char *) NULL;
7446           windows->image.x=0;
7447           windows->image.y=0;
7448         }
7449       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7450       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7451       break;
7452     }
7453     case CropCommand:
7454     {
7455       /*
7456         Crop image.
7457       */
7458       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7459         exception);
7460       break;
7461     }
7462     case ChopCommand:
7463     {
7464       /*
7465         Chop image.
7466       */
7467       status=XChopImage(display,resource_info,windows,image,exception);
7468       if (status == MagickFalse)
7469         {
7470           XNoticeWidget(display,windows,"Unable to cut X image",
7471             (*image)->filename);
7472           break;
7473         }
7474       break;
7475     }
7476     case FlopCommand:
7477     {
7478       Image
7479         *flop_image;
7480 
7481       /*
7482         Flop image scanlines.
7483       */
7484       XSetCursorState(display,windows,MagickTrue);
7485       XCheckRefreshWindows(display,windows);
7486       flop_image=FlopImage(*image,exception);
7487       if (flop_image != (Image *) NULL)
7488         {
7489           *image=DestroyImage(*image);
7490           *image=flop_image;
7491         }
7492       CatchException(exception);
7493       XSetCursorState(display,windows,MagickFalse);
7494       if (windows->image.crop_geometry != (char *) NULL)
7495         {
7496           /*
7497             Flop crop geometry.
7498           */
7499           width=(unsigned int) (*image)->columns;
7500           height=(unsigned int) (*image)->rows;
7501           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7502             &width,&height);
7503           (void) FormatLocaleString(windows->image.crop_geometry,
7504             MagickPathExtent,"%ux%u%+d%+d",width,height,(int) (*image)->columns-
7505             (int) width-x,y);
7506         }
7507       if (windows->image.orphan != MagickFalse)
7508         break;
7509       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7510       break;
7511     }
7512     case FlipCommand:
7513     {
7514       Image
7515         *flip_image;
7516 
7517       /*
7518         Flip image scanlines.
7519       */
7520       XSetCursorState(display,windows,MagickTrue);
7521       XCheckRefreshWindows(display,windows);
7522       flip_image=FlipImage(*image,exception);
7523       if (flip_image != (Image *) NULL)
7524         {
7525           *image=DestroyImage(*image);
7526           *image=flip_image;
7527         }
7528       CatchException(exception);
7529       XSetCursorState(display,windows,MagickFalse);
7530       if (windows->image.crop_geometry != (char *) NULL)
7531         {
7532           /*
7533             Flip crop geometry.
7534           */
7535           width=(unsigned int) (*image)->columns;
7536           height=(unsigned int) (*image)->rows;
7537           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7538             &width,&height);
7539           (void) FormatLocaleString(windows->image.crop_geometry,
7540             MagickPathExtent,"%ux%u%+d%+d",width,height,x,(int) (*image)->rows-
7541             (int) height-y);
7542         }
7543       if (windows->image.orphan != MagickFalse)
7544         break;
7545       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7546       break;
7547     }
7548     case RotateRightCommand:
7549     {
7550       /*
7551         Rotate image 90 degrees clockwise.
7552       */
7553       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7554       if (status == MagickFalse)
7555         {
7556           XNoticeWidget(display,windows,"Unable to rotate X image",
7557             (*image)->filename);
7558           break;
7559         }
7560       break;
7561     }
7562     case RotateLeftCommand:
7563     {
7564       /*
7565         Rotate image 90 degrees counter-clockwise.
7566       */
7567       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7568       if (status == MagickFalse)
7569         {
7570           XNoticeWidget(display,windows,"Unable to rotate X image",
7571             (*image)->filename);
7572           break;
7573         }
7574       break;
7575     }
7576     case RotateCommand:
7577     {
7578       /*
7579         Rotate image.
7580       */
7581       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7582       if (status == MagickFalse)
7583         {
7584           XNoticeWidget(display,windows,"Unable to rotate X image",
7585             (*image)->filename);
7586           break;
7587         }
7588       break;
7589     }
7590     case ShearCommand:
7591     {
7592       Image
7593         *shear_image;
7594 
7595       static char
7596         geometry[MagickPathExtent] = "45.0x45.0";
7597 
7598       /*
7599         Query user for shear color and geometry.
7600       */
7601       XColorBrowserWidget(display,windows,"Select",color);
7602       if (*color == '\0')
7603         break;
7604       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7605         geometry);
7606       if (*geometry == '\0')
7607         break;
7608       /*
7609         Shear image.
7610       */
7611       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7612         exception);
7613       XSetCursorState(display,windows,MagickTrue);
7614       XCheckRefreshWindows(display,windows);
7615       (void) QueryColorCompliance(color,AllCompliance,
7616         &(*image)->background_color,exception);
7617       flags=ParseGeometry(geometry,&geometry_info);
7618       if ((flags & SigmaValue) == 0)
7619         geometry_info.sigma=geometry_info.rho;
7620       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7621         exception);
7622       if (shear_image != (Image *) NULL)
7623         {
7624           *image=DestroyImage(*image);
7625           *image=shear_image;
7626         }
7627       CatchException(exception);
7628       XSetCursorState(display,windows,MagickFalse);
7629       if (windows->image.orphan != MagickFalse)
7630         break;
7631       windows->image.window_changes.width=(int) (*image)->columns;
7632       windows->image.window_changes.height=(int) (*image)->rows;
7633       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7634       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7635       break;
7636     }
7637     case RollCommand:
7638     {
7639       Image
7640         *roll_image;
7641 
7642       static char
7643         geometry[MagickPathExtent] = "+2+2";
7644 
7645       /*
7646         Query user for the roll geometry.
7647       */
7648       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7649         geometry);
7650       if (*geometry == '\0')
7651         break;
7652       /*
7653         Roll image.
7654       */
7655       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7656         exception);
7657       XSetCursorState(display,windows,MagickTrue);
7658       XCheckRefreshWindows(display,windows);
7659       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7660         exception);
7661       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7662         exception);
7663       if (roll_image != (Image *) NULL)
7664         {
7665           *image=DestroyImage(*image);
7666           *image=roll_image;
7667         }
7668       CatchException(exception);
7669       XSetCursorState(display,windows,MagickFalse);
7670       if (windows->image.orphan != MagickFalse)
7671         break;
7672       windows->image.window_changes.width=(int) (*image)->columns;
7673       windows->image.window_changes.height=(int) (*image)->rows;
7674       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7675       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7676       break;
7677     }
7678     case TrimCommand:
7679     {
7680       static char
7681         fuzz[MagickPathExtent];
7682 
7683       /*
7684         Query user for the fuzz factor.
7685       */
7686       (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7687         (*image)->fuzz/(QuantumRange+1.0));
7688       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7689       if (*fuzz == '\0')
7690         break;
7691       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7692       /*
7693         Trim image.
7694       */
7695       status=XTrimImage(display,resource_info,windows,*image,exception);
7696       if (status == MagickFalse)
7697         {
7698           XNoticeWidget(display,windows,"Unable to trim X image",
7699             (*image)->filename);
7700           break;
7701         }
7702       break;
7703     }
7704     case HueCommand:
7705     {
7706       static char
7707         hue_percent[MagickPathExtent] = "110";
7708 
7709       /*
7710         Query user for percent hue change.
7711       */
7712       (void) XDialogWidget(display,windows,"Apply",
7713         "Enter percent change in image hue (0-200):",hue_percent);
7714       if (*hue_percent == '\0')
7715         break;
7716       /*
7717         Vary the image hue.
7718       */
7719       XSetCursorState(display,windows,MagickTrue);
7720       XCheckRefreshWindows(display,windows);
7721       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7722       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7723         MagickPathExtent);
7724       (void) ModulateImage(*image,modulate_factors,exception);
7725       XSetCursorState(display,windows,MagickFalse);
7726       if (windows->image.orphan != MagickFalse)
7727         break;
7728       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7729       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7730       break;
7731     }
7732     case SaturationCommand:
7733     {
7734       static char
7735         saturation_percent[MagickPathExtent] = "110";
7736 
7737       /*
7738         Query user for percent saturation change.
7739       */
7740       (void) XDialogWidget(display,windows,"Apply",
7741         "Enter percent change in color saturation (0-200):",saturation_percent);
7742       if (*saturation_percent == '\0')
7743         break;
7744       /*
7745         Vary color saturation.
7746       */
7747       XSetCursorState(display,windows,MagickTrue);
7748       XCheckRefreshWindows(display,windows);
7749       (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7750       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7751         MagickPathExtent);
7752       (void) ModulateImage(*image,modulate_factors,exception);
7753       XSetCursorState(display,windows,MagickFalse);
7754       if (windows->image.orphan != MagickFalse)
7755         break;
7756       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7757       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7758       break;
7759     }
7760     case BrightnessCommand:
7761     {
7762       static char
7763         brightness_percent[MagickPathExtent] = "110";
7764 
7765       /*
7766         Query user for percent brightness change.
7767       */
7768       (void) XDialogWidget(display,windows,"Apply",
7769         "Enter percent change in color brightness (0-200):",brightness_percent);
7770       if (*brightness_percent == '\0')
7771         break;
7772       /*
7773         Vary the color brightness.
7774       */
7775       XSetCursorState(display,windows,MagickTrue);
7776       XCheckRefreshWindows(display,windows);
7777       (void) CopyMagickString(modulate_factors,brightness_percent,
7778         MagickPathExtent);
7779       (void) ModulateImage(*image,modulate_factors,exception);
7780       XSetCursorState(display,windows,MagickFalse);
7781       if (windows->image.orphan != MagickFalse)
7782         break;
7783       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7784       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7785       break;
7786     }
7787     case GammaCommand:
7788     {
7789       static char
7790         factor[MagickPathExtent] = "1.6";
7791 
7792       /*
7793         Query user for gamma value.
7794       */
7795       (void) XDialogWidget(display,windows,"Gamma",
7796         "Enter gamma value (e.g. 1.2):",factor);
7797       if (*factor == '\0')
7798         break;
7799       /*
7800         Gamma correct image.
7801       */
7802       XSetCursorState(display,windows,MagickTrue);
7803       XCheckRefreshWindows(display,windows);
7804       (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7805       XSetCursorState(display,windows,MagickFalse);
7806       if (windows->image.orphan != MagickFalse)
7807         break;
7808       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7809       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7810       break;
7811     }
7812     case SpiffCommand:
7813     {
7814       /*
7815         Sharpen the image contrast.
7816       */
7817       XSetCursorState(display,windows,MagickTrue);
7818       XCheckRefreshWindows(display,windows);
7819       (void) ContrastImage(*image,MagickTrue,exception);
7820       XSetCursorState(display,windows,MagickFalse);
7821       if (windows->image.orphan != MagickFalse)
7822         break;
7823       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7824       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7825       break;
7826     }
7827     case DullCommand:
7828     {
7829       /*
7830         Dull the image contrast.
7831       */
7832       XSetCursorState(display,windows,MagickTrue);
7833       XCheckRefreshWindows(display,windows);
7834       (void) ContrastImage(*image,MagickFalse,exception);
7835       XSetCursorState(display,windows,MagickFalse);
7836       if (windows->image.orphan != MagickFalse)
7837         break;
7838       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7839       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7840       break;
7841     }
7842     case ContrastStretchCommand:
7843     {
7844       double
7845         black_point,
7846         white_point;
7847 
7848       static char
7849         levels[MagickPathExtent] = "1%";
7850 
7851       /*
7852         Query user for gamma value.
7853       */
7854       (void) XDialogWidget(display,windows,"Contrast Stretch",
7855         "Enter black and white points:",levels);
7856       if (*levels == '\0')
7857         break;
7858       /*
7859         Contrast stretch image.
7860       */
7861       XSetCursorState(display,windows,MagickTrue);
7862       XCheckRefreshWindows(display,windows);
7863       flags=ParseGeometry(levels,&geometry_info);
7864       black_point=geometry_info.rho;
7865       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7866       if ((flags & PercentValue) != 0)
7867         {
7868           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7869           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7870         }
7871       white_point=(double) (*image)->columns*(*image)->rows-white_point;
7872       (void) ContrastStretchImage(*image,black_point,white_point,
7873         exception);
7874       XSetCursorState(display,windows,MagickFalse);
7875       if (windows->image.orphan != MagickFalse)
7876         break;
7877       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7878       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7879       break;
7880     }
7881     case SigmoidalContrastCommand:
7882     {
7883       GeometryInfo
7884         geometry_info;
7885 
7886       MagickStatusType
7887         flags;
7888 
7889       static char
7890         levels[MagickPathExtent] = "3x50%";
7891 
7892       /*
7893         Query user for gamma value.
7894       */
7895       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7896         "Enter contrast and midpoint:",levels);
7897       if (*levels == '\0')
7898         break;
7899       /*
7900         Contrast stretch image.
7901       */
7902       XSetCursorState(display,windows,MagickTrue);
7903       XCheckRefreshWindows(display,windows);
7904       flags=ParseGeometry(levels,&geometry_info);
7905       if ((flags & SigmaValue) == 0)
7906         geometry_info.sigma=1.0*QuantumRange/2.0;
7907       if ((flags & PercentValue) != 0)
7908         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7909       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7910         geometry_info.sigma,exception);
7911       XSetCursorState(display,windows,MagickFalse);
7912       if (windows->image.orphan != MagickFalse)
7913         break;
7914       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7915       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7916       break;
7917     }
7918     case NormalizeCommand:
7919     {
7920       /*
7921         Perform histogram normalization on the image.
7922       */
7923       XSetCursorState(display,windows,MagickTrue);
7924       XCheckRefreshWindows(display,windows);
7925       (void) NormalizeImage(*image,exception);
7926       XSetCursorState(display,windows,MagickFalse);
7927       if (windows->image.orphan != MagickFalse)
7928         break;
7929       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7930       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7931       break;
7932     }
7933     case EqualizeCommand:
7934     {
7935       /*
7936         Perform histogram equalization on the image.
7937       */
7938       XSetCursorState(display,windows,MagickTrue);
7939       XCheckRefreshWindows(display,windows);
7940       (void) EqualizeImage(*image,exception);
7941       XSetCursorState(display,windows,MagickFalse);
7942       if (windows->image.orphan != MagickFalse)
7943         break;
7944       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7945       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7946       break;
7947     }
7948     case NegateCommand:
7949     {
7950       /*
7951         Negate colors in image.
7952       */
7953       XSetCursorState(display,windows,MagickTrue);
7954       XCheckRefreshWindows(display,windows);
7955       (void) NegateImage(*image,MagickFalse,exception);
7956       XSetCursorState(display,windows,MagickFalse);
7957       if (windows->image.orphan != MagickFalse)
7958         break;
7959       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7960       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7961       break;
7962     }
7963     case GrayscaleCommand:
7964     {
7965       /*
7966         Convert image to grayscale.
7967       */
7968       XSetCursorState(display,windows,MagickTrue);
7969       XCheckRefreshWindows(display,windows);
7970       (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7971         GrayscaleType : GrayscaleAlphaType,exception);
7972       XSetCursorState(display,windows,MagickFalse);
7973       if (windows->image.orphan != MagickFalse)
7974         break;
7975       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7976       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7977       break;
7978     }
7979     case MapCommand:
7980     {
7981       Image
7982         *affinity_image;
7983 
7984       static char
7985         filename[MagickPathExtent] = "\0";
7986 
7987       /*
7988         Request image file name from user.
7989       */
7990       XFileBrowserWidget(display,windows,"Map",filename);
7991       if (*filename == '\0')
7992         break;
7993       /*
7994         Map image.
7995       */
7996       XSetCursorState(display,windows,MagickTrue);
7997       XCheckRefreshWindows(display,windows);
7998       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
7999       affinity_image=ReadImage(image_info,exception);
8000       if (affinity_image != (Image *) NULL)
8001         {
8002           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8003           affinity_image=DestroyImage(affinity_image);
8004         }
8005       CatchException(exception);
8006       XSetCursorState(display,windows,MagickFalse);
8007       if (windows->image.orphan != MagickFalse)
8008         break;
8009       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8010       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8011       break;
8012     }
8013     case QuantizeCommand:
8014     {
8015       int
8016         status;
8017 
8018       static char
8019         colors[MagickPathExtent] = "256";
8020 
8021       /*
8022         Query user for maximum number of colors.
8023       */
8024       status=XDialogWidget(display,windows,"Quantize",
8025         "Maximum number of colors:",colors);
8026       if (*colors == '\0')
8027         break;
8028       /*
8029         Color reduce the image.
8030       */
8031       XSetCursorState(display,windows,MagickTrue);
8032       XCheckRefreshWindows(display,windows);
8033       quantize_info.number_colors=StringToUnsignedLong(colors);
8034       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8035         NoDitherMethod;
8036       (void) QuantizeImage(&quantize_info,*image,exception);
8037       XSetCursorState(display,windows,MagickFalse);
8038       if (windows->image.orphan != MagickFalse)
8039         break;
8040       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8041       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8042       break;
8043     }
8044     case DespeckleCommand:
8045     {
8046       Image
8047         *despeckle_image;
8048 
8049       /*
8050         Despeckle image.
8051       */
8052       XSetCursorState(display,windows,MagickTrue);
8053       XCheckRefreshWindows(display,windows);
8054       despeckle_image=DespeckleImage(*image,exception);
8055       if (despeckle_image != (Image *) NULL)
8056         {
8057           *image=DestroyImage(*image);
8058           *image=despeckle_image;
8059         }
8060       CatchException(exception);
8061       XSetCursorState(display,windows,MagickFalse);
8062       if (windows->image.orphan != MagickFalse)
8063         break;
8064       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8065       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8066       break;
8067     }
8068     case EmbossCommand:
8069     {
8070       Image
8071         *emboss_image;
8072 
8073       static char
8074         radius[MagickPathExtent] = "0.0x1.0";
8075 
8076       /*
8077         Query user for emboss radius.
8078       */
8079       (void) XDialogWidget(display,windows,"Emboss",
8080         "Enter the emboss radius and standard deviation:",radius);
8081       if (*radius == '\0')
8082         break;
8083       /*
8084         Reduce noise in the image.
8085       */
8086       XSetCursorState(display,windows,MagickTrue);
8087       XCheckRefreshWindows(display,windows);
8088       flags=ParseGeometry(radius,&geometry_info);
8089       if ((flags & SigmaValue) == 0)
8090         geometry_info.sigma=1.0;
8091       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8092         exception);
8093       if (emboss_image != (Image *) NULL)
8094         {
8095           *image=DestroyImage(*image);
8096           *image=emboss_image;
8097         }
8098       CatchException(exception);
8099       XSetCursorState(display,windows,MagickFalse);
8100       if (windows->image.orphan != MagickFalse)
8101         break;
8102       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8103       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8104       break;
8105     }
8106     case ReduceNoiseCommand:
8107     {
8108       Image
8109         *noise_image;
8110 
8111       static char
8112         radius[MagickPathExtent] = "0";
8113 
8114       /*
8115         Query user for noise radius.
8116       */
8117       (void) XDialogWidget(display,windows,"Reduce Noise",
8118         "Enter the noise radius:",radius);
8119       if (*radius == '\0')
8120         break;
8121       /*
8122         Reduce noise in the image.
8123       */
8124       XSetCursorState(display,windows,MagickTrue);
8125       XCheckRefreshWindows(display,windows);
8126       flags=ParseGeometry(radius,&geometry_info);
8127       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8128         geometry_info.rho,(size_t) geometry_info.rho,exception);
8129       if (noise_image != (Image *) NULL)
8130         {
8131           *image=DestroyImage(*image);
8132           *image=noise_image;
8133         }
8134       CatchException(exception);
8135       XSetCursorState(display,windows,MagickFalse);
8136       if (windows->image.orphan != MagickFalse)
8137         break;
8138       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8139       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8140       break;
8141     }
8142     case AddNoiseCommand:
8143     {
8144       char
8145         **noises;
8146 
8147       Image
8148         *noise_image;
8149 
8150       static char
8151         noise_type[MagickPathExtent] = "Gaussian";
8152 
8153       /*
8154         Add noise to the image.
8155       */
8156       noises=GetCommandOptions(MagickNoiseOptions);
8157       if (noises == (char **) NULL)
8158         break;
8159       XListBrowserWidget(display,windows,&windows->widget,
8160         (const char **) noises,"Add Noise",
8161         "Select a type of noise to add to your image:",noise_type);
8162       noises=DestroyStringList(noises);
8163       if (*noise_type == '\0')
8164         break;
8165       XSetCursorState(display,windows,MagickTrue);
8166       XCheckRefreshWindows(display,windows);
8167       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8168         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8169       if (noise_image != (Image *) NULL)
8170         {
8171           *image=DestroyImage(*image);
8172           *image=noise_image;
8173         }
8174       CatchException(exception);
8175       XSetCursorState(display,windows,MagickFalse);
8176       if (windows->image.orphan != MagickFalse)
8177         break;
8178       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8179       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8180       break;
8181     }
8182     case SharpenCommand:
8183     {
8184       Image
8185         *sharp_image;
8186 
8187       static char
8188         radius[MagickPathExtent] = "0.0x1.0";
8189 
8190       /*
8191         Query user for sharpen radius.
8192       */
8193       (void) XDialogWidget(display,windows,"Sharpen",
8194         "Enter the sharpen radius and standard deviation:",radius);
8195       if (*radius == '\0')
8196         break;
8197       /*
8198         Sharpen image scanlines.
8199       */
8200       XSetCursorState(display,windows,MagickTrue);
8201       XCheckRefreshWindows(display,windows);
8202       flags=ParseGeometry(radius,&geometry_info);
8203       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8204         exception);
8205       if (sharp_image != (Image *) NULL)
8206         {
8207           *image=DestroyImage(*image);
8208           *image=sharp_image;
8209         }
8210       CatchException(exception);
8211       XSetCursorState(display,windows,MagickFalse);
8212       if (windows->image.orphan != MagickFalse)
8213         break;
8214       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8215       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8216       break;
8217     }
8218     case BlurCommand:
8219     {
8220       Image
8221         *blur_image;
8222 
8223       static char
8224         radius[MagickPathExtent] = "0.0x1.0";
8225 
8226       /*
8227         Query user for blur radius.
8228       */
8229       (void) XDialogWidget(display,windows,"Blur",
8230         "Enter the blur radius and standard deviation:",radius);
8231       if (*radius == '\0')
8232         break;
8233       /*
8234         Blur an image.
8235       */
8236       XSetCursorState(display,windows,MagickTrue);
8237       XCheckRefreshWindows(display,windows);
8238       flags=ParseGeometry(radius,&geometry_info);
8239       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8240         exception);
8241       if (blur_image != (Image *) NULL)
8242         {
8243           *image=DestroyImage(*image);
8244           *image=blur_image;
8245         }
8246       CatchException(exception);
8247       XSetCursorState(display,windows,MagickFalse);
8248       if (windows->image.orphan != MagickFalse)
8249         break;
8250       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8251       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8252       break;
8253     }
8254     case ThresholdCommand:
8255     {
8256       double
8257         threshold;
8258 
8259       static char
8260         factor[MagickPathExtent] = "128";
8261 
8262       /*
8263         Query user for threshold value.
8264       */
8265       (void) XDialogWidget(display,windows,"Threshold",
8266         "Enter threshold value:",factor);
8267       if (*factor == '\0')
8268         break;
8269       /*
8270         Gamma correct image.
8271       */
8272       XSetCursorState(display,windows,MagickTrue);
8273       XCheckRefreshWindows(display,windows);
8274       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8275       (void) BilevelImage(*image,threshold,exception);
8276       XSetCursorState(display,windows,MagickFalse);
8277       if (windows->image.orphan != MagickFalse)
8278         break;
8279       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8280       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8281       break;
8282     }
8283     case EdgeDetectCommand:
8284     {
8285       Image
8286         *edge_image;
8287 
8288       static char
8289         radius[MagickPathExtent] = "0";
8290 
8291       /*
8292         Query user for edge factor.
8293       */
8294       (void) XDialogWidget(display,windows,"Detect Edges",
8295         "Enter the edge detect radius:",radius);
8296       if (*radius == '\0')
8297         break;
8298       /*
8299         Detect edge in image.
8300       */
8301       XSetCursorState(display,windows,MagickTrue);
8302       XCheckRefreshWindows(display,windows);
8303       flags=ParseGeometry(radius,&geometry_info);
8304       edge_image=EdgeImage(*image,geometry_info.rho,exception);
8305       if (edge_image != (Image *) NULL)
8306         {
8307           *image=DestroyImage(*image);
8308           *image=edge_image;
8309         }
8310       CatchException(exception);
8311       XSetCursorState(display,windows,MagickFalse);
8312       if (windows->image.orphan != MagickFalse)
8313         break;
8314       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8315       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8316       break;
8317     }
8318     case SpreadCommand:
8319     {
8320       Image
8321         *spread_image;
8322 
8323       static char
8324         amount[MagickPathExtent] = "2";
8325 
8326       /*
8327         Query user for spread amount.
8328       */
8329       (void) XDialogWidget(display,windows,"Spread",
8330         "Enter the displacement amount:",amount);
8331       if (*amount == '\0')
8332         break;
8333       /*
8334         Displace image pixels by a random amount.
8335       */
8336       XSetCursorState(display,windows,MagickTrue);
8337       XCheckRefreshWindows(display,windows);
8338       flags=ParseGeometry(amount,&geometry_info);
8339       spread_image=EdgeImage(*image,geometry_info.rho,exception);
8340       if (spread_image != (Image *) NULL)
8341         {
8342           *image=DestroyImage(*image);
8343           *image=spread_image;
8344         }
8345       CatchException(exception);
8346       XSetCursorState(display,windows,MagickFalse);
8347       if (windows->image.orphan != MagickFalse)
8348         break;
8349       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8350       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8351       break;
8352     }
8353     case ShadeCommand:
8354     {
8355       Image
8356         *shade_image;
8357 
8358       int
8359         status;
8360 
8361       static char
8362         geometry[MagickPathExtent] = "30x30";
8363 
8364       /*
8365         Query user for the shade geometry.
8366       */
8367       status=XDialogWidget(display,windows,"Shade",
8368         "Enter the azimuth and elevation of the light source:",geometry);
8369       if (*geometry == '\0')
8370         break;
8371       /*
8372         Shade image pixels.
8373       */
8374       XSetCursorState(display,windows,MagickTrue);
8375       XCheckRefreshWindows(display,windows);
8376       flags=ParseGeometry(geometry,&geometry_info);
8377       if ((flags & SigmaValue) == 0)
8378         geometry_info.sigma=1.0;
8379       shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
8380         geometry_info.rho,geometry_info.sigma,exception);
8381       if (shade_image != (Image *) NULL)
8382         {
8383           *image=DestroyImage(*image);
8384           *image=shade_image;
8385         }
8386       CatchException(exception);
8387       XSetCursorState(display,windows,MagickFalse);
8388       if (windows->image.orphan != MagickFalse)
8389         break;
8390       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8391       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8392       break;
8393     }
8394     case RaiseCommand:
8395     {
8396       static char
8397         bevel_width[MagickPathExtent] = "10";
8398 
8399       /*
8400         Query user for bevel width.
8401       */
8402       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8403       if (*bevel_width == '\0')
8404         break;
8405       /*
8406         Raise an image.
8407       */
8408       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8409         exception);
8410       XSetCursorState(display,windows,MagickTrue);
8411       XCheckRefreshWindows(display,windows);
8412       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8413         exception);
8414       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8415       XSetCursorState(display,windows,MagickFalse);
8416       if (windows->image.orphan != MagickFalse)
8417         break;
8418       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8419       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8420       break;
8421     }
8422     case SegmentCommand:
8423     {
8424       static char
8425         threshold[MagickPathExtent] = "1.0x1.5";
8426 
8427       /*
8428         Query user for smoothing threshold.
8429       */
8430       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8431         threshold);
8432       if (*threshold == '\0')
8433         break;
8434       /*
8435         Segment an image.
8436       */
8437       XSetCursorState(display,windows,MagickTrue);
8438       XCheckRefreshWindows(display,windows);
8439       flags=ParseGeometry(threshold,&geometry_info);
8440       if ((flags & SigmaValue) == 0)
8441         geometry_info.sigma=1.0;
8442       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8443         geometry_info.sigma,exception);
8444       XSetCursorState(display,windows,MagickFalse);
8445       if (windows->image.orphan != MagickFalse)
8446         break;
8447       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8448       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8449       break;
8450     }
8451     case SepiaToneCommand:
8452     {
8453       double
8454         threshold;
8455 
8456       Image
8457         *sepia_image;
8458 
8459       static char
8460         factor[MagickPathExtent] = "80%";
8461 
8462       /*
8463         Query user for sepia-tone factor.
8464       */
8465       (void) XDialogWidget(display,windows,"Sepia Tone",
8466         "Enter the sepia tone factor (0 - 99.9%):",factor);
8467       if (*factor == '\0')
8468         break;
8469       /*
8470         Sepia tone image pixels.
8471       */
8472       XSetCursorState(display,windows,MagickTrue);
8473       XCheckRefreshWindows(display,windows);
8474       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8475       sepia_image=SepiaToneImage(*image,threshold,exception);
8476       if (sepia_image != (Image *) NULL)
8477         {
8478           *image=DestroyImage(*image);
8479           *image=sepia_image;
8480         }
8481       CatchException(exception);
8482       XSetCursorState(display,windows,MagickFalse);
8483       if (windows->image.orphan != MagickFalse)
8484         break;
8485       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8486       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8487       break;
8488     }
8489     case SolarizeCommand:
8490     {
8491       double
8492         threshold;
8493 
8494       static char
8495         factor[MagickPathExtent] = "60%";
8496 
8497       /*
8498         Query user for solarize factor.
8499       */
8500       (void) XDialogWidget(display,windows,"Solarize",
8501         "Enter the solarize factor (0 - 99.9%):",factor);
8502       if (*factor == '\0')
8503         break;
8504       /*
8505         Solarize image pixels.
8506       */
8507       XSetCursorState(display,windows,MagickTrue);
8508       XCheckRefreshWindows(display,windows);
8509       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8510       (void) SolarizeImage(*image,threshold,exception);
8511       XSetCursorState(display,windows,MagickFalse);
8512       if (windows->image.orphan != MagickFalse)
8513         break;
8514       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8515       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8516       break;
8517     }
8518     case SwirlCommand:
8519     {
8520       Image
8521         *swirl_image;
8522 
8523       static char
8524         degrees[MagickPathExtent] = "60";
8525 
8526       /*
8527         Query user for swirl angle.
8528       */
8529       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8530         degrees);
8531       if (*degrees == '\0')
8532         break;
8533       /*
8534         Swirl image pixels about the center.
8535       */
8536       XSetCursorState(display,windows,MagickTrue);
8537       XCheckRefreshWindows(display,windows);
8538       flags=ParseGeometry(degrees,&geometry_info);
8539       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8540         exception);
8541       if (swirl_image != (Image *) NULL)
8542         {
8543           *image=DestroyImage(*image);
8544           *image=swirl_image;
8545         }
8546       CatchException(exception);
8547       XSetCursorState(display,windows,MagickFalse);
8548       if (windows->image.orphan != MagickFalse)
8549         break;
8550       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8551       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8552       break;
8553     }
8554     case ImplodeCommand:
8555     {
8556       Image
8557         *implode_image;
8558 
8559       static char
8560         factor[MagickPathExtent] = "0.3";
8561 
8562       /*
8563         Query user for implode factor.
8564       */
8565       (void) XDialogWidget(display,windows,"Implode",
8566         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8567       if (*factor == '\0')
8568         break;
8569       /*
8570         Implode image pixels about the center.
8571       */
8572       XSetCursorState(display,windows,MagickTrue);
8573       XCheckRefreshWindows(display,windows);
8574       flags=ParseGeometry(factor,&geometry_info);
8575       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8576         exception);
8577       if (implode_image != (Image *) NULL)
8578         {
8579           *image=DestroyImage(*image);
8580           *image=implode_image;
8581         }
8582       CatchException(exception);
8583       XSetCursorState(display,windows,MagickFalse);
8584       if (windows->image.orphan != MagickFalse)
8585         break;
8586       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8587       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8588       break;
8589     }
8590     case VignetteCommand:
8591     {
8592       Image
8593         *vignette_image;
8594 
8595       static char
8596         geometry[MagickPathExtent] = "0x20";
8597 
8598       /*
8599         Query user for the vignette geometry.
8600       */
8601       (void) XDialogWidget(display,windows,"Vignette",
8602         "Enter the radius, sigma, and x and y offsets:",geometry);
8603       if (*geometry == '\0')
8604         break;
8605       /*
8606         Soften the edges of the image in vignette style
8607       */
8608       XSetCursorState(display,windows,MagickTrue);
8609       XCheckRefreshWindows(display,windows);
8610       flags=ParseGeometry(geometry,&geometry_info);
8611       if ((flags & SigmaValue) == 0)
8612         geometry_info.sigma=1.0;
8613       if ((flags & XiValue) == 0)
8614         geometry_info.xi=0.1*(*image)->columns;
8615       if ((flags & PsiValue) == 0)
8616         geometry_info.psi=0.1*(*image)->rows;
8617       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8618         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8619         exception);
8620       if (vignette_image != (Image *) NULL)
8621         {
8622           *image=DestroyImage(*image);
8623           *image=vignette_image;
8624         }
8625       CatchException(exception);
8626       XSetCursorState(display,windows,MagickFalse);
8627       if (windows->image.orphan != MagickFalse)
8628         break;
8629       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8630       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8631       break;
8632     }
8633     case WaveCommand:
8634     {
8635       Image
8636         *wave_image;
8637 
8638       static char
8639         geometry[MagickPathExtent] = "25x150";
8640 
8641       /*
8642         Query user for the wave geometry.
8643       */
8644       (void) XDialogWidget(display,windows,"Wave",
8645         "Enter the amplitude and length of the wave:",geometry);
8646       if (*geometry == '\0')
8647         break;
8648       /*
8649         Alter an image along a sine wave.
8650       */
8651       XSetCursorState(display,windows,MagickTrue);
8652       XCheckRefreshWindows(display,windows);
8653       flags=ParseGeometry(geometry,&geometry_info);
8654       if ((flags & SigmaValue) == 0)
8655         geometry_info.sigma=1.0;
8656       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8657         (*image)->interpolate,exception);
8658       if (wave_image != (Image *) NULL)
8659         {
8660           *image=DestroyImage(*image);
8661           *image=wave_image;
8662         }
8663       CatchException(exception);
8664       XSetCursorState(display,windows,MagickFalse);
8665       if (windows->image.orphan != MagickFalse)
8666         break;
8667       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8668       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8669       break;
8670     }
8671     case OilPaintCommand:
8672     {
8673       Image
8674         *paint_image;
8675 
8676       static char
8677         radius[MagickPathExtent] = "0";
8678 
8679       /*
8680         Query user for circular neighborhood radius.
8681       */
8682       (void) XDialogWidget(display,windows,"Oil Paint",
8683         "Enter the mask radius:",radius);
8684       if (*radius == '\0')
8685         break;
8686       /*
8687         OilPaint image scanlines.
8688       */
8689       XSetCursorState(display,windows,MagickTrue);
8690       XCheckRefreshWindows(display,windows);
8691       flags=ParseGeometry(radius,&geometry_info);
8692       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8693         exception);
8694       if (paint_image != (Image *) NULL)
8695         {
8696           *image=DestroyImage(*image);
8697           *image=paint_image;
8698         }
8699       CatchException(exception);
8700       XSetCursorState(display,windows,MagickFalse);
8701       if (windows->image.orphan != MagickFalse)
8702         break;
8703       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8704       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8705       break;
8706     }
8707     case CharcoalDrawCommand:
8708     {
8709       Image
8710         *charcoal_image;
8711 
8712       static char
8713         radius[MagickPathExtent] = "0x1";
8714 
8715       /*
8716         Query user for charcoal radius.
8717       */
8718       (void) XDialogWidget(display,windows,"Charcoal Draw",
8719         "Enter the charcoal radius and sigma:",radius);
8720       if (*radius == '\0')
8721         break;
8722       /*
8723         Charcoal the image.
8724       */
8725       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8726         exception);
8727       XSetCursorState(display,windows,MagickTrue);
8728       XCheckRefreshWindows(display,windows);
8729       flags=ParseGeometry(radius,&geometry_info);
8730       if ((flags & SigmaValue) == 0)
8731         geometry_info.sigma=geometry_info.rho;
8732       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8733         exception);
8734       if (charcoal_image != (Image *) NULL)
8735         {
8736           *image=DestroyImage(*image);
8737           *image=charcoal_image;
8738         }
8739       CatchException(exception);
8740       XSetCursorState(display,windows,MagickFalse);
8741       if (windows->image.orphan != MagickFalse)
8742         break;
8743       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8744       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8745       break;
8746     }
8747     case AnnotateCommand:
8748     {
8749       /*
8750         Annotate the image with text.
8751       */
8752       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8753       if (status == MagickFalse)
8754         {
8755           XNoticeWidget(display,windows,"Unable to annotate X image",
8756             (*image)->filename);
8757           break;
8758         }
8759       break;
8760     }
8761     case DrawCommand:
8762     {
8763       /*
8764         Draw image.
8765       */
8766       status=XDrawEditImage(display,resource_info,windows,image,exception);
8767       if (status == MagickFalse)
8768         {
8769           XNoticeWidget(display,windows,"Unable to draw on the X image",
8770             (*image)->filename);
8771           break;
8772         }
8773       break;
8774     }
8775     case ColorCommand:
8776     {
8777       /*
8778         Color edit.
8779       */
8780       status=XColorEditImage(display,resource_info,windows,image,exception);
8781       if (status == MagickFalse)
8782         {
8783           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8784             (*image)->filename);
8785           break;
8786         }
8787       break;
8788     }
8789     case MatteCommand:
8790     {
8791       /*
8792         Matte edit.
8793       */
8794       status=XMatteEditImage(display,resource_info,windows,image,exception);
8795       if (status == MagickFalse)
8796         {
8797           XNoticeWidget(display,windows,"Unable to matte edit X image",
8798             (*image)->filename);
8799           break;
8800         }
8801       break;
8802     }
8803     case CompositeCommand:
8804     {
8805       /*
8806         Composite image.
8807       */
8808       status=XCompositeImage(display,resource_info,windows,*image,
8809         exception);
8810       if (status == MagickFalse)
8811         {
8812           XNoticeWidget(display,windows,"Unable to composite X image",
8813             (*image)->filename);
8814           break;
8815         }
8816       break;
8817     }
8818     case AddBorderCommand:
8819     {
8820       Image
8821         *border_image;
8822 
8823       static char
8824         geometry[MagickPathExtent] = "6x6";
8825 
8826       /*
8827         Query user for border color and geometry.
8828       */
8829       XColorBrowserWidget(display,windows,"Select",color);
8830       if (*color == '\0')
8831         break;
8832       (void) XDialogWidget(display,windows,"Add Border",
8833         "Enter border geometry:",geometry);
8834       if (*geometry == '\0')
8835         break;
8836       /*
8837         Add a border to the image.
8838       */
8839       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8840         exception);
8841       XSetCursorState(display,windows,MagickTrue);
8842       XCheckRefreshWindows(display,windows);
8843       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8844         exception);
8845       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8846         exception);
8847       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8848         exception);
8849       if (border_image != (Image *) NULL)
8850         {
8851           *image=DestroyImage(*image);
8852           *image=border_image;
8853         }
8854       CatchException(exception);
8855       XSetCursorState(display,windows,MagickFalse);
8856       if (windows->image.orphan != MagickFalse)
8857         break;
8858       windows->image.window_changes.width=(int) (*image)->columns;
8859       windows->image.window_changes.height=(int) (*image)->rows;
8860       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8861       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8862       break;
8863     }
8864     case AddFrameCommand:
8865     {
8866       FrameInfo
8867         frame_info;
8868 
8869       Image
8870         *frame_image;
8871 
8872       static char
8873         geometry[MagickPathExtent] = "6x6";
8874 
8875       /*
8876         Query user for frame color and geometry.
8877       */
8878       XColorBrowserWidget(display,windows,"Select",color);
8879       if (*color == '\0')
8880         break;
8881       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8882         geometry);
8883       if (*geometry == '\0')
8884         break;
8885       /*
8886         Surround image with an ornamental border.
8887       */
8888       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8889         exception);
8890       XSetCursorState(display,windows,MagickTrue);
8891       XCheckRefreshWindows(display,windows);
8892       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8893         exception);
8894       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8895         exception);
8896       frame_info.width=page_geometry.width;
8897       frame_info.height=page_geometry.height;
8898       frame_info.outer_bevel=page_geometry.x;
8899       frame_info.inner_bevel=page_geometry.y;
8900       frame_info.x=(ssize_t) frame_info.width;
8901       frame_info.y=(ssize_t) frame_info.height;
8902       frame_info.width=(*image)->columns+2*frame_info.width;
8903       frame_info.height=(*image)->rows+2*frame_info.height;
8904       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8905       if (frame_image != (Image *) NULL)
8906         {
8907           *image=DestroyImage(*image);
8908           *image=frame_image;
8909         }
8910       CatchException(exception);
8911       XSetCursorState(display,windows,MagickFalse);
8912       if (windows->image.orphan != MagickFalse)
8913         break;
8914       windows->image.window_changes.width=(int) (*image)->columns;
8915       windows->image.window_changes.height=(int) (*image)->rows;
8916       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8917       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8918       break;
8919     }
8920     case CommentCommand:
8921     {
8922       const char
8923         *value;
8924 
8925       FILE
8926         *file;
8927 
8928       int
8929         unique_file;
8930 
8931       /*
8932         Edit image comment.
8933       */
8934       unique_file=AcquireUniqueFileResource(image_info->filename);
8935       if (unique_file == -1)
8936         XNoticeWidget(display,windows,"Unable to edit image comment",
8937           image_info->filename);
8938       value=GetImageProperty(*image,"comment",exception);
8939       if (value == (char *) NULL)
8940         unique_file=close(unique_file)-1;
8941       else
8942         {
8943           const char
8944             *p;
8945 
8946           file=fdopen(unique_file,"w");
8947           if (file == (FILE *) NULL)
8948             {
8949               XNoticeWidget(display,windows,"Unable to edit image comment",
8950                 image_info->filename);
8951               break;
8952             }
8953           for (p=value; *p != '\0'; p++)
8954             (void) fputc((int) *p,file);
8955           (void) fputc('\n',file);
8956           (void) fclose(file);
8957         }
8958       XSetCursorState(display,windows,MagickTrue);
8959       XCheckRefreshWindows(display,windows);
8960       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8961         exception);
8962       if (status == MagickFalse)
8963         XNoticeWidget(display,windows,"Unable to edit image comment",
8964           (char *) NULL);
8965       else
8966         {
8967           char
8968             *comment;
8969 
8970           comment=FileToString(image_info->filename,~0UL,exception);
8971           if (comment != (char *) NULL)
8972             {
8973               (void) SetImageProperty(*image,"comment",comment,exception);
8974               (*image)->taint=MagickTrue;
8975             }
8976         }
8977       (void) RelinquishUniqueFileResource(image_info->filename);
8978       XSetCursorState(display,windows,MagickFalse);
8979       break;
8980     }
8981     case LaunchCommand:
8982     {
8983       /*
8984         Launch program.
8985       */
8986       XSetCursorState(display,windows,MagickTrue);
8987       XCheckRefreshWindows(display,windows);
8988       (void) AcquireUniqueFilename(filename);
8989       (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
8990         filename);
8991       status=WriteImage(image_info,*image,exception);
8992       if (status == MagickFalse)
8993         XNoticeWidget(display,windows,"Unable to launch image editor",
8994           (char *) NULL);
8995       else
8996         {
8997           nexus=ReadImage(resource_info->image_info,exception);
8998           CatchException(exception);
8999           XClientMessage(display,windows->image.id,windows->im_protocols,
9000             windows->im_next_image,CurrentTime);
9001         }
9002       (void) RelinquishUniqueFileResource(filename);
9003       XSetCursorState(display,windows,MagickFalse);
9004       break;
9005     }
9006     case RegionofInterestCommand:
9007     {
9008       /*
9009         Apply an image processing technique to a region of interest.
9010       */
9011       (void) XROIImage(display,resource_info,windows,image,exception);
9012       break;
9013     }
9014     case InfoCommand:
9015       break;
9016     case ZoomCommand:
9017     {
9018       /*
9019         Zoom image.
9020       */
9021       if (windows->magnify.mapped != MagickFalse)
9022         (void) XRaiseWindow(display,windows->magnify.id);
9023       else
9024         {
9025           /*
9026             Make magnify image.
9027           */
9028           XSetCursorState(display,windows,MagickTrue);
9029           (void) XMapRaised(display,windows->magnify.id);
9030           XSetCursorState(display,windows,MagickFalse);
9031         }
9032       break;
9033     }
9034     case ShowPreviewCommand:
9035     {
9036       char
9037         **previews;
9038 
9039       Image
9040         *preview_image;
9041 
9042       PreviewType
9043         preview;
9044 
9045       static char
9046         preview_type[MagickPathExtent] = "Gamma";
9047 
9048       /*
9049         Select preview type from menu.
9050       */
9051       previews=GetCommandOptions(MagickPreviewOptions);
9052       if (previews == (char **) NULL)
9053         break;
9054       XListBrowserWidget(display,windows,&windows->widget,
9055         (const char **) previews,"Preview",
9056         "Select an enhancement, effect, or F/X:",preview_type);
9057       previews=DestroyStringList(previews);
9058       if (*preview_type == '\0')
9059         break;
9060       /*
9061         Show image preview.
9062       */
9063       XSetCursorState(display,windows,MagickTrue);
9064       XCheckRefreshWindows(display,windows);
9065       preview=(PreviewType) ParseCommandOption(MagickPreviewOptions,
9066         MagickFalse,preview_type);
9067       (void) FormatImageProperty(*image,"group","%.20g",(double)
9068         windows->image.id);
9069       (void) DeleteImageProperty(*image,"label");
9070       (void) SetImageProperty(*image,"label","Preview",exception);
9071       preview_image=PreviewImage(*image,preview,exception);
9072       if (preview_image == (Image *) NULL)
9073         break;
9074       (void) AcquireUniqueFilename(filename);
9075       (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
9076         "show:%s",filename);
9077       status=WriteImage(image_info,preview_image,exception);
9078       (void) RelinquishUniqueFileResource(filename);
9079       preview_image=DestroyImage(preview_image);
9080       if (status == MagickFalse)
9081         XNoticeWidget(display,windows,"Unable to show image preview",
9082           (*image)->filename);
9083       XDelay(display,1500);
9084       XSetCursorState(display,windows,MagickFalse);
9085       break;
9086     }
9087     case ShowHistogramCommand:
9088     {
9089       Image
9090         *histogram_image;
9091 
9092       /*
9093         Show image histogram.
9094       */
9095       XSetCursorState(display,windows,MagickTrue);
9096       XCheckRefreshWindows(display,windows);
9097       (void) DeleteImageProperty(*image,"label");
9098       (void) FormatImageProperty(*image,"group","%.20g",(double)
9099         windows->image.id);
9100       (void) SetImageProperty(*image,"label","Histogram",exception);
9101       (void) AcquireUniqueFilename(filename);
9102       (void) FormatLocaleString((*image)->filename,MagickPathExtent,
9103         "histogram:%s",filename);
9104       status=WriteImage(image_info,*image,exception);
9105       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9106       histogram_image=ReadImage(image_info,exception);
9107       (void) RelinquishUniqueFileResource(filename);
9108       if (histogram_image == (Image *) NULL)
9109         break;
9110       (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9111         "show:%s",filename);
9112       status=WriteImage(image_info,histogram_image,exception);
9113       histogram_image=DestroyImage(histogram_image);
9114       if (status == MagickFalse)
9115         XNoticeWidget(display,windows,"Unable to show histogram",
9116           (*image)->filename);
9117       XDelay(display,1500);
9118       XSetCursorState(display,windows,MagickFalse);
9119       break;
9120     }
9121     case ShowMatteCommand:
9122     {
9123       Image
9124         *matte_image;
9125 
9126       if ((*image)->alpha_trait == UndefinedPixelTrait)
9127         {
9128           XNoticeWidget(display,windows,
9129             "Image does not have any matte information",(*image)->filename);
9130           break;
9131         }
9132       /*
9133         Show image matte.
9134       */
9135       XSetCursorState(display,windows,MagickTrue);
9136       XCheckRefreshWindows(display,windows);
9137       (void) FormatImageProperty(*image,"group","%.20g",(double)
9138         windows->image.id);
9139       (void) DeleteImageProperty(*image,"label");
9140       (void) SetImageProperty(*image,"label","Matte",exception);
9141       (void) AcquireUniqueFilename(filename);
9142       (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9143         filename);
9144       status=WriteImage(image_info,*image,exception);
9145       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9146       matte_image=ReadImage(image_info,exception);
9147       (void) RelinquishUniqueFileResource(filename);
9148       if (matte_image == (Image *) NULL)
9149         break;
9150       (void) FormatLocaleString(matte_image->filename,MagickPathExtent,
9151         "show:%s",filename);
9152       status=WriteImage(image_info,matte_image,exception);
9153       matte_image=DestroyImage(matte_image);
9154       if (status == MagickFalse)
9155         XNoticeWidget(display,windows,"Unable to show matte",
9156           (*image)->filename);
9157       XDelay(display,1500);
9158       XSetCursorState(display,windows,MagickFalse);
9159       break;
9160     }
9161     case BackgroundCommand:
9162     {
9163       /*
9164         Background image.
9165       */
9166       status=XBackgroundImage(display,resource_info,windows,image,exception);
9167       if (status == MagickFalse)
9168         break;
9169       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9170       if (nexus != (Image *) NULL)
9171         XClientMessage(display,windows->image.id,windows->im_protocols,
9172           windows->im_next_image,CurrentTime);
9173       break;
9174     }
9175     case SlideShowCommand:
9176     {
9177       static char
9178         delay[MagickPathExtent] = "5";
9179 
9180       /*
9181         Display next image after pausing.
9182       */
9183       (void) XDialogWidget(display,windows,"Slide Show",
9184         "Pause how many 1/100ths of a second between images:",delay);
9185       if (*delay == '\0')
9186         break;
9187       resource_info->delay=StringToUnsignedLong(delay);
9188       XClientMessage(display,windows->image.id,windows->im_protocols,
9189         windows->im_next_image,CurrentTime);
9190       break;
9191     }
9192     case PreferencesCommand:
9193     {
9194       /*
9195         Set user preferences.
9196       */
9197       status=XPreferencesWidget(display,resource_info,windows);
9198       if (status == MagickFalse)
9199         break;
9200       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9201       if (nexus != (Image *) NULL)
9202         XClientMessage(display,windows->image.id,windows->im_protocols,
9203           windows->im_next_image,CurrentTime);
9204       break;
9205     }
9206     case HelpCommand:
9207     {
9208       /*
9209         User requested help.
9210       */
9211       XTextViewHelp(display,resource_info,windows,MagickFalse,
9212         "Help Viewer - Display",DisplayHelp);
9213       break;
9214     }
9215     case BrowseDocumentationCommand:
9216     {
9217       Atom
9218         mozilla_atom;
9219 
9220       Window
9221         mozilla_window,
9222         root_window;
9223 
9224       /*
9225         Browse the ImageMagick documentation.
9226       */
9227       root_window=XRootWindow(display,XDefaultScreen(display));
9228       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9229       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9230       if (mozilla_window != (Window) NULL)
9231         {
9232           char
9233             command[MagickPathExtent];
9234 
9235           /*
9236             Display documentation using Netscape remote control.
9237           */
9238           (void) FormatLocaleString(command,MagickPathExtent,
9239             "openurl(%s,new-tab)",MagickAuthoritativeURL);
9240           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9241           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9242             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9243           XSetCursorState(display,windows,MagickFalse);
9244           break;
9245         }
9246       XSetCursorState(display,windows,MagickTrue);
9247       XCheckRefreshWindows(display,windows);
9248       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9249         exception);
9250       if (status == MagickFalse)
9251         XNoticeWidget(display,windows,"Unable to browse documentation",
9252           (char *) NULL);
9253       XDelay(display,1500);
9254       XSetCursorState(display,windows,MagickFalse);
9255       break;
9256     }
9257     case VersionCommand:
9258     {
9259       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9260         GetMagickCopyright());
9261       break;
9262     }
9263     case SaveToUndoBufferCommand:
9264       break;
9265     default:
9266     {
9267       (void) XBell(display,0);
9268       break;
9269     }
9270   }
9271   image_info=DestroyImageInfo(image_info);
9272   return(nexus);
9273 }
9274 
9275 /*
9276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9277 %                                                                             %
9278 %                                                                             %
9279 %                                                                             %
9280 +   X M a g n i f y I m a g e                                                 %
9281 %                                                                             %
9282 %                                                                             %
9283 %                                                                             %
9284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9285 %
9286 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9287 %  The magnified portion is displayed in a separate window.
9288 %
9289 %  The format of the XMagnifyImage method is:
9290 %
9291 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9292 %        ExceptionInfo *exception)
9293 %
9294 %  A description of each parameter follows:
9295 %
9296 %    o display: Specifies a connection to an X server;  returned from
9297 %      XOpenDisplay.
9298 %
9299 %    o windows: Specifies a pointer to a XWindows structure.
9300 %
9301 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9302 %      the entire image is refreshed.
9303 %
9304 %    o exception: return any errors or warnings in this structure.
9305 %
9306 */
XMagnifyImage(Display * display,XWindows * windows,XEvent * event,ExceptionInfo * exception)9307 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9308   ExceptionInfo *exception)
9309 {
9310   char
9311     text[MagickPathExtent];
9312 
9313   int
9314     x,
9315     y;
9316 
9317   size_t
9318     state;
9319 
9320   /*
9321     Update magnified image until the mouse button is released.
9322   */
9323   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9324   state=DefaultState;
9325   x=event->xbutton.x;
9326   y=event->xbutton.y;
9327   windows->magnify.x=(int) windows->image.x+x;
9328   windows->magnify.y=(int) windows->image.y+y;
9329   do
9330   {
9331     /*
9332       Map and unmap Info widget as text cursor crosses its boundaries.
9333     */
9334     if (windows->info.mapped != MagickFalse)
9335       {
9336         if ((x < (int) (windows->info.x+windows->info.width)) &&
9337             (y < (int) (windows->info.y+windows->info.height)))
9338           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9339       }
9340     else
9341       if ((x > (int) (windows->info.x+windows->info.width)) ||
9342           (y > (int) (windows->info.y+windows->info.height)))
9343         (void) XMapWindow(display,windows->info.id);
9344     if (windows->info.mapped != MagickFalse)
9345       {
9346         /*
9347           Display pointer position.
9348         */
9349         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9350           windows->magnify.x,windows->magnify.y);
9351         XInfoWidget(display,windows,text);
9352       }
9353     /*
9354       Wait for next event.
9355     */
9356     XScreenEvent(display,windows,event,exception);
9357     switch (event->type)
9358     {
9359       case ButtonPress:
9360         break;
9361       case ButtonRelease:
9362       {
9363         /*
9364           User has finished magnifying image.
9365         */
9366         x=event->xbutton.x;
9367         y=event->xbutton.y;
9368         state|=ExitState;
9369         break;
9370       }
9371       case Expose:
9372         break;
9373       case MotionNotify:
9374       {
9375         x=event->xmotion.x;
9376         y=event->xmotion.y;
9377         break;
9378       }
9379       default:
9380         break;
9381     }
9382     /*
9383       Check boundary conditions.
9384     */
9385     if (x < 0)
9386       x=0;
9387     else
9388       if (x >= (int) windows->image.width)
9389         x=(int) windows->image.width-1;
9390     if (y < 0)
9391       y=0;
9392     else
9393      if (y >= (int) windows->image.height)
9394        y=(int) windows->image.height-1;
9395   } while ((state & ExitState) == 0);
9396   /*
9397     Display magnified image.
9398   */
9399   XSetCursorState(display,windows,MagickFalse);
9400 }
9401 
9402 /*
9403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9404 %                                                                             %
9405 %                                                                             %
9406 %                                                                             %
9407 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9408 %                                                                             %
9409 %                                                                             %
9410 %                                                                             %
9411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9412 %
9413 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9414 %  pixel as specified by the key symbol.
9415 %
9416 %  The format of the XMagnifyWindowCommand method is:
9417 %
9418 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9419 %        const MagickStatusType state,const KeySym key_symbol,
9420 %        ExceptionInfo *exception)
9421 %
9422 %  A description of each parameter follows:
9423 %
9424 %    o display: Specifies a connection to an X server; returned from
9425 %      XOpenDisplay.
9426 %
9427 %    o windows: Specifies a pointer to a XWindows structure.
9428 %
9429 %    o state: key mask.
9430 %
9431 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9432 %      to trim.
9433 %
9434 %    o exception: return any errors or warnings in this structure.
9435 %
9436 */
XMagnifyWindowCommand(Display * display,XWindows * windows,const MagickStatusType state,const KeySym key_symbol,ExceptionInfo * exception)9437 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9438   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9439 {
9440   unsigned int
9441     quantum;
9442 
9443   /*
9444     User specified a magnify factor or position.
9445   */
9446   quantum=1;
9447   if ((state & Mod1Mask) != 0)
9448     quantum=10;
9449   switch ((int) key_symbol)
9450   {
9451     case QuitCommand:
9452     {
9453       (void) XWithdrawWindow(display,windows->magnify.id,
9454         windows->magnify.screen);
9455       break;
9456     }
9457     case XK_Home:
9458     case XK_KP_Home:
9459     {
9460       windows->magnify.x=(int) windows->image.width/2;
9461       windows->magnify.y=(int) windows->image.height/2;
9462       break;
9463     }
9464     case XK_Left:
9465     case XK_KP_Left:
9466     {
9467       if (windows->magnify.x > 0)
9468         windows->magnify.x-=quantum;
9469       break;
9470     }
9471     case XK_Up:
9472     case XK_KP_Up:
9473     {
9474       if (windows->magnify.y > 0)
9475         windows->magnify.y-=quantum;
9476       break;
9477     }
9478     case XK_Right:
9479     case XK_KP_Right:
9480     {
9481       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9482         windows->magnify.x+=quantum;
9483       break;
9484     }
9485     case XK_Down:
9486     case XK_KP_Down:
9487     {
9488       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9489         windows->magnify.y+=quantum;
9490       break;
9491     }
9492     case XK_0:
9493     case XK_1:
9494     case XK_2:
9495     case XK_3:
9496     case XK_4:
9497     case XK_5:
9498     case XK_6:
9499     case XK_7:
9500     case XK_8:
9501     case XK_9:
9502     {
9503       windows->magnify.data=(key_symbol-XK_0);
9504       break;
9505     }
9506     case XK_KP_0:
9507     case XK_KP_1:
9508     case XK_KP_2:
9509     case XK_KP_3:
9510     case XK_KP_4:
9511     case XK_KP_5:
9512     case XK_KP_6:
9513     case XK_KP_7:
9514     case XK_KP_8:
9515     case XK_KP_9:
9516     {
9517       windows->magnify.data=(key_symbol-XK_KP_0);
9518       break;
9519     }
9520     default:
9521       break;
9522   }
9523   XMakeMagnifyImage(display,windows,exception);
9524 }
9525 
9526 /*
9527 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9528 %                                                                             %
9529 %                                                                             %
9530 %                                                                             %
9531 +   X M a k e P a n I m a g e                                                 %
9532 %                                                                             %
9533 %                                                                             %
9534 %                                                                             %
9535 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9536 %
9537 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9538 %  icon window.
9539 %
9540 %  The format of the XMakePanImage method is:
9541 %
9542 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9543 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9544 %
9545 %  A description of each parameter follows:
9546 %
9547 %    o display: Specifies a connection to an X server;  returned from
9548 %      XOpenDisplay.
9549 %
9550 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9551 %
9552 %    o windows: Specifies a pointer to a XWindows structure.
9553 %
9554 %    o image: the image.
9555 %
9556 %    o exception: return any errors or warnings in this structure.
9557 %
9558 */
XMakePanImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)9559 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9560   XWindows *windows,Image *image,ExceptionInfo *exception)
9561 {
9562   MagickStatusType
9563     status;
9564 
9565   /*
9566     Create and display image for panning icon.
9567   */
9568   XSetCursorState(display,windows,MagickTrue);
9569   XCheckRefreshWindows(display,windows);
9570   windows->pan.x=(int) windows->image.x;
9571   windows->pan.y=(int) windows->image.y;
9572   status=XMakeImage(display,resource_info,&windows->pan,image,
9573     windows->pan.width,windows->pan.height,exception);
9574   if (status == MagickFalse)
9575     ThrowXWindowException(ResourceLimitError,
9576      "MemoryAllocationFailed",image->filename);
9577   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9578     windows->pan.pixmap);
9579   (void) XClearWindow(display,windows->pan.id);
9580   XDrawPanRectangle(display,windows);
9581   XSetCursorState(display,windows,MagickFalse);
9582 }
9583 
9584 /*
9585 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9586 %                                                                             %
9587 %                                                                             %
9588 %                                                                             %
9589 +   X M a t t a E d i t I m a g e                                             %
9590 %                                                                             %
9591 %                                                                             %
9592 %                                                                             %
9593 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9594 %
9595 %  XMatteEditImage() allows the user to interactively change the Matte channel
9596 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9597 %  before the matte information is stored.
9598 %
9599 %  The format of the XMatteEditImage method is:
9600 %
9601 %      MagickBooleanType XMatteEditImage(Display *display,
9602 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9603 %        ExceptionInfo *exception)
9604 %
9605 %  A description of each parameter follows:
9606 %
9607 %    o display: Specifies a connection to an X server;  returned from
9608 %      XOpenDisplay.
9609 %
9610 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9611 %
9612 %    o windows: Specifies a pointer to a XWindows structure.
9613 %
9614 %    o image: the image; returned from ReadImage.
9615 %
9616 %    o exception: return any errors or warnings in this structure.
9617 %
9618 */
XMatteEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)9619 static MagickBooleanType XMatteEditImage(Display *display,
9620   XResourceInfo *resource_info,XWindows *windows,Image **image,
9621   ExceptionInfo *exception)
9622 {
9623   const char
9624     *const MatteEditMenu[] =
9625     {
9626       "Method",
9627       "Border Color",
9628       "Fuzz",
9629       "Matte Value",
9630       "Undo",
9631       "Help",
9632       "Dismiss",
9633       (char *) NULL
9634     };
9635 
9636   static char
9637     matte[MagickPathExtent] = "0";
9638 
9639   static const ModeType
9640     MatteEditCommands[] =
9641     {
9642       MatteEditMethod,
9643       MatteEditBorderCommand,
9644       MatteEditFuzzCommand,
9645       MatteEditValueCommand,
9646       MatteEditUndoCommand,
9647       MatteEditHelpCommand,
9648       MatteEditDismissCommand
9649     };
9650 
9651   static PaintMethod
9652     method = PointMethod;
9653 
9654   static XColor
9655     border_color = { 0, 0, 0, 0, 0, 0 };
9656 
9657   char
9658     command[MagickPathExtent],
9659     text[MagickPathExtent];
9660 
9661   Cursor
9662     cursor;
9663 
9664   int
9665     entry,
9666     id,
9667     x,
9668     x_offset,
9669     y,
9670     y_offset;
9671 
9672   int
9673     i;
9674 
9675   Quantum
9676     *q;
9677 
9678   unsigned int
9679     height,
9680     width;
9681 
9682   size_t
9683     state;
9684 
9685   XEvent
9686     event;
9687 
9688   /*
9689     Map Command widget.
9690   */
9691   (void) CloneString(&windows->command.name,"Matte Edit");
9692   windows->command.data=4;
9693   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9694   (void) XMapRaised(display,windows->command.id);
9695   XClientMessage(display,windows->image.id,windows->im_protocols,
9696     windows->im_update_widget,CurrentTime);
9697   /*
9698     Make cursor.
9699   */
9700   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9701     resource_info->background_color,resource_info->foreground_color);
9702   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9703   /*
9704     Track pointer until button 1 is pressed.
9705   */
9706   XQueryPosition(display,windows->image.id,&x,&y);
9707   (void) XSelectInput(display,windows->image.id,
9708     windows->image.attributes.event_mask | PointerMotionMask);
9709   state=DefaultState;
9710   do
9711   {
9712     if (windows->info.mapped != MagickFalse)
9713       {
9714         /*
9715           Display pointer position.
9716         */
9717         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9718           x+windows->image.x,y+windows->image.y);
9719         XInfoWidget(display,windows,text);
9720       }
9721     /*
9722       Wait for next event.
9723     */
9724     XScreenEvent(display,windows,&event,exception);
9725     if (event.xany.window == windows->command.id)
9726       {
9727         /*
9728           Select a command from the Command widget.
9729         */
9730         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9731         if (id < 0)
9732           {
9733             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9734             continue;
9735           }
9736         switch (MatteEditCommands[id])
9737         {
9738           case MatteEditMethod:
9739           {
9740             char
9741               **methods;
9742 
9743             /*
9744               Select a method from the pop-up menu.
9745             */
9746             methods=GetCommandOptions(MagickMethodOptions);
9747             if (methods == (char **) NULL)
9748               break;
9749             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9750               (const char **) methods,command);
9751             if (entry >= 0)
9752               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9753                 MagickFalse,methods[entry]);
9754             methods=DestroyStringList(methods);
9755             break;
9756           }
9757           case MatteEditBorderCommand:
9758           {
9759             const char
9760               *ColorMenu[MaxNumberPens];
9761 
9762             int
9763               pen_number;
9764 
9765             /*
9766               Initialize menu selections.
9767             */
9768             for (i=0; i < (int) (MaxNumberPens-2); i++)
9769               ColorMenu[i]=resource_info->pen_colors[i];
9770             ColorMenu[MaxNumberPens-2]="Browser...";
9771             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9772             /*
9773               Select a pen color from the pop-up menu.
9774             */
9775             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9776               (const char **) ColorMenu,command);
9777             if (pen_number < 0)
9778               break;
9779             if (pen_number == (MaxNumberPens-2))
9780               {
9781                 static char
9782                   color_name[MagickPathExtent] = "gray";
9783 
9784                 /*
9785                   Select a pen color from a dialog.
9786                 */
9787                 resource_info->pen_colors[pen_number]=color_name;
9788                 XColorBrowserWidget(display,windows,"Select",color_name);
9789                 if (*color_name == '\0')
9790                   break;
9791               }
9792             /*
9793               Set border color.
9794             */
9795             (void) XParseColor(display,windows->map_info->colormap,
9796               resource_info->pen_colors[pen_number],&border_color);
9797             break;
9798           }
9799           case MatteEditFuzzCommand:
9800           {
9801             const char
9802               *const FuzzMenu[] =
9803               {
9804                 "0%",
9805                 "2%",
9806                 "5%",
9807                 "10%",
9808                 "15%",
9809                 "Dialog...",
9810                 (char *) NULL,
9811               };
9812 
9813             static char
9814               fuzz[MagickPathExtent];
9815 
9816             /*
9817               Select a command from the pop-up menu.
9818             */
9819             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9820               command);
9821             if (entry < 0)
9822               break;
9823             if (entry != 5)
9824               {
9825                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9826                   QuantumRange+1.0);
9827                 break;
9828               }
9829             (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9830             (void) XDialogWidget(display,windows,"Ok",
9831               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9832             if (*fuzz == '\0')
9833               break;
9834             (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9835             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9836               1.0);
9837             break;
9838           }
9839           case MatteEditValueCommand:
9840           {
9841             const char
9842               *const MatteMenu[] =
9843               {
9844                 "Opaque",
9845                 "Transparent",
9846                 "Dialog...",
9847                 (char *) NULL,
9848               };
9849 
9850             static char
9851               message[MagickPathExtent];
9852 
9853             /*
9854               Select a command from the pop-up menu.
9855             */
9856             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9857               command);
9858             if (entry < 0)
9859               break;
9860             if (entry != 2)
9861               {
9862                 (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
9863                   OpaqueAlpha);
9864                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9865                   (void) FormatLocaleString(matte,MagickPathExtent,
9866                     QuantumFormat,(Quantum) TransparentAlpha);
9867                 break;
9868               }
9869             (void) FormatLocaleString(message,MagickPathExtent,
9870               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9871               QuantumRange);
9872             (void) XDialogWidget(display,windows,"Matte",message,matte);
9873             if (*matte == '\0')
9874               break;
9875             break;
9876           }
9877           case MatteEditUndoCommand:
9878           {
9879             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9880               image,exception);
9881             break;
9882           }
9883           case MatteEditHelpCommand:
9884           {
9885             XTextViewHelp(display,resource_info,windows,MagickFalse,
9886               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9887             break;
9888           }
9889           case MatteEditDismissCommand:
9890           {
9891             /*
9892               Prematurely exit.
9893             */
9894             state|=EscapeState;
9895             state|=ExitState;
9896             break;
9897           }
9898           default:
9899             break;
9900         }
9901         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9902         continue;
9903       }
9904     switch (event.type)
9905     {
9906       case ButtonPress:
9907       {
9908         if (event.xbutton.button != Button1)
9909           break;
9910         if ((event.xbutton.window != windows->image.id) &&
9911             (event.xbutton.window != windows->magnify.id))
9912           break;
9913         /*
9914           Update matte data.
9915         */
9916         x=event.xbutton.x;
9917         y=event.xbutton.y;
9918         (void) XMagickCommand(display,resource_info,windows,
9919           SaveToUndoBufferCommand,image,exception);
9920         state|=UpdateConfigurationState;
9921         break;
9922       }
9923       case ButtonRelease:
9924       {
9925         if (event.xbutton.button != Button1)
9926           break;
9927         if ((event.xbutton.window != windows->image.id) &&
9928             (event.xbutton.window != windows->magnify.id))
9929           break;
9930         /*
9931           Update colormap information.
9932         */
9933         x=event.xbutton.x;
9934         y=event.xbutton.y;
9935         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9936         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9937         XInfoWidget(display,windows,text);
9938         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9939         state&=(~UpdateConfigurationState);
9940         break;
9941       }
9942       case Expose:
9943         break;
9944       case KeyPress:
9945       {
9946         char
9947           command[MagickPathExtent];
9948 
9949         KeySym
9950           key_symbol;
9951 
9952         if (event.xkey.window == windows->magnify.id)
9953           {
9954             Window
9955               window;
9956 
9957             window=windows->magnify.id;
9958             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9959           }
9960         if (event.xkey.window != windows->image.id)
9961           break;
9962         /*
9963           Respond to a user key press.
9964         */
9965         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9966           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9967         switch ((int) key_symbol)
9968         {
9969           case XK_Escape:
9970           case XK_F20:
9971           {
9972             /*
9973               Prematurely exit.
9974             */
9975             state|=ExitState;
9976             break;
9977           }
9978           case XK_F1:
9979           case XK_Help:
9980           {
9981             XTextViewHelp(display,resource_info,windows,MagickFalse,
9982               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9983             break;
9984           }
9985           default:
9986           {
9987             (void) XBell(display,0);
9988             break;
9989           }
9990         }
9991         break;
9992       }
9993       case MotionNotify:
9994       {
9995         /*
9996           Map and unmap Info widget as cursor crosses its boundaries.
9997         */
9998         x=event.xmotion.x;
9999         y=event.xmotion.y;
10000         if (windows->info.mapped != MagickFalse)
10001           {
10002             if ((x < (int) (windows->info.x+windows->info.width)) &&
10003                 (y < (int) (windows->info.y+windows->info.height)))
10004               (void) XWithdrawWindow(display,windows->info.id,
10005                 windows->info.screen);
10006           }
10007         else
10008           if ((x > (int) (windows->info.x+windows->info.width)) ||
10009               (y > (int) (windows->info.y+windows->info.height)))
10010             (void) XMapWindow(display,windows->info.id);
10011         break;
10012       }
10013       default:
10014         break;
10015     }
10016     if (event.xany.window == windows->magnify.id)
10017       {
10018         x=windows->magnify.x-windows->image.x;
10019         y=windows->magnify.y-windows->image.y;
10020       }
10021     x_offset=x;
10022     y_offset=y;
10023     if ((state & UpdateConfigurationState) != 0)
10024       {
10025         CacheView
10026           *image_view;
10027 
10028         int
10029           x,
10030           y;
10031 
10032         /*
10033           Matte edit is relative to image configuration.
10034         */
10035         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10036           MagickTrue);
10037         XPutPixel(windows->image.ximage,x_offset,y_offset,
10038           windows->pixel_info->background_color.pixel);
10039         width=(unsigned int) (*image)->columns;
10040         height=(unsigned int) (*image)->rows;
10041         x=0;
10042         y=0;
10043         if (windows->image.crop_geometry != (char *) NULL)
10044           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10045             &height);
10046         x_offset=(int) (width*(windows->image.x+x_offset)/
10047           windows->image.ximage->width+x);
10048         y_offset=(int) (height*(windows->image.y+y_offset)/
10049           windows->image.ximage->height+y);
10050         if ((x_offset < 0) || (y_offset < 0))
10051           continue;
10052         if ((x_offset >= (int) (*image)->columns) ||
10053             (y_offset >= (int) (*image)->rows))
10054           continue;
10055         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10056           return(MagickFalse);
10057         if ((*image)->alpha_trait == UndefinedPixelTrait)
10058           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10059         image_view=AcquireAuthenticCacheView(*image,exception);
10060         switch (method)
10061         {
10062           case PointMethod:
10063           default:
10064           {
10065             /*
10066               Update matte information using point algorithm.
10067             */
10068             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10069               (ssize_t) y_offset,1,1,exception);
10070             if (q == (Quantum *) NULL)
10071               break;
10072             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10073             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10074             break;
10075           }
10076           case ReplaceMethod:
10077           {
10078             PixelInfo
10079               pixel,
10080               target;
10081 
10082             /*
10083               Update matte information using replace algorithm.
10084             */
10085             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10086               x_offset,(ssize_t) y_offset,&target,exception);
10087             for (y=0; y < (int) (*image)->rows; y++)
10088             {
10089               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10090                 (*image)->columns,1,exception);
10091               if (q == (Quantum *) NULL)
10092                 break;
10093               for (x=0; x < (int) (*image)->columns; x++)
10094               {
10095                 GetPixelInfoPixel(*image,q,&pixel);
10096                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10097                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10098                 q+=GetPixelChannels(*image);
10099               }
10100               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10101                 break;
10102             }
10103             break;
10104           }
10105           case FloodfillMethod:
10106           case FillToBorderMethod:
10107           {
10108             ChannelType
10109               channel_mask;
10110 
10111             DrawInfo
10112               *draw_info;
10113 
10114             PixelInfo
10115               target;
10116 
10117             /*
10118               Update matte information using floodfill algorithm.
10119             */
10120             (void) GetOneVirtualPixelInfo(*image,
10121               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10122               y_offset,&target,exception);
10123             if (method == FillToBorderMethod)
10124               {
10125                 target.red=(double) ScaleShortToQuantum(
10126                   border_color.red);
10127                 target.green=(double) ScaleShortToQuantum(
10128                   border_color.green);
10129                 target.blue=(double) ScaleShortToQuantum(
10130                   border_color.blue);
10131               }
10132             draw_info=CloneDrawInfo(resource_info->image_info,
10133               (DrawInfo *) NULL);
10134             draw_info->fill.alpha=(double) ClampToQuantum(
10135               StringToDouble(matte,(char **) NULL));
10136             channel_mask=SetImageChannelMask(*image,AlphaChannel);
10137             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10138               x_offset,(ssize_t) y_offset,
10139               method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
10140             (void) SetPixelChannelMask(*image,channel_mask);
10141             draw_info=DestroyDrawInfo(draw_info);
10142             break;
10143           }
10144           case ResetMethod:
10145           {
10146             /*
10147               Update matte information using reset algorithm.
10148             */
10149             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10150               return(MagickFalse);
10151             for (y=0; y < (int) (*image)->rows; y++)
10152             {
10153               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10154                 (*image)->columns,1,exception);
10155               if (q == (Quantum *) NULL)
10156                 break;
10157               for (x=0; x < (int) (*image)->columns; x++)
10158               {
10159                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10160                 q+=GetPixelChannels(*image);
10161               }
10162               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10163                 break;
10164             }
10165             if (StringToLong(matte) == (long) OpaqueAlpha)
10166               (*image)->alpha_trait=UndefinedPixelTrait;
10167             break;
10168           }
10169         }
10170         image_view=DestroyCacheView(image_view);
10171         state&=(~UpdateConfigurationState);
10172       }
10173   } while ((state & ExitState) == 0);
10174   (void) XSelectInput(display,windows->image.id,
10175     windows->image.attributes.event_mask);
10176   XSetCursorState(display,windows,MagickFalse);
10177   (void) XFreeCursor(display,cursor);
10178   return(MagickTrue);
10179 }
10180 
10181 /*
10182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10183 %                                                                             %
10184 %                                                                             %
10185 %                                                                             %
10186 +   X O p e n I m a g e                                                       %
10187 %                                                                             %
10188 %                                                                             %
10189 %                                                                             %
10190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10191 %
10192 %  XOpenImage() loads an image from a file.
10193 %
10194 %  The format of the XOpenImage method is:
10195 %
10196 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10197 %       XWindows *windows,const unsigned int command)
10198 %
10199 %  A description of each parameter follows:
10200 %
10201 %    o display: Specifies a connection to an X server; returned from
10202 %      XOpenDisplay.
10203 %
10204 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10205 %
10206 %    o windows: Specifies a pointer to a XWindows structure.
10207 %
10208 %    o command: A value other than zero indicates that the file is selected
10209 %      from the command line argument list.
10210 %
10211 */
XOpenImage(Display * display,XResourceInfo * resource_info,XWindows * windows,const MagickBooleanType command)10212 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10213   XWindows *windows,const MagickBooleanType command)
10214 {
10215   const MagickInfo
10216     *magick_info;
10217 
10218   ExceptionInfo
10219     *exception;
10220 
10221   Image
10222     *nexus;
10223 
10224   ImageInfo
10225     *image_info;
10226 
10227   static char
10228     filename[MagickPathExtent] = "\0";
10229 
10230   /*
10231     Request file name from user.
10232   */
10233   if (command == MagickFalse)
10234     XFileBrowserWidget(display,windows,"Open",filename);
10235   else
10236     {
10237       char
10238         **filelist,
10239         **files;
10240 
10241       int
10242         count,
10243         status;
10244 
10245       int
10246         i,
10247         j;
10248 
10249       /*
10250         Select next image from the command line.
10251       */
10252       status=XGetCommand(display,windows->image.id,&files,&count);
10253       if (status == 0)
10254         {
10255           ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10256           return((Image *) NULL);
10257         }
10258       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10259       if (filelist == (char **) NULL)
10260         {
10261           ThrowXWindowException(ResourceLimitError,
10262             "MemoryAllocationFailed","...");
10263           (void) XFreeStringList(files);
10264           return((Image *) NULL);
10265         }
10266       j=0;
10267       for (i=1; i < count; i++)
10268         if (*files[i] != '-')
10269           filelist[j++]=files[i];
10270       filelist[j]=(char *) NULL;
10271       XListBrowserWidget(display,windows,&windows->widget,
10272         (const char **) filelist,"Load","Select Image to Load:",filename);
10273       filelist=(char **) RelinquishMagickMemory(filelist);
10274       (void) XFreeStringList(files);
10275     }
10276   if (*filename == '\0')
10277     return((Image *) NULL);
10278   image_info=CloneImageInfo(resource_info->image_info);
10279   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10280     (void *) NULL);
10281   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10282   exception=AcquireExceptionInfo();
10283   (void) SetImageInfo(image_info,0,exception);
10284   if (LocaleCompare(image_info->magick,"X") == 0)
10285     {
10286       char
10287         seconds[MagickPathExtent];
10288 
10289       /*
10290         User may want to delay the X server screen grab.
10291       */
10292       (void) CopyMagickString(seconds,"0",MagickPathExtent);
10293       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10294         seconds);
10295       if (*seconds == '\0')
10296         return((Image *) NULL);
10297       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10298     }
10299   magick_info=GetMagickInfo(image_info->magick,exception);
10300   if ((magick_info != (const MagickInfo *) NULL) &&
10301       GetMagickRawSupport(magick_info) == MagickTrue)
10302     {
10303       char
10304         geometry[MagickPathExtent];
10305 
10306       /*
10307         Request image size from the user.
10308       */
10309       (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10310       if (image_info->size != (char *) NULL)
10311         (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10312       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10313         geometry);
10314       (void) CloneString(&image_info->size,geometry);
10315     }
10316   /*
10317     Load the image.
10318   */
10319   XSetCursorState(display,windows,MagickTrue);
10320   XCheckRefreshWindows(display,windows);
10321   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10322   nexus=ReadImage(image_info,exception);
10323   CatchException(exception);
10324   XSetCursorState(display,windows,MagickFalse);
10325   if (nexus != (Image *) NULL)
10326     XClientMessage(display,windows->image.id,windows->im_protocols,
10327       windows->im_next_image,CurrentTime);
10328   else
10329     {
10330       char
10331         *text,
10332         **textlist;
10333 
10334       /*
10335         Unknown image format.
10336       */
10337       text=FileToString(filename,~0UL,exception);
10338       if (text == (char *) NULL)
10339         return((Image *) NULL);
10340       textlist=StringToList(text);
10341       if (textlist != (char **) NULL)
10342         {
10343           char
10344             title[MagickPathExtent];
10345 
10346           int
10347             i;
10348 
10349           (void) FormatLocaleString(title,MagickPathExtent,
10350             "Unknown format: %s",filename);
10351           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10352             (const char **) textlist);
10353           for (i=0; textlist[i] != (char *) NULL; i++)
10354             textlist[i]=DestroyString(textlist[i]);
10355           textlist=(char **) RelinquishMagickMemory(textlist);
10356         }
10357       text=DestroyString(text);
10358     }
10359   exception=DestroyExceptionInfo(exception);
10360   image_info=DestroyImageInfo(image_info);
10361   return(nexus);
10362 }
10363 
10364 /*
10365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10366 %                                                                             %
10367 %                                                                             %
10368 %                                                                             %
10369 +   X P a n I m a g e                                                         %
10370 %                                                                             %
10371 %                                                                             %
10372 %                                                                             %
10373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10374 %
10375 %  XPanImage() pans the image until the mouse button is released.
10376 %
10377 %  The format of the XPanImage method is:
10378 %
10379 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10380 %        ExceptionInfo *exception)
10381 %
10382 %  A description of each parameter follows:
10383 %
10384 %    o display: Specifies a connection to an X server;  returned from
10385 %      XOpenDisplay.
10386 %
10387 %    o windows: Specifies a pointer to a XWindows structure.
10388 %
10389 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10390 %      the entire image is refreshed.
10391 %
10392 %    o exception: return any errors or warnings in this structure.
10393 %
10394 */
XPanImage(Display * display,XWindows * windows,XEvent * event,ExceptionInfo * exception)10395 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10396   ExceptionInfo *exception)
10397 {
10398   char
10399     text[MagickPathExtent];
10400 
10401   Cursor
10402     cursor;
10403 
10404   double
10405     x_factor,
10406     y_factor;
10407 
10408   RectangleInfo
10409     pan_info;
10410 
10411   size_t
10412     state;
10413 
10414   /*
10415     Define cursor.
10416   */
10417   if ((windows->image.ximage->width > (int) windows->image.width) &&
10418       (windows->image.ximage->height > (int) windows->image.height))
10419     cursor=XCreateFontCursor(display,XC_fleur);
10420   else
10421     if (windows->image.ximage->width > (int) windows->image.width)
10422       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10423     else
10424       if (windows->image.ximage->height > (int) windows->image.height)
10425         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10426       else
10427         cursor=XCreateFontCursor(display,XC_arrow);
10428   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10429   /*
10430     Pan image as pointer moves until the mouse button is released.
10431   */
10432   x_factor=(double) windows->image.ximage->width/windows->pan.width;
10433   y_factor=(double) windows->image.ximage->height/windows->pan.height;
10434   pan_info.width=windows->pan.width*windows->image.width/
10435     windows->image.ximage->width;
10436   pan_info.height=windows->pan.height*windows->image.height/
10437     windows->image.ximage->height;
10438   pan_info.x=0;
10439   pan_info.y=0;
10440   state=UpdateConfigurationState;
10441   do
10442   {
10443     switch (event->type)
10444     {
10445       case ButtonPress:
10446       {
10447         /*
10448           User choose an initial pan location.
10449         */
10450         pan_info.x=(ssize_t) event->xbutton.x;
10451         pan_info.y=(ssize_t) event->xbutton.y;
10452         state|=UpdateConfigurationState;
10453         break;
10454       }
10455       case ButtonRelease:
10456       {
10457         /*
10458           User has finished panning the image.
10459         */
10460         pan_info.x=(ssize_t) event->xbutton.x;
10461         pan_info.y=(ssize_t) event->xbutton.y;
10462         state|=UpdateConfigurationState | ExitState;
10463         break;
10464       }
10465       case MotionNotify:
10466       {
10467         pan_info.x=(ssize_t) event->xmotion.x;
10468         pan_info.y=(ssize_t) event->xmotion.y;
10469         state|=UpdateConfigurationState;
10470       }
10471       default:
10472         break;
10473     }
10474     if ((state & UpdateConfigurationState) != 0)
10475       {
10476         /*
10477           Check boundary conditions.
10478         */
10479         if (pan_info.x < (ssize_t) (pan_info.width/2))
10480           pan_info.x=0;
10481         else
10482           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10483         if (pan_info.x < 0)
10484           pan_info.x=0;
10485         else
10486           if ((int) (pan_info.x+windows->image.width) >
10487               windows->image.ximage->width)
10488             pan_info.x=(ssize_t)
10489               (windows->image.ximage->width-windows->image.width);
10490         if (pan_info.y < (ssize_t) (pan_info.height/2))
10491           pan_info.y=0;
10492         else
10493           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10494         if (pan_info.y < 0)
10495           pan_info.y=0;
10496         else
10497           if ((int) (pan_info.y+windows->image.height) >
10498               windows->image.ximage->height)
10499             pan_info.y=(ssize_t)
10500               (windows->image.ximage->height-windows->image.height);
10501         if ((windows->image.x != (int) pan_info.x) ||
10502             (windows->image.y != (int) pan_info.y))
10503           {
10504             /*
10505               Display image pan offset.
10506             */
10507             windows->image.x=(int) pan_info.x;
10508             windows->image.y=(int) pan_info.y;
10509             (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
10510               windows->image.width,windows->image.height,windows->image.x,
10511               windows->image.y);
10512             XInfoWidget(display,windows,text);
10513             /*
10514               Refresh Image window.
10515             */
10516             XDrawPanRectangle(display,windows);
10517             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10518           }
10519         state&=(~UpdateConfigurationState);
10520       }
10521     /*
10522       Wait for next event.
10523     */
10524     if ((state & ExitState) == 0)
10525       XScreenEvent(display,windows,event,exception);
10526   } while ((state & ExitState) == 0);
10527   /*
10528     Restore cursor.
10529   */
10530   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10531   (void) XFreeCursor(display,cursor);
10532   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10533 }
10534 
10535 /*
10536 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10537 %                                                                             %
10538 %                                                                             %
10539 %                                                                             %
10540 +   X P a s t e I m a g e                                                     %
10541 %                                                                             %
10542 %                                                                             %
10543 %                                                                             %
10544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10545 %
10546 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10547 %  window image at a location the user chooses with the pointer.
10548 %
10549 %  The format of the XPasteImage method is:
10550 %
10551 %      MagickBooleanType XPasteImage(Display *display,
10552 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10553 %        ExceptionInfo *exception)
10554 %
10555 %  A description of each parameter follows:
10556 %
10557 %    o display: Specifies a connection to an X server;  returned from
10558 %      XOpenDisplay.
10559 %
10560 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10561 %
10562 %    o windows: Specifies a pointer to a XWindows structure.
10563 %
10564 %    o image: the image; returned from ReadImage.
10565 %
10566 %    o exception: return any errors or warnings in this structure.
10567 %
10568 */
XPasteImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)10569 static MagickBooleanType XPasteImage(Display *display,
10570   XResourceInfo *resource_info,XWindows *windows,Image *image,
10571   ExceptionInfo *exception)
10572 {
10573   const char
10574     *const PasteMenu[] =
10575     {
10576       "Operator",
10577       "Help",
10578       "Dismiss",
10579       (char *) NULL
10580     };
10581 
10582   static const ModeType
10583     PasteCommands[] =
10584     {
10585       PasteOperatorsCommand,
10586       PasteHelpCommand,
10587       PasteDismissCommand
10588     };
10589 
10590   static CompositeOperator
10591     compose = CopyCompositeOp;
10592 
10593   char
10594     text[MagickPathExtent];
10595 
10596   Cursor
10597     cursor;
10598 
10599   Image
10600     *paste_image;
10601 
10602   int
10603     entry,
10604     id,
10605     x,
10606     y;
10607 
10608   double
10609     scale_factor;
10610 
10611   RectangleInfo
10612     highlight_info,
10613     paste_info;
10614 
10615   unsigned int
10616     height,
10617     width;
10618 
10619   size_t
10620     state;
10621 
10622   XEvent
10623     event;
10624 
10625   /*
10626     Copy image.
10627   */
10628   if (resource_info->copy_image == (Image *) NULL)
10629     return(MagickFalse);
10630   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10631   if (paste_image == (Image *) NULL)
10632     return(MagickFalse);
10633   /*
10634     Map Command widget.
10635   */
10636   (void) CloneString(&windows->command.name,"Paste");
10637   windows->command.data=1;
10638   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10639   (void) XMapRaised(display,windows->command.id);
10640   XClientMessage(display,windows->image.id,windows->im_protocols,
10641     windows->im_update_widget,CurrentTime);
10642   /*
10643     Track pointer until button 1 is pressed.
10644   */
10645   XSetCursorState(display,windows,MagickFalse);
10646   XQueryPosition(display,windows->image.id,&x,&y);
10647   (void) XSelectInput(display,windows->image.id,
10648     windows->image.attributes.event_mask | PointerMotionMask);
10649   paste_info.x=(ssize_t) windows->image.x+x;
10650   paste_info.y=(ssize_t) windows->image.y+y;
10651   paste_info.width=0;
10652   paste_info.height=0;
10653   cursor=XCreateFontCursor(display,XC_ul_angle);
10654   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10655   state=DefaultState;
10656   do
10657   {
10658     if (windows->info.mapped != MagickFalse)
10659       {
10660         /*
10661           Display pointer position.
10662         */
10663         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
10664           (long) paste_info.x,(long) paste_info.y);
10665         XInfoWidget(display,windows,text);
10666       }
10667     highlight_info=paste_info;
10668     highlight_info.x=paste_info.x-windows->image.x;
10669     highlight_info.y=paste_info.y-windows->image.y;
10670     XHighlightRectangle(display,windows->image.id,
10671       windows->image.highlight_context,&highlight_info);
10672     /*
10673       Wait for next event.
10674     */
10675     XScreenEvent(display,windows,&event,exception);
10676     XHighlightRectangle(display,windows->image.id,
10677       windows->image.highlight_context,&highlight_info);
10678     if (event.xany.window == windows->command.id)
10679       {
10680         /*
10681           Select a command from the Command widget.
10682         */
10683         id=XCommandWidget(display,windows,PasteMenu,&event);
10684         if (id < 0)
10685           continue;
10686         switch (PasteCommands[id])
10687         {
10688           case PasteOperatorsCommand:
10689           {
10690             char
10691               command[MagickPathExtent],
10692               **operators;
10693 
10694             /*
10695               Select a command from the pop-up menu.
10696             */
10697             operators=GetCommandOptions(MagickComposeOptions);
10698             if (operators == (char **) NULL)
10699               break;
10700             entry=XMenuWidget(display,windows,PasteMenu[id],
10701               (const char **) operators,command);
10702             if (entry >= 0)
10703               compose=(CompositeOperator) ParseCommandOption(
10704                 MagickComposeOptions,MagickFalse,operators[entry]);
10705             operators=DestroyStringList(operators);
10706             break;
10707           }
10708           case PasteHelpCommand:
10709           {
10710             XTextViewHelp(display,resource_info,windows,MagickFalse,
10711               "Help Viewer - Image Composite",ImagePasteHelp);
10712             break;
10713           }
10714           case PasteDismissCommand:
10715           {
10716             /*
10717               Prematurely exit.
10718             */
10719             state|=EscapeState;
10720             state|=ExitState;
10721             break;
10722           }
10723           default:
10724             break;
10725         }
10726         continue;
10727       }
10728     switch (event.type)
10729     {
10730       case ButtonPress:
10731       {
10732         if (image->debug != MagickFalse)
10733           (void) LogMagickEvent(X11Event,GetMagickModule(),
10734             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10735             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10736         if (event.xbutton.button != Button1)
10737           break;
10738         if (event.xbutton.window != windows->image.id)
10739           break;
10740         /*
10741           Paste rectangle is relative to image configuration.
10742         */
10743         width=(unsigned int) image->columns;
10744         height=(unsigned int) image->rows;
10745         x=0;
10746         y=0;
10747         if (windows->image.crop_geometry != (char *) NULL)
10748           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10749             &width,&height);
10750         scale_factor=(double) windows->image.ximage->width/width;
10751         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10752         scale_factor=(double) windows->image.ximage->height/height;
10753         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10754         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10755         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10756         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10757         break;
10758       }
10759       case ButtonRelease:
10760       {
10761         if (image->debug != MagickFalse)
10762           (void) LogMagickEvent(X11Event,GetMagickModule(),
10763             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10764             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10765         if (event.xbutton.button != Button1)
10766           break;
10767         if (event.xbutton.window != windows->image.id)
10768           break;
10769         if ((paste_info.width != 0) && (paste_info.height != 0))
10770           {
10771             /*
10772               User has selected the location of the paste image.
10773             */
10774             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10775             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10776             state|=ExitState;
10777           }
10778         break;
10779       }
10780       case Expose:
10781         break;
10782       case KeyPress:
10783       {
10784         char
10785           command[MagickPathExtent];
10786 
10787         KeySym
10788           key_symbol;
10789 
10790         int
10791           length;
10792 
10793         if (event.xkey.window != windows->image.id)
10794           break;
10795         /*
10796           Respond to a user key press.
10797         */
10798         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10799           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10800         *(command+length)='\0';
10801         if (image->debug != MagickFalse)
10802           (void) LogMagickEvent(X11Event,GetMagickModule(),
10803             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10804         switch ((int) key_symbol)
10805         {
10806           case XK_Escape:
10807           case XK_F20:
10808           {
10809             /*
10810               Prematurely exit.
10811             */
10812             paste_image=DestroyImage(paste_image);
10813             state|=EscapeState;
10814             state|=ExitState;
10815             break;
10816           }
10817           case XK_F1:
10818           case XK_Help:
10819           {
10820             (void) XSetFunction(display,windows->image.highlight_context,
10821               GXcopy);
10822             XTextViewHelp(display,resource_info,windows,MagickFalse,
10823               "Help Viewer - Image Composite",ImagePasteHelp);
10824             (void) XSetFunction(display,windows->image.highlight_context,
10825               GXinvert);
10826             break;
10827           }
10828           default:
10829           {
10830             (void) XBell(display,0);
10831             break;
10832           }
10833         }
10834         break;
10835       }
10836       case MotionNotify:
10837       {
10838         /*
10839           Map and unmap Info widget as text cursor crosses its boundaries.
10840         */
10841         x=event.xmotion.x;
10842         y=event.xmotion.y;
10843         if (windows->info.mapped != MagickFalse)
10844           {
10845             if ((x < (int) (windows->info.x+windows->info.width)) &&
10846                 (y < (int) (windows->info.y+windows->info.height)))
10847               (void) XWithdrawWindow(display,windows->info.id,
10848                 windows->info.screen);
10849           }
10850         else
10851           if ((x > (int) (windows->info.x+windows->info.width)) ||
10852               (y > (int) (windows->info.y+windows->info.height)))
10853             (void) XMapWindow(display,windows->info.id);
10854         paste_info.x=(ssize_t) windows->image.x+x;
10855         paste_info.y=(ssize_t) windows->image.y+y;
10856         break;
10857       }
10858       default:
10859       {
10860         if (image->debug != MagickFalse)
10861           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10862             event.type);
10863         break;
10864       }
10865     }
10866   } while ((state & ExitState) == 0);
10867   (void) XSelectInput(display,windows->image.id,
10868     windows->image.attributes.event_mask);
10869   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10870   XSetCursorState(display,windows,MagickFalse);
10871   (void) XFreeCursor(display,cursor);
10872   if ((state & EscapeState) != 0)
10873     return(MagickTrue);
10874   /*
10875     Image pasting is relative to image configuration.
10876   */
10877   XSetCursorState(display,windows,MagickTrue);
10878   XCheckRefreshWindows(display,windows);
10879   width=(unsigned int) image->columns;
10880   height=(unsigned int) image->rows;
10881   x=0;
10882   y=0;
10883   if (windows->image.crop_geometry != (char *) NULL)
10884     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10885   scale_factor=(double) width/windows->image.ximage->width;
10886   paste_info.x+=x;
10887   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10888   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10889   scale_factor=(double) height/windows->image.ximage->height;
10890   paste_info.y+=y;
10891   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10892   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10893   /*
10894     Paste image with X Image window.
10895   */
10896   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10897     paste_info.y,exception);
10898   paste_image=DestroyImage(paste_image);
10899   XSetCursorState(display,windows,MagickFalse);
10900   /*
10901     Update image colormap.
10902   */
10903   XConfigureImageColormap(display,resource_info,windows,image,exception);
10904   (void) XConfigureImage(display,resource_info,windows,image,exception);
10905   return(MagickTrue);
10906 }
10907 
10908 /*
10909 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10910 %                                                                             %
10911 %                                                                             %
10912 %                                                                             %
10913 +   X P r i n t I m a g e                                                     %
10914 %                                                                             %
10915 %                                                                             %
10916 %                                                                             %
10917 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10918 %
10919 %  XPrintImage() prints an image to a Postscript printer.
10920 %
10921 %  The format of the XPrintImage method is:
10922 %
10923 %      MagickBooleanType XPrintImage(Display *display,
10924 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10925 %        ExceptionInfo *exception)
10926 %
10927 %  A description of each parameter follows:
10928 %
10929 %    o display: Specifies a connection to an X server; returned from
10930 %      XOpenDisplay.
10931 %
10932 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10933 %
10934 %    o windows: Specifies a pointer to a XWindows structure.
10935 %
10936 %    o image: the image.
10937 %
10938 %    o exception: return any errors or warnings in this structure.
10939 %
10940 */
XPrintImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)10941 static MagickBooleanType XPrintImage(Display *display,
10942   XResourceInfo *resource_info,XWindows *windows,Image *image,
10943   ExceptionInfo *exception)
10944 {
10945   char
10946     filename[MagickPathExtent],
10947     geometry[MagickPathExtent];
10948 
10949   const char
10950     *const PageSizes[] =
10951     {
10952       "Letter",
10953       "Tabloid",
10954       "Ledger",
10955       "Legal",
10956       "Statement",
10957       "Executive",
10958       "A3",
10959       "A4",
10960       "A5",
10961       "B4",
10962       "B5",
10963       "Folio",
10964       "Quarto",
10965       "10x14",
10966       (char *) NULL
10967     };
10968 
10969   Image
10970     *print_image;
10971 
10972   ImageInfo
10973     *image_info;
10974 
10975   MagickStatusType
10976     status;
10977 
10978   /*
10979     Request Postscript page geometry from user.
10980   */
10981   image_info=CloneImageInfo(resource_info->image_info);
10982   (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
10983   if (image_info->page != (char *) NULL)
10984     (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
10985   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10986     "Select Postscript Page Geometry:",geometry);
10987   if (*geometry == '\0')
10988     return(MagickTrue);
10989   image_info->page=GetPageGeometry(geometry);
10990   /*
10991     Apply image transforms.
10992   */
10993   XSetCursorState(display,windows,MagickTrue);
10994   XCheckRefreshWindows(display,windows);
10995   print_image=CloneImage(image,0,0,MagickTrue,exception);
10996   if (print_image == (Image *) NULL)
10997     return(MagickFalse);
10998   (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
10999     windows->image.ximage->width,windows->image.ximage->height);
11000   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11001     exception);
11002   /*
11003     Print image.
11004   */
11005   (void) AcquireUniqueFilename(filename);
11006   (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
11007     filename);
11008   status=WriteImage(image_info,print_image,exception);
11009   (void) RelinquishUniqueFileResource(filename);
11010   print_image=DestroyImage(print_image);
11011   image_info=DestroyImageInfo(image_info);
11012   XSetCursorState(display,windows,MagickFalse);
11013   return(status != 0 ? MagickTrue : MagickFalse);
11014 }
11015 
11016 /*
11017 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11018 %                                                                             %
11019 %                                                                             %
11020 %                                                                             %
11021 +   X R O I I m a g e                                                         %
11022 %                                                                             %
11023 %                                                                             %
11024 %                                                                             %
11025 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11026 %
11027 %  XROIImage() applies an image processing technique to a region of interest.
11028 %
11029 %  The format of the XROIImage method is:
11030 %
11031 %      MagickBooleanType XROIImage(Display *display,
11032 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11033 %        ExceptionInfo *exception)
11034 %
11035 %  A description of each parameter follows:
11036 %
11037 %    o display: Specifies a connection to an X server; returned from
11038 %      XOpenDisplay.
11039 %
11040 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11041 %
11042 %    o windows: Specifies a pointer to a XWindows structure.
11043 %
11044 %    o image: the image; returned from ReadImage.
11045 %
11046 %    o exception: return any errors or warnings in this structure.
11047 %
11048 */
XROIImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)11049 static MagickBooleanType XROIImage(Display *display,
11050   XResourceInfo *resource_info,XWindows *windows,Image **image,
11051   ExceptionInfo *exception)
11052 {
11053 #define ApplyMenus  7
11054 
11055   const char
11056     *const ROIMenu[] =
11057     {
11058       "Help",
11059       "Dismiss",
11060       (char *) NULL
11061     },
11062     *const ApplyMenu[] =
11063     {
11064       "File",
11065       "Edit",
11066       "Transform",
11067       "Enhance",
11068       "Effects",
11069       "F/X",
11070       "Miscellany",
11071       "Help",
11072       "Dismiss",
11073       (char *) NULL
11074     },
11075     *const FileMenu[] =
11076     {
11077       "Save...",
11078       "Print...",
11079       (char *) NULL
11080     },
11081     *const EditMenu[] =
11082     {
11083       "Undo",
11084       "Redo",
11085       (char *) NULL
11086     },
11087     *const TransformMenu[] =
11088     {
11089       "Flop",
11090       "Flip",
11091       "Rotate Right",
11092       "Rotate Left",
11093       (char *) NULL
11094     },
11095     *const EnhanceMenu[] =
11096     {
11097       "Hue...",
11098       "Saturation...",
11099       "Brightness...",
11100       "Gamma...",
11101       "Spiff",
11102       "Dull",
11103       "Contrast Stretch...",
11104       "Sigmoidal Contrast...",
11105       "Normalize",
11106       "Equalize",
11107       "Negate",
11108       "Grayscale",
11109       "Map...",
11110       "Quantize...",
11111       (char *) NULL
11112     },
11113     *const EffectsMenu[] =
11114     {
11115       "Despeckle",
11116       "Emboss",
11117       "Reduce Noise",
11118       "Add Noise",
11119       "Sharpen...",
11120       "Blur...",
11121       "Threshold...",
11122       "Edge Detect...",
11123       "Spread...",
11124       "Shade...",
11125       "Raise...",
11126       "Segment...",
11127       (char *) NULL
11128     },
11129     *const FXMenu[] =
11130     {
11131       "Solarize...",
11132       "Sepia Tone...",
11133       "Swirl...",
11134       "Implode...",
11135       "Vignette...",
11136       "Wave...",
11137       "Oil Paint...",
11138       "Charcoal Draw...",
11139       (char *) NULL
11140     },
11141     *const MiscellanyMenu[] =
11142     {
11143       "Image Info",
11144       "Zoom Image",
11145       "Show Preview...",
11146       "Show Histogram",
11147       "Show Matte",
11148       (char *) NULL
11149     };
11150 
11151   const char
11152     *const *Menus[ApplyMenus] =
11153     {
11154       FileMenu,
11155       EditMenu,
11156       TransformMenu,
11157       EnhanceMenu,
11158       EffectsMenu,
11159       FXMenu,
11160       MiscellanyMenu
11161     };
11162 
11163   static const CommandType
11164     ApplyCommands[] =
11165     {
11166       NullCommand,
11167       NullCommand,
11168       NullCommand,
11169       NullCommand,
11170       NullCommand,
11171       NullCommand,
11172       NullCommand,
11173       HelpCommand,
11174       QuitCommand
11175     },
11176     FileCommands[] =
11177     {
11178       SaveCommand,
11179       PrintCommand
11180     },
11181     EditCommands[] =
11182     {
11183       UndoCommand,
11184       RedoCommand
11185     },
11186     TransformCommands[] =
11187     {
11188       FlopCommand,
11189       FlipCommand,
11190       RotateRightCommand,
11191       RotateLeftCommand
11192     },
11193     EnhanceCommands[] =
11194     {
11195       HueCommand,
11196       SaturationCommand,
11197       BrightnessCommand,
11198       GammaCommand,
11199       SpiffCommand,
11200       DullCommand,
11201       ContrastStretchCommand,
11202       SigmoidalContrastCommand,
11203       NormalizeCommand,
11204       EqualizeCommand,
11205       NegateCommand,
11206       GrayscaleCommand,
11207       MapCommand,
11208       QuantizeCommand
11209     },
11210     EffectsCommands[] =
11211     {
11212       DespeckleCommand,
11213       EmbossCommand,
11214       ReduceNoiseCommand,
11215       AddNoiseCommand,
11216       SharpenCommand,
11217       BlurCommand,
11218       EdgeDetectCommand,
11219       SpreadCommand,
11220       ShadeCommand,
11221       RaiseCommand,
11222       SegmentCommand
11223     },
11224     FXCommands[] =
11225     {
11226       SolarizeCommand,
11227       SepiaToneCommand,
11228       SwirlCommand,
11229       ImplodeCommand,
11230       VignetteCommand,
11231       WaveCommand,
11232       OilPaintCommand,
11233       CharcoalDrawCommand
11234     },
11235     MiscellanyCommands[] =
11236     {
11237       InfoCommand,
11238       ZoomCommand,
11239       ShowPreviewCommand,
11240       ShowHistogramCommand,
11241       ShowMatteCommand
11242     },
11243     ROICommands[] =
11244     {
11245       ROIHelpCommand,
11246       ROIDismissCommand
11247     };
11248 
11249   static const CommandType
11250     *Commands[ApplyMenus] =
11251     {
11252       FileCommands,
11253       EditCommands,
11254       TransformCommands,
11255       EnhanceCommands,
11256       EffectsCommands,
11257       FXCommands,
11258       MiscellanyCommands
11259     };
11260 
11261   char
11262     command[MagickPathExtent],
11263     text[MagickPathExtent];
11264 
11265   CommandType
11266     command_type;
11267 
11268   Cursor
11269     cursor;
11270 
11271   Image
11272     *roi_image;
11273 
11274   int
11275     entry,
11276     id,
11277     x,
11278     y;
11279 
11280   double
11281     scale_factor;
11282 
11283   MagickProgressMonitor
11284     progress_monitor;
11285 
11286   RectangleInfo
11287     crop_info,
11288     highlight_info,
11289     roi_info;
11290 
11291   unsigned int
11292     height,
11293     width;
11294 
11295   size_t
11296     state;
11297 
11298   XEvent
11299     event;
11300 
11301   /*
11302     Map Command widget.
11303   */
11304   (void) CloneString(&windows->command.name,"ROI");
11305   windows->command.data=0;
11306   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11307   (void) XMapRaised(display,windows->command.id);
11308   XClientMessage(display,windows->image.id,windows->im_protocols,
11309     windows->im_update_widget,CurrentTime);
11310   /*
11311     Track pointer until button 1 is pressed.
11312   */
11313   XQueryPosition(display,windows->image.id,&x,&y);
11314   (void) XSelectInput(display,windows->image.id,
11315     windows->image.attributes.event_mask | PointerMotionMask);
11316   roi_info.x=(ssize_t) windows->image.x+x;
11317   roi_info.y=(ssize_t) windows->image.y+y;
11318   roi_info.width=0;
11319   roi_info.height=0;
11320   cursor=XCreateFontCursor(display,XC_fleur);
11321   state=DefaultState;
11322   do
11323   {
11324     if (windows->info.mapped != MagickFalse)
11325       {
11326         /*
11327           Display pointer position.
11328         */
11329         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11330           (long) roi_info.x,(long) roi_info.y);
11331         XInfoWidget(display,windows,text);
11332       }
11333     /*
11334       Wait for next event.
11335     */
11336     XScreenEvent(display,windows,&event,exception);
11337     if (event.xany.window == windows->command.id)
11338       {
11339         /*
11340           Select a command from the Command widget.
11341         */
11342         id=XCommandWidget(display,windows,ROIMenu,&event);
11343         if (id < 0)
11344           continue;
11345         switch (ROICommands[id])
11346         {
11347           case ROIHelpCommand:
11348           {
11349             XTextViewHelp(display,resource_info,windows,MagickFalse,
11350               "Help Viewer - Region of Interest",ImageROIHelp);
11351             break;
11352           }
11353           case ROIDismissCommand:
11354           {
11355             /*
11356               Prematurely exit.
11357             */
11358             state|=EscapeState;
11359             state|=ExitState;
11360             break;
11361           }
11362           default:
11363             break;
11364         }
11365         continue;
11366       }
11367     switch (event.type)
11368     {
11369       case ButtonPress:
11370       {
11371         if (event.xbutton.button != Button1)
11372           break;
11373         if (event.xbutton.window != windows->image.id)
11374           break;
11375         /*
11376           Note first corner of region of interest rectangle-- exit loop.
11377         */
11378         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11379         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11380         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11381         state|=ExitState;
11382         break;
11383       }
11384       case ButtonRelease:
11385         break;
11386       case Expose:
11387         break;
11388       case KeyPress:
11389       {
11390         KeySym
11391           key_symbol;
11392 
11393         if (event.xkey.window != windows->image.id)
11394           break;
11395         /*
11396           Respond to a user key press.
11397         */
11398         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11399           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11400         switch ((int) key_symbol)
11401         {
11402           case XK_Escape:
11403           case XK_F20:
11404           {
11405             /*
11406               Prematurely exit.
11407             */
11408             state|=EscapeState;
11409             state|=ExitState;
11410             break;
11411           }
11412           case XK_F1:
11413           case XK_Help:
11414           {
11415             XTextViewHelp(display,resource_info,windows,MagickFalse,
11416               "Help Viewer - Region of Interest",ImageROIHelp);
11417             break;
11418           }
11419           default:
11420           {
11421             (void) XBell(display,0);
11422             break;
11423           }
11424         }
11425         break;
11426       }
11427       case MotionNotify:
11428       {
11429         /*
11430           Map and unmap Info widget as text cursor crosses its boundaries.
11431         */
11432         x=event.xmotion.x;
11433         y=event.xmotion.y;
11434         if (windows->info.mapped != MagickFalse)
11435           {
11436             if ((x < (int) (windows->info.x+windows->info.width)) &&
11437                 (y < (int) (windows->info.y+windows->info.height)))
11438               (void) XWithdrawWindow(display,windows->info.id,
11439                 windows->info.screen);
11440           }
11441         else
11442           if ((x > (int) (windows->info.x+windows->info.width)) ||
11443               (y > (int) (windows->info.y+windows->info.height)))
11444             (void) XMapWindow(display,windows->info.id);
11445         roi_info.x=(ssize_t) windows->image.x+x;
11446         roi_info.y=(ssize_t) windows->image.y+y;
11447         break;
11448       }
11449       default:
11450         break;
11451     }
11452   } while ((state & ExitState) == 0);
11453   (void) XSelectInput(display,windows->image.id,
11454     windows->image.attributes.event_mask);
11455   if ((state & EscapeState) != 0)
11456     {
11457       /*
11458         User want to exit without region of interest.
11459       */
11460       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11461       (void) XFreeCursor(display,cursor);
11462       return(MagickTrue);
11463     }
11464   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11465   do
11466   {
11467     /*
11468       Size rectangle as pointer moves until the mouse button is released.
11469     */
11470     x=(int) roi_info.x;
11471     y=(int) roi_info.y;
11472     roi_info.width=0;
11473     roi_info.height=0;
11474     state=DefaultState;
11475     do
11476     {
11477       highlight_info=roi_info;
11478       highlight_info.x=roi_info.x-windows->image.x;
11479       highlight_info.y=roi_info.y-windows->image.y;
11480       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11481         {
11482           /*
11483             Display info and draw region of interest rectangle.
11484           */
11485           if (windows->info.mapped == MagickFalse)
11486             (void) XMapWindow(display,windows->info.id);
11487           (void) FormatLocaleString(text,MagickPathExtent,
11488             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11489             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11490           XInfoWidget(display,windows,text);
11491           XHighlightRectangle(display,windows->image.id,
11492             windows->image.highlight_context,&highlight_info);
11493         }
11494       else
11495         if (windows->info.mapped != MagickFalse)
11496           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11497       /*
11498         Wait for next event.
11499       */
11500       XScreenEvent(display,windows,&event,exception);
11501       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11502         XHighlightRectangle(display,windows->image.id,
11503           windows->image.highlight_context,&highlight_info);
11504       switch (event.type)
11505       {
11506         case ButtonPress:
11507         {
11508           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11509           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11510           break;
11511         }
11512         case ButtonRelease:
11513         {
11514           /*
11515             User has committed to region of interest rectangle.
11516           */
11517           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11518           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11519           XSetCursorState(display,windows,MagickFalse);
11520           state|=ExitState;
11521           if (LocaleCompare(windows->command.name,"Apply") == 0)
11522             break;
11523           (void) CloneString(&windows->command.name,"Apply");
11524           windows->command.data=ApplyMenus;
11525           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11526           break;
11527         }
11528         case Expose:
11529           break;
11530         case MotionNotify:
11531         {
11532           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11533           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11534         }
11535         default:
11536           break;
11537       }
11538       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11539           ((state & ExitState) != 0))
11540         {
11541           /*
11542             Check boundary conditions.
11543           */
11544           if (roi_info.x < 0)
11545             roi_info.x=0;
11546           else
11547             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11548               roi_info.x=(ssize_t) windows->image.ximage->width;
11549           if ((int) roi_info.x < x)
11550             roi_info.width=(unsigned int) (x-roi_info.x);
11551           else
11552             {
11553               roi_info.width=(unsigned int) (roi_info.x-x);
11554               roi_info.x=(ssize_t) x;
11555             }
11556           if (roi_info.y < 0)
11557             roi_info.y=0;
11558           else
11559             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11560               roi_info.y=(ssize_t) windows->image.ximage->height;
11561           if ((int) roi_info.y < y)
11562             roi_info.height=(unsigned int) (y-roi_info.y);
11563           else
11564             {
11565               roi_info.height=(unsigned int) (roi_info.y-y);
11566               roi_info.y=(ssize_t) y;
11567             }
11568         }
11569     } while ((state & ExitState) == 0);
11570     /*
11571       Wait for user to grab a corner of the rectangle or press return.
11572     */
11573     state=DefaultState;
11574     command_type=NullCommand;
11575     crop_info.x=0;
11576     crop_info.y=0;
11577     (void) XMapWindow(display,windows->info.id);
11578     do
11579     {
11580       if (windows->info.mapped != MagickFalse)
11581         {
11582           /*
11583             Display pointer position.
11584           */
11585           (void) FormatLocaleString(text,MagickPathExtent,
11586             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11587             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11588           XInfoWidget(display,windows,text);
11589         }
11590       highlight_info=roi_info;
11591       highlight_info.x=roi_info.x-windows->image.x;
11592       highlight_info.y=roi_info.y-windows->image.y;
11593       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11594         {
11595           state|=EscapeState;
11596           state|=ExitState;
11597           break;
11598         }
11599       if ((state & UpdateRegionState) != 0)
11600         {
11601           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11602           switch (command_type)
11603           {
11604             case UndoCommand:
11605             case RedoCommand:
11606             {
11607               (void) XMagickCommand(display,resource_info,windows,command_type,
11608                 image,exception);
11609               break;
11610             }
11611             default:
11612             {
11613               /*
11614                 Region of interest is relative to image configuration.
11615               */
11616               progress_monitor=SetImageProgressMonitor(*image,
11617                 (MagickProgressMonitor) NULL,(*image)->client_data);
11618               crop_info=roi_info;
11619               width=(unsigned int) (*image)->columns;
11620               height=(unsigned int) (*image)->rows;
11621               x=0;
11622               y=0;
11623               if (windows->image.crop_geometry != (char *) NULL)
11624                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11625                   &width,&height);
11626               scale_factor=(double) width/windows->image.ximage->width;
11627               crop_info.x+=x;
11628               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11629               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11630               scale_factor=(double)
11631                 height/windows->image.ximage->height;
11632               crop_info.y+=y;
11633               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11634               crop_info.height=(unsigned int)
11635                 (scale_factor*crop_info.height+0.5);
11636               roi_image=CropImage(*image,&crop_info,exception);
11637               (void) SetImageProgressMonitor(*image,progress_monitor,
11638                 (*image)->client_data);
11639               if (roi_image == (Image *) NULL)
11640                 continue;
11641               /*
11642                 Apply image processing technique to the region of interest.
11643               */
11644               windows->image.orphan=MagickTrue;
11645               (void) XMagickCommand(display,resource_info,windows,command_type,
11646                 &roi_image,exception);
11647               progress_monitor=SetImageProgressMonitor(*image,
11648                 (MagickProgressMonitor) NULL,(*image)->client_data);
11649               (void) XMagickCommand(display,resource_info,windows,
11650                 SaveToUndoBufferCommand,image,exception);
11651               windows->image.orphan=MagickFalse;
11652               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11653                 MagickTrue,crop_info.x,crop_info.y,exception);
11654               roi_image=DestroyImage(roi_image);
11655               (void) SetImageProgressMonitor(*image,progress_monitor,
11656                 (*image)->client_data);
11657               break;
11658             }
11659           }
11660           if (command_type != InfoCommand)
11661             {
11662               XConfigureImageColormap(display,resource_info,windows,*image,
11663                 exception);
11664               (void) XConfigureImage(display,resource_info,windows,*image,
11665                 exception);
11666             }
11667           XCheckRefreshWindows(display,windows);
11668           XInfoWidget(display,windows,text);
11669           (void) XSetFunction(display,windows->image.highlight_context,
11670             GXinvert);
11671           state&=(~UpdateRegionState);
11672         }
11673       XHighlightRectangle(display,windows->image.id,
11674         windows->image.highlight_context,&highlight_info);
11675       XScreenEvent(display,windows,&event,exception);
11676       if (event.xany.window == windows->command.id)
11677         {
11678           /*
11679             Select a command from the Command widget.
11680           */
11681           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11682           command_type=NullCommand;
11683           id=XCommandWidget(display,windows,ApplyMenu,&event);
11684           if (id >= 0)
11685             {
11686               (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11687               command_type=ApplyCommands[id];
11688               if (id < ApplyMenus)
11689                 {
11690                   /*
11691                     Select a command from a pop-up menu.
11692                   */
11693                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11694                     (const char **) Menus[id],command);
11695                   if (entry >= 0)
11696                     {
11697                       (void) CopyMagickString(command,Menus[id][entry],
11698                         MagickPathExtent);
11699                       command_type=Commands[id][entry];
11700                     }
11701                 }
11702             }
11703           (void) XSetFunction(display,windows->image.highlight_context,
11704             GXinvert);
11705           XHighlightRectangle(display,windows->image.id,
11706             windows->image.highlight_context,&highlight_info);
11707           if (command_type == HelpCommand)
11708             {
11709               (void) XSetFunction(display,windows->image.highlight_context,
11710                 GXcopy);
11711               XTextViewHelp(display,resource_info,windows,MagickFalse,
11712                 "Help Viewer - Region of Interest",ImageROIHelp);
11713               (void) XSetFunction(display,windows->image.highlight_context,
11714                 GXinvert);
11715               continue;
11716             }
11717           if (command_type == QuitCommand)
11718             {
11719               /*
11720                 exit.
11721               */
11722               state|=EscapeState;
11723               state|=ExitState;
11724               continue;
11725             }
11726           if (command_type != NullCommand)
11727             state|=UpdateRegionState;
11728           continue;
11729         }
11730       XHighlightRectangle(display,windows->image.id,
11731         windows->image.highlight_context,&highlight_info);
11732       switch (event.type)
11733       {
11734         case ButtonPress:
11735         {
11736           x=windows->image.x;
11737           y=windows->image.y;
11738           if (event.xbutton.button != Button1)
11739             break;
11740           if (event.xbutton.window != windows->image.id)
11741             break;
11742           x=windows->image.x+event.xbutton.x;
11743           y=windows->image.y+event.xbutton.y;
11744           if ((x < (int) (roi_info.x+RoiDelta)) &&
11745               (x > (int) (roi_info.x-RoiDelta)) &&
11746               (y < (int) (roi_info.y+RoiDelta)) &&
11747               (y > (int) (roi_info.y-RoiDelta)))
11748             {
11749               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11750               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11751               state|=UpdateConfigurationState;
11752               break;
11753             }
11754           if ((x < (int) (roi_info.x+RoiDelta)) &&
11755               (x > (int) (roi_info.x-RoiDelta)) &&
11756               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11757               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11758             {
11759               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11760               state|=UpdateConfigurationState;
11761               break;
11762             }
11763           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11764               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11765               (y < (int) (roi_info.y+RoiDelta)) &&
11766               (y > (int) (roi_info.y-RoiDelta)))
11767             {
11768               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11769               state|=UpdateConfigurationState;
11770               break;
11771             }
11772           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11773               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11774               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11775               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11776             {
11777               state|=UpdateConfigurationState;
11778               break;
11779             }
11780         }
11781         case ButtonRelease:
11782         {
11783           if (event.xbutton.window == windows->pan.id)
11784             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11785                 (highlight_info.y != crop_info.y-windows->image.y))
11786               XHighlightRectangle(display,windows->image.id,
11787                 windows->image.highlight_context,&highlight_info);
11788           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11789             event.xbutton.time);
11790           break;
11791         }
11792         case Expose:
11793         {
11794           if (event.xexpose.window == windows->image.id)
11795             if (event.xexpose.count == 0)
11796               {
11797                 event.xexpose.x=(int) highlight_info.x;
11798                 event.xexpose.y=(int) highlight_info.y;
11799                 event.xexpose.width=(int) highlight_info.width;
11800                 event.xexpose.height=(int) highlight_info.height;
11801                 XRefreshWindow(display,&windows->image,&event);
11802               }
11803           if (event.xexpose.window == windows->info.id)
11804             if (event.xexpose.count == 0)
11805               XInfoWidget(display,windows,text);
11806           break;
11807         }
11808         case KeyPress:
11809         {
11810           KeySym
11811             key_symbol;
11812 
11813           if (event.xkey.window != windows->image.id)
11814             break;
11815           /*
11816             Respond to a user key press.
11817           */
11818           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11819             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11820           switch ((int) key_symbol)
11821           {
11822             case XK_Shift_L:
11823             case XK_Shift_R:
11824               break;
11825             case XK_Escape:
11826             case XK_F20:
11827               state|=EscapeState;
11828             case XK_Return:
11829             {
11830               state|=ExitState;
11831               break;
11832             }
11833             case XK_Home:
11834             case XK_KP_Home:
11835             {
11836               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11837               roi_info.y=(ssize_t) (windows->image.height/2L-
11838                 roi_info.height/2L);
11839               break;
11840             }
11841             case XK_Left:
11842             case XK_KP_Left:
11843             {
11844               roi_info.x--;
11845               break;
11846             }
11847             case XK_Up:
11848             case XK_KP_Up:
11849             case XK_Next:
11850             {
11851               roi_info.y--;
11852               break;
11853             }
11854             case XK_Right:
11855             case XK_KP_Right:
11856             {
11857               roi_info.x++;
11858               break;
11859             }
11860             case XK_Prior:
11861             case XK_Down:
11862             case XK_KP_Down:
11863             {
11864               roi_info.y++;
11865               break;
11866             }
11867             case XK_F1:
11868             case XK_Help:
11869             {
11870               (void) XSetFunction(display,windows->image.highlight_context,
11871                 GXcopy);
11872               XTextViewHelp(display,resource_info,windows,MagickFalse,
11873                 "Help Viewer - Region of Interest",ImageROIHelp);
11874               (void) XSetFunction(display,windows->image.highlight_context,
11875                 GXinvert);
11876               break;
11877             }
11878             default:
11879             {
11880               command_type=XImageWindowCommand(display,resource_info,windows,
11881                 event.xkey.state,key_symbol,image,exception);
11882               if (command_type != NullCommand)
11883                 state|=UpdateRegionState;
11884               break;
11885             }
11886           }
11887           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11888             event.xkey.time);
11889           break;
11890         }
11891         case KeyRelease:
11892           break;
11893         case MotionNotify:
11894         {
11895           if (event.xbutton.window != windows->image.id)
11896             break;
11897           /*
11898             Map and unmap Info widget as text cursor crosses its boundaries.
11899           */
11900           x=event.xmotion.x;
11901           y=event.xmotion.y;
11902           if (windows->info.mapped != MagickFalse)
11903             {
11904               if ((x < (int) (windows->info.x+windows->info.width)) &&
11905                   (y < (int) (windows->info.y+windows->info.height)))
11906                 (void) XWithdrawWindow(display,windows->info.id,
11907                   windows->info.screen);
11908             }
11909           else
11910             if ((x > (int) (windows->info.x+windows->info.width)) ||
11911                 (y > (int) (windows->info.y+windows->info.height)))
11912               (void) XMapWindow(display,windows->info.id);
11913           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11914           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11915           break;
11916         }
11917         case SelectionRequest:
11918         {
11919           XSelectionEvent
11920             notify;
11921 
11922           XSelectionRequestEvent
11923             *request;
11924 
11925           /*
11926             Set primary selection.
11927           */
11928           (void) FormatLocaleString(text,MagickPathExtent,
11929             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11930             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11931           request=(&(event.xselectionrequest));
11932           (void) XChangeProperty(request->display,request->requestor,
11933             request->property,request->target,8,PropModeReplace,
11934             (unsigned char *) text,(int) strlen(text));
11935           notify.type=SelectionNotify;
11936           notify.display=request->display;
11937           notify.requestor=request->requestor;
11938           notify.selection=request->selection;
11939           notify.target=request->target;
11940           notify.time=request->time;
11941           if (request->property == None)
11942             notify.property=request->target;
11943           else
11944             notify.property=request->property;
11945           (void) XSendEvent(request->display,request->requestor,False,0,
11946             (XEvent *) &notify);
11947         }
11948         default:
11949           break;
11950       }
11951       if ((state & UpdateConfigurationState) != 0)
11952         {
11953           (void) XPutBackEvent(display,&event);
11954           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11955           break;
11956         }
11957     } while ((state & ExitState) == 0);
11958   } while ((state & ExitState) == 0);
11959   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11960   XSetCursorState(display,windows,MagickFalse);
11961   if ((state & EscapeState) != 0)
11962     return(MagickTrue);
11963   return(MagickTrue);
11964 }
11965 
11966 /*
11967 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11968 %                                                                             %
11969 %                                                                             %
11970 %                                                                             %
11971 +   X R o t a t e I m a g e                                                   %
11972 %                                                                             %
11973 %                                                                             %
11974 %                                                                             %
11975 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11976 %
11977 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11978 %  rotation angle is computed from the slope of a line drawn by the user.
11979 %
11980 %  The format of the XRotateImage method is:
11981 %
11982 %      MagickBooleanType XRotateImage(Display *display,
11983 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11984 %        Image **image,ExceptionInfo *exception)
11985 %
11986 %  A description of each parameter follows:
11987 %
11988 %    o display: Specifies a connection to an X server; returned from
11989 %      XOpenDisplay.
11990 %
11991 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11992 %
11993 %    o windows: Specifies a pointer to a XWindows structure.
11994 %
11995 %    o degrees: Specifies the number of degrees to rotate the image.
11996 %
11997 %    o image: the image.
11998 %
11999 %    o exception: return any errors or warnings in this structure.
12000 %
12001 */
XRotateImage(Display * display,XResourceInfo * resource_info,XWindows * windows,double degrees,Image ** image,ExceptionInfo * exception)12002 static MagickBooleanType XRotateImage(Display *display,
12003   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12004   ExceptionInfo *exception)
12005 {
12006   const char
12007     *const RotateMenu[] =
12008     {
12009       "Pixel Color",
12010       "Direction",
12011       "Help",
12012       "Dismiss",
12013       (char *) NULL
12014     };
12015 
12016   static ModeType
12017     direction = HorizontalRotateCommand;
12018 
12019   static const ModeType
12020     DirectionCommands[] =
12021     {
12022       HorizontalRotateCommand,
12023       VerticalRotateCommand
12024     },
12025     RotateCommands[] =
12026     {
12027       RotateColorCommand,
12028       RotateDirectionCommand,
12029       RotateHelpCommand,
12030       RotateDismissCommand
12031     };
12032 
12033   static unsigned int
12034     pen_id = 0;
12035 
12036   char
12037     command[MagickPathExtent],
12038     text[MagickPathExtent];
12039 
12040   Image
12041     *rotate_image;
12042 
12043   int
12044     id,
12045     x,
12046     y;
12047 
12048   double
12049     normalized_degrees;
12050 
12051   int
12052     i;
12053 
12054   unsigned int
12055     height,
12056     rotations,
12057     width;
12058 
12059   if (degrees == 0.0)
12060     {
12061       unsigned int
12062         distance;
12063 
12064       size_t
12065         state;
12066 
12067       XEvent
12068         event;
12069 
12070       XSegment
12071         rotate_info;
12072 
12073       /*
12074         Map Command widget.
12075       */
12076       (void) CloneString(&windows->command.name,"Rotate");
12077       windows->command.data=2;
12078       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12079       (void) XMapRaised(display,windows->command.id);
12080       XClientMessage(display,windows->image.id,windows->im_protocols,
12081         windows->im_update_widget,CurrentTime);
12082       /*
12083         Wait for first button press.
12084       */
12085       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12086       XQueryPosition(display,windows->image.id,&x,&y);
12087       rotate_info.x1=x;
12088       rotate_info.y1=y;
12089       rotate_info.x2=x;
12090       rotate_info.y2=y;
12091       state=DefaultState;
12092       do
12093       {
12094         XHighlightLine(display,windows->image.id,
12095           windows->image.highlight_context,&rotate_info);
12096         /*
12097           Wait for next event.
12098         */
12099         XScreenEvent(display,windows,&event,exception);
12100         XHighlightLine(display,windows->image.id,
12101           windows->image.highlight_context,&rotate_info);
12102         if (event.xany.window == windows->command.id)
12103           {
12104             /*
12105               Select a command from the Command widget.
12106             */
12107             id=XCommandWidget(display,windows,RotateMenu,&event);
12108             if (id < 0)
12109               continue;
12110             (void) XSetFunction(display,windows->image.highlight_context,
12111               GXcopy);
12112             switch (RotateCommands[id])
12113             {
12114               case RotateColorCommand:
12115               {
12116                 const char
12117                   *ColorMenu[MaxNumberPens];
12118 
12119                 int
12120                   pen_number;
12121 
12122                 XColor
12123                   color;
12124 
12125                 /*
12126                   Initialize menu selections.
12127                 */
12128                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12129                   ColorMenu[i]=resource_info->pen_colors[i];
12130                 ColorMenu[MaxNumberPens-2]="Browser...";
12131                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12132                 /*
12133                   Select a pen color from the pop-up menu.
12134                 */
12135                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12136                   (const char **) ColorMenu,command);
12137                 if (pen_number < 0)
12138                   break;
12139                 if (pen_number == (MaxNumberPens-2))
12140                   {
12141                     static char
12142                       color_name[MagickPathExtent] = "gray";
12143 
12144                     /*
12145                       Select a pen color from a dialog.
12146                     */
12147                     resource_info->pen_colors[pen_number]=color_name;
12148                     XColorBrowserWidget(display,windows,"Select",color_name);
12149                     if (*color_name == '\0')
12150                       break;
12151                   }
12152                 /*
12153                   Set pen color.
12154                 */
12155                 (void) XParseColor(display,windows->map_info->colormap,
12156                   resource_info->pen_colors[pen_number],&color);
12157                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12158                   (unsigned int) MaxColors,&color);
12159                 windows->pixel_info->pen_colors[pen_number]=color;
12160                 pen_id=(unsigned int) pen_number;
12161                 break;
12162               }
12163               case RotateDirectionCommand:
12164               {
12165                 const char
12166                   *Directions[] =
12167                   {
12168                     "horizontal",
12169                     "vertical",
12170                     (char *) NULL,
12171                   };
12172 
12173                 /*
12174                   Select a command from the pop-up menu.
12175                 */
12176                 id=XMenuWidget(display,windows,RotateMenu[id],
12177                   Directions,command);
12178                 if (id >= 0)
12179                   direction=DirectionCommands[id];
12180                 break;
12181               }
12182               case RotateHelpCommand:
12183               {
12184                 XTextViewHelp(display,resource_info,windows,MagickFalse,
12185                   "Help Viewer - Image Rotation",ImageRotateHelp);
12186                 break;
12187               }
12188               case RotateDismissCommand:
12189               {
12190                 /*
12191                   Prematurely exit.
12192                 */
12193                 state|=EscapeState;
12194                 state|=ExitState;
12195                 break;
12196               }
12197               default:
12198                 break;
12199             }
12200             (void) XSetFunction(display,windows->image.highlight_context,
12201               GXinvert);
12202             continue;
12203           }
12204         switch (event.type)
12205         {
12206           case ButtonPress:
12207           {
12208             if (event.xbutton.button != Button1)
12209               break;
12210             if (event.xbutton.window != windows->image.id)
12211               break;
12212             /*
12213               exit loop.
12214             */
12215             (void) XSetFunction(display,windows->image.highlight_context,
12216               GXcopy);
12217             rotate_info.x1=event.xbutton.x;
12218             rotate_info.y1=event.xbutton.y;
12219             state|=ExitState;
12220             break;
12221           }
12222           case ButtonRelease:
12223             break;
12224           case Expose:
12225             break;
12226           case KeyPress:
12227           {
12228             char
12229               command[MagickPathExtent];
12230 
12231             KeySym
12232               key_symbol;
12233 
12234             if (event.xkey.window != windows->image.id)
12235               break;
12236             /*
12237               Respond to a user key press.
12238             */
12239             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12240               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12241             switch ((int) key_symbol)
12242             {
12243               case XK_Escape:
12244               case XK_F20:
12245               {
12246                 /*
12247                   Prematurely exit.
12248                 */
12249                 state|=EscapeState;
12250                 state|=ExitState;
12251                 break;
12252               }
12253               case XK_F1:
12254               case XK_Help:
12255               {
12256                 (void) XSetFunction(display,windows->image.highlight_context,
12257                   GXcopy);
12258                 XTextViewHelp(display,resource_info,windows,MagickFalse,
12259                   "Help Viewer - Image Rotation",ImageRotateHelp);
12260                 (void) XSetFunction(display,windows->image.highlight_context,
12261                   GXinvert);
12262                 break;
12263               }
12264               default:
12265               {
12266                 (void) XBell(display,0);
12267                 break;
12268               }
12269             }
12270             break;
12271           }
12272           case MotionNotify:
12273           {
12274             rotate_info.x1=event.xmotion.x;
12275             rotate_info.y1=event.xmotion.y;
12276           }
12277         }
12278         rotate_info.x2=rotate_info.x1;
12279         rotate_info.y2=rotate_info.y1;
12280         if (direction == HorizontalRotateCommand)
12281           rotate_info.x2+=32;
12282         else
12283           rotate_info.y2-=32;
12284       } while ((state & ExitState) == 0);
12285       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12286       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12287       if ((state & EscapeState) != 0)
12288         return(MagickTrue);
12289       /*
12290         Draw line as pointer moves until the mouse button is released.
12291       */
12292       distance=0;
12293       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12294       state=DefaultState;
12295       do
12296       {
12297         if (distance > 9)
12298           {
12299             /*
12300               Display info and draw rotation line.
12301             */
12302             if (windows->info.mapped == MagickFalse)
12303               (void) XMapWindow(display,windows->info.id);
12304             (void) FormatLocaleString(text,MagickPathExtent," %g",
12305               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12306             XInfoWidget(display,windows,text);
12307             XHighlightLine(display,windows->image.id,
12308               windows->image.highlight_context,&rotate_info);
12309           }
12310         else
12311           if (windows->info.mapped != MagickFalse)
12312             (void) XWithdrawWindow(display,windows->info.id,
12313               windows->info.screen);
12314         /*
12315           Wait for next event.
12316         */
12317         XScreenEvent(display,windows,&event,exception);
12318         if (distance > 9)
12319           XHighlightLine(display,windows->image.id,
12320             windows->image.highlight_context,&rotate_info);
12321         switch (event.type)
12322         {
12323           case ButtonPress:
12324             break;
12325           case ButtonRelease:
12326           {
12327             /*
12328               User has committed to rotation line.
12329             */
12330             rotate_info.x2=event.xbutton.x;
12331             rotate_info.y2=event.xbutton.y;
12332             state|=ExitState;
12333             break;
12334           }
12335           case Expose:
12336             break;
12337           case MotionNotify:
12338           {
12339             rotate_info.x2=event.xmotion.x;
12340             rotate_info.y2=event.xmotion.y;
12341           }
12342           default:
12343             break;
12344         }
12345         /*
12346           Check boundary conditions.
12347         */
12348         if (rotate_info.x2 < 0)
12349           rotate_info.x2=0;
12350         else
12351           if (rotate_info.x2 > (int) windows->image.width)
12352             rotate_info.x2=(short) windows->image.width;
12353         if (rotate_info.y2 < 0)
12354           rotate_info.y2=0;
12355         else
12356           if (rotate_info.y2 > (int) windows->image.height)
12357             rotate_info.y2=(short) windows->image.height;
12358         /*
12359           Compute rotation angle from the slope of the line.
12360         */
12361         degrees=0.0;
12362         distance=(unsigned int)
12363           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12364           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12365         if (distance > 9)
12366           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12367             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12368       } while ((state & ExitState) == 0);
12369       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12370       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12371       if (distance <= 9)
12372         return(MagickTrue);
12373     }
12374   if (direction == VerticalRotateCommand)
12375     degrees-=90.0;
12376   if (degrees == 0.0)
12377     return(MagickTrue);
12378   /*
12379     Rotate image.
12380   */
12381   normalized_degrees=degrees;
12382   while (normalized_degrees < -45.0)
12383     normalized_degrees+=360.0;
12384   for (rotations=0; normalized_degrees > 45.0; rotations++)
12385     normalized_degrees-=90.0;
12386   if (normalized_degrees != 0.0)
12387     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12388       exception);
12389   XSetCursorState(display,windows,MagickTrue);
12390   XCheckRefreshWindows(display,windows);
12391   (*image)->background_color.red=(double) ScaleShortToQuantum(
12392     windows->pixel_info->pen_colors[pen_id].red);
12393   (*image)->background_color.green=(double) ScaleShortToQuantum(
12394     windows->pixel_info->pen_colors[pen_id].green);
12395   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12396     windows->pixel_info->pen_colors[pen_id].blue);
12397   rotate_image=RotateImage(*image,degrees,exception);
12398   XSetCursorState(display,windows,MagickFalse);
12399   if (rotate_image == (Image *) NULL)
12400     return(MagickFalse);
12401   *image=DestroyImage(*image);
12402   *image=rotate_image;
12403   if (windows->image.crop_geometry != (char *) NULL)
12404     {
12405       /*
12406         Rotate crop geometry.
12407       */
12408       width=(unsigned int) (*image)->columns;
12409       height=(unsigned int) (*image)->rows;
12410       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12411       switch (rotations % 4)
12412       {
12413         default:
12414         case 0:
12415           break;
12416         case 1:
12417         {
12418           /*
12419             Rotate 90 degrees.
12420           */
12421           (void) FormatLocaleString(windows->image.crop_geometry,
12422             MagickPathExtent,"%ux%u%+d%+d",height,width,(int) (*image)->columns-
12423             (int) height-y,x);
12424           break;
12425         }
12426         case 2:
12427         {
12428           /*
12429             Rotate 180 degrees.
12430           */
12431           (void) FormatLocaleString(windows->image.crop_geometry,
12432             MagickPathExtent,"%ux%u%+d%+d",width,height,(int) width-x,(int)
12433             height-y);
12434           break;
12435         }
12436         case 3:
12437         {
12438           /*
12439             Rotate 270 degrees.
12440           */
12441           (void) FormatLocaleString(windows->image.crop_geometry,
12442             MagickPathExtent,"%ux%u%+d%+d",height,width,y,(int) (*image)->rows-
12443             (int) width-x);
12444           break;
12445         }
12446       }
12447     }
12448   if (windows->image.orphan != MagickFalse)
12449     return(MagickTrue);
12450   if (normalized_degrees != 0.0)
12451     {
12452       /*
12453         Update image colormap.
12454       */
12455       windows->image.window_changes.width=(int) (*image)->columns;
12456       windows->image.window_changes.height=(int) (*image)->rows;
12457       if (windows->image.crop_geometry != (char *) NULL)
12458         {
12459           /*
12460             Obtain dimensions of image from crop geometry.
12461           */
12462           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12463             &width,&height);
12464           windows->image.window_changes.width=(int) width;
12465           windows->image.window_changes.height=(int) height;
12466         }
12467       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12468     }
12469   else
12470     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12471       {
12472         windows->image.window_changes.width=windows->image.ximage->height;
12473         windows->image.window_changes.height=windows->image.ximage->width;
12474       }
12475   /*
12476     Update image configuration.
12477   */
12478   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12479   return(MagickTrue);
12480 }
12481 
12482 /*
12483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12484 %                                                                             %
12485 %                                                                             %
12486 %                                                                             %
12487 +   X S a v e I m a g e                                                       %
12488 %                                                                             %
12489 %                                                                             %
12490 %                                                                             %
12491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12492 %
12493 %  XSaveImage() saves an image to a file.
12494 %
12495 %  The format of the XSaveImage method is:
12496 %
12497 %      MagickBooleanType XSaveImage(Display *display,
12498 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12499 %        ExceptionInfo *exception)
12500 %
12501 %  A description of each parameter follows:
12502 %
12503 %    o display: Specifies a connection to an X server; returned from
12504 %      XOpenDisplay.
12505 %
12506 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12507 %
12508 %    o windows: Specifies a pointer to a XWindows structure.
12509 %
12510 %    o image: the image.
12511 %
12512 %    o exception: return any errors or warnings in this structure.
12513 %
12514 */
XSaveImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)12515 static MagickBooleanType XSaveImage(Display *display,
12516   XResourceInfo *resource_info,XWindows *windows,Image *image,
12517   ExceptionInfo *exception)
12518 {
12519   char
12520     filename[MagickPathExtent],
12521     geometry[MagickPathExtent];
12522 
12523   Image
12524     *save_image;
12525 
12526   ImageInfo
12527     *image_info;
12528 
12529   MagickStatusType
12530     status;
12531 
12532   /*
12533     Request file name from user.
12534   */
12535   if (resource_info->write_filename != (char *) NULL)
12536     (void) CopyMagickString(filename,resource_info->write_filename,
12537       MagickPathExtent);
12538   else
12539     {
12540       char
12541         path[MagickPathExtent];
12542 
12543       int
12544         status;
12545 
12546       GetPathComponent(image->filename,HeadPath,path);
12547       GetPathComponent(image->filename,TailPath,filename);
12548       if (*path != '\0')
12549         {
12550           status=chdir(path);
12551           if (status == -1)
12552             (void) ThrowMagickException(exception,GetMagickModule(),
12553               FileOpenError,"UnableToOpenFile","%s",path);
12554         }
12555     }
12556   XFileBrowserWidget(display,windows,"Save",filename);
12557   if (*filename == '\0')
12558     return(MagickTrue);
12559   if (IsPathAccessible(filename) != MagickFalse)
12560     {
12561       int
12562         status;
12563 
12564       /*
12565         File exists-- seek user's permission before overwriting.
12566       */
12567       status=XConfirmWidget(display,windows,"Overwrite",filename);
12568       if (status <= 0)
12569         return(MagickTrue);
12570     }
12571   image_info=CloneImageInfo(resource_info->image_info);
12572   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12573   (void) SetImageInfo(image_info,1,exception);
12574   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12575       (LocaleCompare(image_info->magick,"JPG") == 0))
12576     {
12577       char
12578         quality[MagickPathExtent];
12579 
12580       int
12581         status;
12582 
12583       /*
12584         Request JPEG quality from user.
12585       */
12586       (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12587         image->quality);
12588       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12589         quality);
12590       if (*quality == '\0')
12591         return(MagickTrue);
12592       image->quality=StringToUnsignedLong(quality);
12593       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12594     }
12595   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12596       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12597       (LocaleCompare(image_info->magick,"PS") == 0) ||
12598       (LocaleCompare(image_info->magick,"PS2") == 0))
12599     {
12600       char
12601         geometry[MagickPathExtent];
12602 
12603       const char
12604         *const PageSizes[] =
12605         {
12606           "Letter",
12607           "Tabloid",
12608           "Ledger",
12609           "Legal",
12610           "Statement",
12611           "Executive",
12612           "A3",
12613           "A4",
12614           "A5",
12615           "B4",
12616           "B5",
12617           "Folio",
12618           "Quarto",
12619           "10x14",
12620           (char *) NULL
12621         };
12622 
12623       /*
12624         Request page geometry from user.
12625       */
12626       (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12627       if (LocaleCompare(image_info->magick,"PDF") == 0)
12628         (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12629       if (image_info->page != (char *) NULL)
12630         (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12631       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12632         "Select page geometry:",geometry);
12633       if (*geometry != '\0')
12634         image_info->page=GetPageGeometry(geometry);
12635     }
12636   /*
12637     Apply image transforms.
12638   */
12639   XSetCursorState(display,windows,MagickTrue);
12640   XCheckRefreshWindows(display,windows);
12641   save_image=CloneImage(image,0,0,MagickTrue,exception);
12642   if (save_image == (Image *) NULL)
12643     return(MagickFalse);
12644   (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12645     windows->image.ximage->width,windows->image.ximage->height);
12646   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12647     exception);
12648   /*
12649     Write image.
12650   */
12651   (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12652   status=WriteImage(image_info,save_image,exception);
12653   if (status != MagickFalse)
12654     image->taint=MagickFalse;
12655   save_image=DestroyImage(save_image);
12656   image_info=DestroyImageInfo(image_info);
12657   XSetCursorState(display,windows,MagickFalse);
12658   return(status != 0 ? MagickTrue : MagickFalse);
12659 }
12660 
12661 /*
12662 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12663 %                                                                             %
12664 %                                                                             %
12665 %                                                                             %
12666 +   X S c r e e n E v e n t                                                   %
12667 %                                                                             %
12668 %                                                                             %
12669 %                                                                             %
12670 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12671 %
12672 %  XScreenEvent() handles global events associated with the Pan and Magnify
12673 %  windows.
12674 %
12675 %  The format of the XScreenEvent function is:
12676 %
12677 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12678 %        ExceptionInfo *exception)
12679 %
12680 %  A description of each parameter follows:
12681 %
12682 %    o display: Specifies a pointer to the Display structure;  returned from
12683 %      XOpenDisplay.
12684 %
12685 %    o windows: Specifies a pointer to a XWindows structure.
12686 %
12687 %    o event: Specifies a pointer to a X11 XEvent structure.
12688 %
12689 %    o exception: return any errors or warnings in this structure.
12690 %
12691 */
12692 
12693 #if defined(__cplusplus) || defined(c_plusplus)
12694 extern "C" {
12695 #endif
12696 
XPredicate(Display * magick_unused (display),XEvent * event,char * data)12697 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12698 {
12699   XWindows
12700     *windows;
12701 
12702   windows=(XWindows *) data;
12703   if ((event->type == ClientMessage) &&
12704       (event->xclient.window == windows->image.id))
12705     return(MagickFalse);
12706   return(MagickTrue);
12707 }
12708 
12709 #if defined(__cplusplus) || defined(c_plusplus)
12710 }
12711 #endif
12712 
XScreenEvent(Display * display,XWindows * windows,XEvent * event,ExceptionInfo * exception)12713 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12714   ExceptionInfo *exception)
12715 {
12716   int
12717     x,
12718     y;
12719 
12720   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12721   if (event->xany.window == windows->command.id)
12722     return;
12723   switch (event->type)
12724   {
12725     case ButtonPress:
12726     case ButtonRelease:
12727     {
12728       if ((event->xbutton.button == Button3) &&
12729           (event->xbutton.state & Mod1Mask))
12730         {
12731           /*
12732             Convert Alt-Button3 to Button2.
12733           */
12734           event->xbutton.button=Button2;
12735           event->xbutton.state&=(~Mod1Mask);
12736         }
12737       if (event->xbutton.window == windows->backdrop.id)
12738         {
12739           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12740             event->xbutton.time);
12741           break;
12742         }
12743       if (event->xbutton.window == windows->pan.id)
12744         {
12745           XPanImage(display,windows,event,exception);
12746           break;
12747         }
12748       if (event->xbutton.window == windows->image.id)
12749         if (event->xbutton.button == Button2)
12750           {
12751             /*
12752               Update magnified image.
12753             */
12754             x=event->xbutton.x;
12755             y=event->xbutton.y;
12756             if (x < 0)
12757               x=0;
12758             else
12759               if (x >= (int) windows->image.width)
12760                 x=(int) (windows->image.width-1);
12761             windows->magnify.x=(int) windows->image.x+x;
12762             if (y < 0)
12763               y=0;
12764             else
12765              if (y >= (int) windows->image.height)
12766                y=(int) (windows->image.height-1);
12767             windows->magnify.y=windows->image.y+y;
12768             if (windows->magnify.mapped == MagickFalse)
12769               (void) XMapRaised(display,windows->magnify.id);
12770             XMakeMagnifyImage(display,windows,exception);
12771             if (event->type == ButtonRelease)
12772               (void) XWithdrawWindow(display,windows->info.id,
12773                 windows->info.screen);
12774             break;
12775           }
12776       break;
12777     }
12778     case ClientMessage:
12779     {
12780       /*
12781         If client window delete message, exit.
12782       */
12783       if (event->xclient.message_type != windows->wm_protocols)
12784         break;
12785       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12786         break;
12787       if (event->xclient.window == windows->magnify.id)
12788         {
12789           (void) XWithdrawWindow(display,windows->magnify.id,
12790             windows->magnify.screen);
12791           break;
12792         }
12793       break;
12794     }
12795     case ConfigureNotify:
12796     {
12797       if (event->xconfigure.window == windows->magnify.id)
12798         {
12799           unsigned int
12800             magnify;
12801 
12802           /*
12803             Magnify window has a new configuration.
12804           */
12805           windows->magnify.width=(unsigned int) event->xconfigure.width;
12806           windows->magnify.height=(unsigned int) event->xconfigure.height;
12807           if (windows->magnify.mapped == MagickFalse)
12808             break;
12809           magnify=1;
12810           while ((int) magnify <= event->xconfigure.width)
12811             magnify<<=1;
12812           while ((int) magnify <= event->xconfigure.height)
12813             magnify<<=1;
12814           magnify>>=1;
12815           if (((int) magnify != event->xconfigure.width) ||
12816               ((int) magnify != event->xconfigure.height))
12817             {
12818               XWindowChanges
12819                 window_changes;
12820 
12821               window_changes.width=(int) magnify;
12822               window_changes.height=(int) magnify;
12823               (void) XReconfigureWMWindow(display,windows->magnify.id,
12824                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12825                 &window_changes);
12826               break;
12827             }
12828           XMakeMagnifyImage(display,windows,exception);
12829           break;
12830         }
12831       break;
12832     }
12833     case Expose:
12834     {
12835       if (event->xexpose.window == windows->image.id)
12836         {
12837           XRefreshWindow(display,&windows->image,event);
12838           break;
12839         }
12840       if (event->xexpose.window == windows->pan.id)
12841         if (event->xexpose.count == 0)
12842           {
12843             XDrawPanRectangle(display,windows);
12844             break;
12845           }
12846       if (event->xexpose.window == windows->magnify.id)
12847         if (event->xexpose.count == 0)
12848           {
12849             XMakeMagnifyImage(display,windows,exception);
12850             break;
12851           }
12852       break;
12853     }
12854     case KeyPress:
12855     {
12856       char
12857         command[MagickPathExtent];
12858 
12859       KeySym
12860         key_symbol;
12861 
12862       if (event->xkey.window != windows->magnify.id)
12863         break;
12864       /*
12865         Respond to a user key press.
12866       */
12867       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12868         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12869       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12870         exception);
12871       break;
12872     }
12873     case MapNotify:
12874     {
12875       if (event->xmap.window == windows->magnify.id)
12876         {
12877           windows->magnify.mapped=MagickTrue;
12878           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12879           break;
12880         }
12881       if (event->xmap.window == windows->info.id)
12882         {
12883           windows->info.mapped=MagickTrue;
12884           break;
12885         }
12886       break;
12887     }
12888     case MotionNotify:
12889     {
12890       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12891       if (event->xmotion.window == windows->image.id)
12892         if (windows->magnify.mapped != MagickFalse)
12893           {
12894             /*
12895               Update magnified image.
12896             */
12897             x=event->xmotion.x;
12898             y=event->xmotion.y;
12899             if (x < 0)
12900               x=0;
12901             else
12902               if (x >= (int) windows->image.width)
12903                 x=(int) (windows->image.width-1);
12904             windows->magnify.x=(int) windows->image.x+x;
12905             if (y < 0)
12906               y=0;
12907             else
12908              if (y >= (int) windows->image.height)
12909                y=(int) (windows->image.height-1);
12910             windows->magnify.y=windows->image.y+y;
12911             XMakeMagnifyImage(display,windows,exception);
12912           }
12913       break;
12914     }
12915     case UnmapNotify:
12916     {
12917       if (event->xunmap.window == windows->magnify.id)
12918         {
12919           windows->magnify.mapped=MagickFalse;
12920           break;
12921         }
12922       if (event->xunmap.window == windows->info.id)
12923         {
12924           windows->info.mapped=MagickFalse;
12925           break;
12926         }
12927       break;
12928     }
12929     default:
12930       break;
12931   }
12932 }
12933 
12934 /*
12935 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12936 %                                                                             %
12937 %                                                                             %
12938 %                                                                             %
12939 +   X S e t C r o p G e o m e t r y                                           %
12940 %                                                                             %
12941 %                                                                             %
12942 %                                                                             %
12943 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12944 %
12945 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12946 %  and translates it to a cropping geometry relative to the image.
12947 %
12948 %  The format of the XSetCropGeometry method is:
12949 %
12950 %      void XSetCropGeometry(Display *display,XWindows *windows,
12951 %        RectangleInfo *crop_info,Image *image)
12952 %
12953 %  A description of each parameter follows:
12954 %
12955 %    o display: Specifies a connection to an X server; returned from
12956 %      XOpenDisplay.
12957 %
12958 %    o windows: Specifies a pointer to a XWindows structure.
12959 %
12960 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12961 %      Image window to crop.
12962 %
12963 %    o image: the image.
12964 %
12965 */
XSetCropGeometry(Display * display,XWindows * windows,RectangleInfo * crop_info,Image * image)12966 static void XSetCropGeometry(Display *display,XWindows *windows,
12967   RectangleInfo *crop_info,Image *image)
12968 {
12969   char
12970     text[MagickPathExtent];
12971 
12972   int
12973     x,
12974     y;
12975 
12976   double
12977     scale_factor;
12978 
12979   unsigned int
12980     height,
12981     width;
12982 
12983   if (windows->info.mapped != MagickFalse)
12984     {
12985       /*
12986         Display info on cropping rectangle.
12987       */
12988       (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
12989         (double) crop_info->width,(double) crop_info->height,(double)
12990         crop_info->x,(double) crop_info->y);
12991       XInfoWidget(display,windows,text);
12992     }
12993   /*
12994     Cropping geometry is relative to any previous crop geometry.
12995   */
12996   x=0;
12997   y=0;
12998   width=(unsigned int) image->columns;
12999   height=(unsigned int) image->rows;
13000   if (windows->image.crop_geometry != (char *) NULL)
13001     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13002   else
13003     windows->image.crop_geometry=AcquireString((char *) NULL);
13004   /*
13005     Define the crop geometry string from the cropping rectangle.
13006   */
13007   scale_factor=(double) width/windows->image.ximage->width;
13008   if (crop_info->x > 0)
13009     x+=(int) (scale_factor*crop_info->x+0.5);
13010   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13011   if (width == 0)
13012     width=1;
13013   scale_factor=(double) height/windows->image.ximage->height;
13014   if (crop_info->y > 0)
13015     y+=(int) (scale_factor*crop_info->y+0.5);
13016   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13017   if (height == 0)
13018     height=1;
13019   (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13020     "%ux%u%+d%+d",width,height,x,y);
13021 }
13022 
13023 /*
13024 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13025 %                                                                             %
13026 %                                                                             %
13027 %                                                                             %
13028 +   X T i l e I m a g e                                                       %
13029 %                                                                             %
13030 %                                                                             %
13031 %                                                                             %
13032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13033 %
13034 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13035 %  The load or delete command is chosen from a menu.
13036 %
13037 %  The format of the XTileImage method is:
13038 %
13039 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13040 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13041 %
13042 %  A description of each parameter follows:
13043 %
13044 %    o tile_image:  XTileImage reads or deletes the tile image
13045 %      and returns it.  A null image is returned if an error occurs.
13046 %
13047 %    o display: Specifies a connection to an X server;  returned from
13048 %      XOpenDisplay.
13049 %
13050 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13051 %
13052 %    o windows: Specifies a pointer to a XWindows structure.
13053 %
13054 %    o image: the image; returned from ReadImage.
13055 %
13056 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13057 %      the entire image is refreshed.
13058 %
13059 %    o exception: return any errors or warnings in this structure.
13060 %
13061 */
XTileImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,XEvent * event,ExceptionInfo * exception)13062 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13063   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13064 {
13065   const char
13066     *const VerbMenu[] =
13067     {
13068       "Load",
13069       "Next",
13070       "Former",
13071       "Delete",
13072       "Update",
13073       (char *) NULL,
13074     };
13075 
13076   static const ModeType
13077     TileCommands[] =
13078     {
13079       TileLoadCommand,
13080       TileNextCommand,
13081       TileFormerCommand,
13082       TileDeleteCommand,
13083       TileUpdateCommand
13084     };
13085 
13086   char
13087     command[MagickPathExtent],
13088     filename[MagickPathExtent];
13089 
13090   Image
13091     *tile_image;
13092 
13093   int
13094     id,
13095     status,
13096     tile,
13097     x,
13098     y;
13099 
13100   double
13101     scale_factor;
13102 
13103   char
13104     *p,
13105     *q;
13106 
13107   int
13108     i;
13109 
13110   unsigned int
13111     height,
13112     width;
13113 
13114   /*
13115     Tile image is relative to montage image configuration.
13116   */
13117   x=0;
13118   y=0;
13119   width=(unsigned int) image->columns;
13120   height=(unsigned int) image->rows;
13121   if (windows->image.crop_geometry != (char *) NULL)
13122     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13123   scale_factor=(double) width/windows->image.ximage->width;
13124   event->xbutton.x+=windows->image.x;
13125   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13126   scale_factor=(double) height/windows->image.ximage->height;
13127   event->xbutton.y+=windows->image.y;
13128   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13129   /*
13130     Determine size and location of each tile in the visual image directory.
13131   */
13132   width=(unsigned int) image->columns;
13133   height=(unsigned int) image->rows;
13134   x=0;
13135   y=0;
13136   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13137   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13138     (event->xbutton.x-x)/width;
13139   if (tile < 0)
13140     {
13141       /*
13142         Button press is outside any tile.
13143       */
13144       (void) XBell(display,0);
13145       return((Image *) NULL);
13146     }
13147   /*
13148     Determine file name from the tile directory.
13149   */
13150   p=image->directory;
13151   for (i=tile; (i != 0) && (*p != '\0'); )
13152   {
13153     if (*p == '\xff')
13154       i--;
13155     p++;
13156   }
13157   if (*p == '\0')
13158     {
13159       /*
13160         Button press is outside any tile.
13161       */
13162       (void) XBell(display,0);
13163       return((Image *) NULL);
13164     }
13165   /*
13166     Select a command from the pop-up menu.
13167   */
13168   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13169   if (id < 0)
13170     return((Image *) NULL);
13171   q=p;
13172   while ((*q != '\xff') && (*q != '\0'))
13173     q++;
13174   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13175   /*
13176     Perform command for the selected tile.
13177   */
13178   XSetCursorState(display,windows,MagickTrue);
13179   XCheckRefreshWindows(display,windows);
13180   tile_image=NewImageList();
13181   switch (TileCommands[id])
13182   {
13183     case TileLoadCommand:
13184     {
13185       /*
13186         Load tile image.
13187       */
13188       XCheckRefreshWindows(display,windows);
13189       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13190         MagickPathExtent);
13191       (void) CopyMagickString(resource_info->image_info->filename,filename,
13192         MagickPathExtent);
13193       tile_image=ReadImage(resource_info->image_info,exception);
13194       CatchException(exception);
13195       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13196       break;
13197     }
13198     case TileNextCommand:
13199     {
13200       /*
13201         Display next image.
13202       */
13203       XClientMessage(display,windows->image.id,windows->im_protocols,
13204         windows->im_next_image,CurrentTime);
13205       break;
13206     }
13207     case TileFormerCommand:
13208     {
13209       /*
13210         Display former image.
13211       */
13212       XClientMessage(display,windows->image.id,windows->im_protocols,
13213         windows->im_former_image,CurrentTime);
13214       break;
13215     }
13216     case TileDeleteCommand:
13217     {
13218       /*
13219         Delete tile image.
13220       */
13221       if (IsPathAccessible(filename) == MagickFalse)
13222         {
13223           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13224           break;
13225         }
13226       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13227       if (status <= 0)
13228         break;
13229       status=ShredFile(filename);
13230       if (status != MagickFalse)
13231         {
13232           XNoticeWidget(display,windows,"Unable to delete image file:",
13233             filename);
13234           break;
13235         }
13236     }
13237     case TileUpdateCommand:
13238     {
13239       int
13240         x_offset,
13241         y_offset;
13242 
13243       PixelInfo
13244         pixel;
13245 
13246       int
13247         j;
13248 
13249       Quantum
13250         *s;
13251 
13252       /*
13253         Ensure all the images exist.
13254       */
13255       tile=0;
13256       GetPixelInfo(image,&pixel);
13257       for (p=image->directory; *p != '\0'; p++)
13258       {
13259         CacheView
13260           *image_view;
13261 
13262         q=p;
13263         while ((*q != '\xff') && (*q != '\0'))
13264           q++;
13265         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13266         p=q;
13267         if (IsPathAccessible(filename) != MagickFalse)
13268           {
13269             tile++;
13270             continue;
13271           }
13272         /*
13273           Overwrite tile with background color.
13274         */
13275         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13276         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13277         image_view=AcquireAuthenticCacheView(image,exception);
13278         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13279         for (i=0; i < (int) height; i++)
13280         {
13281           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13282             y_offset+i,width,1,exception);
13283           if (s == (Quantum *) NULL)
13284             break;
13285           for (j=0; j < (int) width; j++)
13286           {
13287             SetPixelViaPixelInfo(image,&pixel,s);
13288             s+=GetPixelChannels(image);
13289           }
13290           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13291             break;
13292         }
13293         image_view=DestroyCacheView(image_view);
13294         tile++;
13295       }
13296       windows->image.window_changes.width=(int) image->columns;
13297       windows->image.window_changes.height=(int) image->rows;
13298       XConfigureImageColormap(display,resource_info,windows,image,exception);
13299       (void) XConfigureImage(display,resource_info,windows,image,exception);
13300       break;
13301     }
13302     default:
13303       break;
13304   }
13305   XSetCursorState(display,windows,MagickFalse);
13306   return(tile_image);
13307 }
13308 
13309 /*
13310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13311 %                                                                             %
13312 %                                                                             %
13313 %                                                                             %
13314 +   X T r a n s l a t e I m a g e                                             %
13315 %                                                                             %
13316 %                                                                             %
13317 %                                                                             %
13318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13319 %
13320 %  XTranslateImage() translates the image within an Image window by one pixel
13321 %  as specified by the key symbol.  If the image has a montage string the
13322 %  translation is respect to the width and height contained within the string.
13323 %
13324 %  The format of the XTranslateImage method is:
13325 %
13326 %      void XTranslateImage(Display *display,XWindows *windows,
13327 %        Image *image,const KeySym key_symbol)
13328 %
13329 %  A description of each parameter follows:
13330 %
13331 %    o display: Specifies a connection to an X server; returned from
13332 %      XOpenDisplay.
13333 %
13334 %    o windows: Specifies a pointer to a XWindows structure.
13335 %
13336 %    o image: the image.
13337 %
13338 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13339 %      to trim.
13340 %
13341 */
XTranslateImage(Display * display,XWindows * windows,Image * image,const KeySym key_symbol)13342 static void XTranslateImage(Display *display,XWindows *windows,
13343   Image *image,const KeySym key_symbol)
13344 {
13345   char
13346     text[MagickPathExtent];
13347 
13348   int
13349     x,
13350     y;
13351 
13352   unsigned int
13353     x_offset,
13354     y_offset;
13355 
13356   /*
13357     User specified a pan position offset.
13358   */
13359   x_offset=windows->image.width;
13360   y_offset=windows->image.height;
13361   if (image->montage != (char *) NULL)
13362     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13363   switch ((int) key_symbol)
13364   {
13365     case XK_Home:
13366     case XK_KP_Home:
13367     {
13368       windows->image.x=(int) windows->image.width/2;
13369       windows->image.y=(int) windows->image.height/2;
13370       break;
13371     }
13372     case XK_Left:
13373     case XK_KP_Left:
13374     {
13375       windows->image.x-=x_offset;
13376       break;
13377     }
13378     case XK_Next:
13379     case XK_Up:
13380     case XK_KP_Up:
13381     {
13382       windows->image.y-=y_offset;
13383       break;
13384     }
13385     case XK_Right:
13386     case XK_KP_Right:
13387     {
13388       windows->image.x+=x_offset;
13389       break;
13390     }
13391     case XK_Prior:
13392     case XK_Down:
13393     case XK_KP_Down:
13394     {
13395       windows->image.y+=y_offset;
13396       break;
13397     }
13398     default:
13399       return;
13400   }
13401   /*
13402     Check boundary conditions.
13403   */
13404   if (windows->image.x < 0)
13405     windows->image.x=0;
13406   else
13407     if ((int) (windows->image.x+windows->image.width) >
13408         windows->image.ximage->width)
13409       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13410   if (windows->image.y < 0)
13411     windows->image.y=0;
13412   else
13413     if ((int) (windows->image.y+windows->image.height) >
13414         windows->image.ximage->height)
13415       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13416   /*
13417     Refresh Image window.
13418   */
13419   (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13420     windows->image.width,windows->image.height,windows->image.x,
13421     windows->image.y);
13422   XInfoWidget(display,windows,text);
13423   XCheckRefreshWindows(display,windows);
13424   XDrawPanRectangle(display,windows);
13425   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13426   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13427 }
13428 
13429 /*
13430 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13431 %                                                                             %
13432 %                                                                             %
13433 %                                                                             %
13434 +   X T r i m I m a g e                                                       %
13435 %                                                                             %
13436 %                                                                             %
13437 %                                                                             %
13438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13439 %
13440 %  XTrimImage() trims the edges from the Image window.
13441 %
13442 %  The format of the XTrimImage method is:
13443 %
13444 %      MagickBooleanType XTrimImage(Display *display,
13445 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13446 %        ExceptionInfo *exception)
13447 %
13448 %  A description of each parameter follows:
13449 %
13450 %    o display: Specifies a connection to an X server; returned from
13451 %      XOpenDisplay.
13452 %
13453 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13454 %
13455 %    o windows: Specifies a pointer to a XWindows structure.
13456 %
13457 %    o image: the image.
13458 %
13459 %    o exception: return any errors or warnings in this structure.
13460 %
13461 */
XTrimImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)13462 static MagickBooleanType XTrimImage(Display *display,
13463   XResourceInfo *resource_info,XWindows *windows,Image *image,
13464   ExceptionInfo *exception)
13465 {
13466   RectangleInfo
13467     trim_info;
13468 
13469   int
13470     x,
13471     y;
13472 
13473   size_t
13474     background,
13475     pixel;
13476 
13477   /*
13478     Trim edges from image.
13479   */
13480   XSetCursorState(display,windows,MagickTrue);
13481   XCheckRefreshWindows(display,windows);
13482   /*
13483     Crop the left edge.
13484   */
13485   background=XGetPixel(windows->image.ximage,0,0);
13486   trim_info.width=(size_t) windows->image.ximage->width;
13487   for (x=0; x < windows->image.ximage->width; x++)
13488   {
13489     for (y=0; y < windows->image.ximage->height; y++)
13490     {
13491       pixel=XGetPixel(windows->image.ximage,x,y);
13492       if (pixel != background)
13493         break;
13494     }
13495     if (y < windows->image.ximage->height)
13496       break;
13497   }
13498   trim_info.x=(ssize_t) x;
13499   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13500     {
13501       XSetCursorState(display,windows,MagickFalse);
13502       return(MagickFalse);
13503     }
13504   /*
13505     Crop the right edge.
13506   */
13507   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13508   for (x=windows->image.ximage->width-1; x != 0; x--)
13509   {
13510     for (y=0; y < windows->image.ximage->height; y++)
13511     {
13512       pixel=XGetPixel(windows->image.ximage,x,y);
13513       if (pixel != background)
13514         break;
13515     }
13516     if (y < windows->image.ximage->height)
13517       break;
13518   }
13519   trim_info.width=(size_t) (x-trim_info.x+1);
13520   /*
13521     Crop the top edge.
13522   */
13523   background=XGetPixel(windows->image.ximage,0,0);
13524   trim_info.height=(size_t) windows->image.ximage->height;
13525   for (y=0; y < windows->image.ximage->height; y++)
13526   {
13527     for (x=0; x < windows->image.ximage->width; x++)
13528     {
13529       pixel=XGetPixel(windows->image.ximage,x,y);
13530       if (pixel != background)
13531         break;
13532     }
13533     if (x < windows->image.ximage->width)
13534       break;
13535   }
13536   trim_info.y=(ssize_t) y;
13537   /*
13538     Crop the bottom edge.
13539   */
13540   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13541   for (y=windows->image.ximage->height-1; y != 0; y--)
13542   {
13543     for (x=0; x < windows->image.ximage->width; x++)
13544     {
13545       pixel=XGetPixel(windows->image.ximage,x,y);
13546       if (pixel != background)
13547         break;
13548     }
13549     if (x < windows->image.ximage->width)
13550       break;
13551   }
13552   trim_info.height=(size_t) y-trim_info.y+1;
13553   if (((unsigned int) trim_info.width != windows->image.width) ||
13554       ((unsigned int) trim_info.height != windows->image.height))
13555     {
13556       /*
13557         Reconfigure Image window as defined by the trimming rectangle.
13558       */
13559       XSetCropGeometry(display,windows,&trim_info,image);
13560       windows->image.window_changes.width=(int) trim_info.width;
13561       windows->image.window_changes.height=(int) trim_info.height;
13562       (void) XConfigureImage(display,resource_info,windows,image,exception);
13563     }
13564   XSetCursorState(display,windows,MagickFalse);
13565   return(MagickTrue);
13566 }
13567 
13568 /*
13569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13570 %                                                                             %
13571 %                                                                             %
13572 %                                                                             %
13573 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13574 %                                                                             %
13575 %                                                                             %
13576 %                                                                             %
13577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13578 %
13579 %  XVisualDirectoryImage() creates a Visual Image Directory.
13580 %
13581 %  The format of the XVisualDirectoryImage method is:
13582 %
13583 %      Image *XVisualDirectoryImage(Display *display,
13584 %        XResourceInfo *resource_info,XWindows *windows,
13585 %        ExceptionInfo *exception)
13586 %
13587 %  A description of each parameter follows:
13588 %
13589 %    o display: Specifies a connection to an X server; returned from
13590 %      XOpenDisplay.
13591 %
13592 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13593 %
13594 %    o windows: Specifies a pointer to a XWindows structure.
13595 %
13596 %    o exception: return any errors or warnings in this structure.
13597 %
13598 */
XVisualDirectoryImage(Display * display,XResourceInfo * resource_info,XWindows * windows,ExceptionInfo * exception)13599 static Image *XVisualDirectoryImage(Display *display,
13600   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13601 {
13602 #define TileImageTag  "Scale/Image"
13603 #define XClientName  "montage"
13604 
13605   char
13606     **filelist;
13607 
13608   Image
13609     *images,
13610     *montage_image,
13611     *next_image,
13612     *thumbnail_image;
13613 
13614   ImageInfo
13615     *read_info;
13616 
13617   int
13618     number_files;
13619 
13620   MagickBooleanType
13621     backdrop;
13622 
13623   MagickStatusType
13624     status;
13625 
13626   MontageInfo
13627     *montage_info;
13628 
13629   RectangleInfo
13630     geometry;
13631 
13632   int
13633     i;
13634 
13635   static char
13636     filename[MagickPathExtent] = "\0",
13637     filenames[MagickPathExtent] = "*";
13638 
13639   XResourceInfo
13640     background_resources;
13641 
13642   /*
13643     Request file name from user.
13644   */
13645   XFileBrowserWidget(display,windows,"Directory",filenames);
13646   if (*filenames == '\0')
13647     return((Image *) NULL);
13648   /*
13649     Expand the filenames.
13650   */
13651   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13652   if (filelist == (char **) NULL)
13653     {
13654       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13655         filenames);
13656       return((Image *) NULL);
13657     }
13658   number_files=1;
13659   filelist[0]=filenames;
13660   status=ExpandFilenames(&number_files,&filelist);
13661   if ((status == MagickFalse) || (number_files == 0))
13662     {
13663       if (number_files == 0)
13664         ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13665       else
13666         ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13667           filenames);
13668       return((Image *) NULL);
13669     }
13670   /*
13671     Set image background resources.
13672   */
13673   background_resources=(*resource_info);
13674   background_resources.window_id=AcquireString("");
13675   (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13676     "0x%lx",windows->image.id);
13677   background_resources.backdrop=MagickTrue;
13678   /*
13679     Read each image and convert them to a tile.
13680   */
13681   backdrop=((windows->visual_info->klass == TrueColor) ||
13682     (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13683   read_info=CloneImageInfo(resource_info->image_info);
13684   (void) SetImageOption(read_info,"jpeg:size","120x120");
13685   (void) CloneString(&read_info->size,DefaultTileGeometry);
13686   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13687     (void *) NULL);
13688   images=NewImageList();
13689   XSetCursorState(display,windows,MagickTrue);
13690   XCheckRefreshWindows(display,windows);
13691   for (i=0; i < (int) number_files; i++)
13692   {
13693     (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13694     filelist[i]=DestroyString(filelist[i]);
13695     *read_info->magick='\0';
13696     next_image=ReadImage(read_info,exception);
13697     CatchException(exception);
13698     if (next_image != (Image *) NULL)
13699       {
13700         (void) DeleteImageProperty(next_image,"label");
13701         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13702           read_info,next_image,DefaultTileLabel,exception),exception);
13703         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13704           exception);
13705         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13706           geometry.height,exception);
13707         if (thumbnail_image != (Image *) NULL)
13708           {
13709             next_image=DestroyImage(next_image);
13710             next_image=thumbnail_image;
13711           }
13712         if (backdrop)
13713           {
13714             (void) XDisplayBackgroundImage(display,&background_resources,
13715               next_image,exception);
13716             XSetCursorState(display,windows,MagickTrue);
13717           }
13718         AppendImageToList(&images,next_image);
13719         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13720           {
13721             MagickBooleanType
13722               proceed;
13723 
13724             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13725               (MagickSizeType) number_files);
13726             if (proceed == MagickFalse)
13727               break;
13728           }
13729       }
13730   }
13731   filelist=(char **) RelinquishMagickMemory(filelist);
13732   if (images == (Image *) NULL)
13733     {
13734       read_info=DestroyImageInfo(read_info);
13735       XSetCursorState(display,windows,MagickFalse);
13736       ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13737       return((Image *) NULL);
13738     }
13739   /*
13740     Create the Visual Image Directory.
13741   */
13742   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13743   montage_info->pointsize=10;
13744   if (resource_info->font != (char *) NULL)
13745     (void) CloneString(&montage_info->font,resource_info->font);
13746   (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13747   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13748     images),exception);
13749   images=DestroyImageList(images);
13750   montage_info=DestroyMontageInfo(montage_info);
13751   read_info=DestroyImageInfo(read_info);
13752   XSetCursorState(display,windows,MagickFalse);
13753   if (montage_image == (Image *) NULL)
13754     return(montage_image);
13755   XClientMessage(display,windows->image.id,windows->im_protocols,
13756     windows->im_next_image,CurrentTime);
13757   return(montage_image);
13758 }
13759 
13760 /*
13761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13762 %                                                                             %
13763 %                                                                             %
13764 %                                                                             %
13765 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13766 %                                                                             %
13767 %                                                                             %
13768 %                                                                             %
13769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13770 %
13771 %  XDisplayBackgroundImage() displays an image in the background of a window.
13772 %
13773 %  The format of the XDisplayBackgroundImage method is:
13774 %
13775 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13776 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13777 %
13778 %  A description of each parameter follows:
13779 %
13780 %    o display: Specifies a connection to an X server;  returned from
13781 %      XOpenDisplay.
13782 %
13783 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13784 %
13785 %    o image: the image.
13786 %
13787 %    o exception: return any errors or warnings in this structure.
13788 %
13789 */
XDisplayBackgroundImage(Display * display,XResourceInfo * resource_info,Image * image,ExceptionInfo * exception)13790 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13791   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13792 {
13793   char
13794     geometry[MagickPathExtent],
13795     visual_type[MagickPathExtent];
13796 
13797   int
13798     height,
13799     status,
13800     width;
13801 
13802   RectangleInfo
13803     geometry_info;
13804 
13805   static XPixelInfo
13806     pixel;
13807 
13808   static XStandardColormap
13809     *map_info;
13810 
13811   static XVisualInfo
13812     *visual_info = (XVisualInfo *) NULL;
13813 
13814   static XWindowInfo
13815     window_info;
13816 
13817   size_t
13818     delay;
13819 
13820   Window
13821     root_window;
13822 
13823   XGCValues
13824     context_values;
13825 
13826   XResourceInfo
13827     resources;
13828 
13829   XWindowAttributes
13830     window_attributes;
13831 
13832   /*
13833     Determine target window.
13834   */
13835   assert(image != (Image *) NULL);
13836   assert(image->signature == MagickCoreSignature);
13837   if (image->debug != MagickFalse)
13838     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13839   resources=(*resource_info);
13840   window_info.id=(Window) NULL;
13841   root_window=XRootWindow(display,XDefaultScreen(display));
13842   if (LocaleCompare(resources.window_id,"root") == 0)
13843     window_info.id=root_window;
13844   else
13845     {
13846       if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13847         window_info.id=XWindowByID(display,root_window,
13848           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13849       if (window_info.id == (Window) NULL)
13850         window_info.id=XWindowByName(display,root_window,resources.window_id);
13851     }
13852   if (window_info.id == (Window) NULL)
13853     {
13854       ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13855         resources.window_id);
13856       return(MagickFalse);
13857     }
13858   /*
13859     Determine window visual id.
13860   */
13861   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13862   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13863   (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13864   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13865   if (status != 0)
13866     (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13867       XVisualIDFromVisual(window_attributes.visual));
13868   if (visual_info == (XVisualInfo *) NULL)
13869     {
13870       /*
13871         Allocate standard colormap.
13872       */
13873       map_info=XAllocStandardColormap();
13874       if (map_info == (XStandardColormap *) NULL)
13875         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13876           image->filename);
13877       map_info->colormap=(Colormap) NULL;
13878       pixel.pixels=(unsigned long *) NULL;
13879       /*
13880         Initialize visual info.
13881       */
13882       resources.map_type=(char *) NULL;
13883       resources.visual_type=visual_type;
13884       visual_info=XBestVisualInfo(display,map_info,&resources);
13885       if (visual_info == (XVisualInfo *) NULL)
13886         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13887           resources.visual_type);
13888       /*
13889         Initialize window info.
13890       */
13891       window_info.ximage=(XImage *) NULL;
13892       window_info.matte_image=(XImage *) NULL;
13893       window_info.pixmap=(Pixmap) NULL;
13894       window_info.matte_pixmap=(Pixmap) NULL;
13895     }
13896   /*
13897     Free previous root colors.
13898   */
13899   if (window_info.id == root_window)
13900     (void) XDestroyWindowColors(display,root_window);
13901   /*
13902     Initialize Standard Colormap.
13903   */
13904   resources.colormap=SharedColormap;
13905   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13906     exception);
13907   /*
13908     Graphic context superclass.
13909   */
13910   context_values.background=pixel.foreground_color.pixel;
13911   context_values.foreground=pixel.background_color.pixel;
13912   pixel.annotate_context=XCreateGC(display,window_info.id,
13913     (size_t) (GCBackground | GCForeground),&context_values);
13914   if (pixel.annotate_context == (GC) NULL)
13915     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13916       image->filename);
13917   /*
13918     Initialize Image window attributes.
13919   */
13920   window_info.name=AcquireString("\0");
13921   window_info.icon_name=AcquireString("\0");
13922   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13923     &resources,&window_info);
13924   /*
13925     Create the X image.
13926   */
13927   window_info.width=(unsigned int) image->columns;
13928   window_info.height=(unsigned int) image->rows;
13929   if ((image->columns != window_info.width) ||
13930       (image->rows != window_info.height))
13931     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13932       image->filename);
13933   (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13934     window_attributes.width,window_attributes.height);
13935   geometry_info.width=window_info.width;
13936   geometry_info.height=window_info.height;
13937   geometry_info.x=(ssize_t) window_info.x;
13938   geometry_info.y=(ssize_t) window_info.y;
13939   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13940     &geometry_info.width,&geometry_info.height);
13941   window_info.width=(unsigned int) geometry_info.width;
13942   window_info.height=(unsigned int) geometry_info.height;
13943   window_info.x=(int) geometry_info.x;
13944   window_info.y=(int) geometry_info.y;
13945   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13946     window_info.height,exception);
13947   if (status == MagickFalse)
13948     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13949       image->filename);
13950   window_info.x=0;
13951   window_info.y=0;
13952   if (image->debug != MagickFalse)
13953     {
13954       (void) LogMagickEvent(X11Event,GetMagickModule(),
13955         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13956         (double) image->columns,(double) image->rows);
13957       if (image->colors != 0)
13958         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13959           image->colors);
13960       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13961     }
13962   /*
13963     Adjust image dimensions as specified by backdrop or geometry options.
13964   */
13965   width=(int) window_info.width;
13966   height=(int) window_info.height;
13967   if (resources.backdrop != MagickFalse)
13968     {
13969       /*
13970         Center image on window.
13971       */
13972       window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13973       window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13974       width=window_attributes.width;
13975       height=window_attributes.height;
13976     }
13977   if ((resources.image_geometry != (char *) NULL) &&
13978       (*resources.image_geometry != '\0'))
13979     {
13980       char
13981         default_geometry[MagickPathExtent];
13982 
13983       int
13984         flags,
13985         gravity;
13986 
13987       XSizeHints
13988         *size_hints;
13989 
13990       /*
13991         User specified geometry.
13992       */
13993       size_hints=XAllocSizeHints();
13994       if (size_hints == (XSizeHints *) NULL)
13995         ThrowXWindowFatalException(ResourceLimitFatalError,
13996           "MemoryAllocationFailed",image->filename);
13997       size_hints->flags=0L;
13998       (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
13999         width,height);
14000       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14001         default_geometry,window_info.border_width,size_hints,&window_info.x,
14002         &window_info.y,&width,&height,&gravity);
14003       if (flags & (XValue | YValue))
14004         {
14005           width=window_attributes.width;
14006           height=window_attributes.height;
14007         }
14008       (void) XFree((void *) size_hints);
14009     }
14010   /*
14011     Create the X pixmap.
14012   */
14013   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14014     (unsigned int) height,window_info.depth);
14015   if (window_info.pixmap == (Pixmap) NULL)
14016     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14017       image->filename);
14018   /*
14019     Display pixmap on the window.
14020   */
14021   if (((unsigned int) width > window_info.width) ||
14022       ((unsigned int) height > window_info.height))
14023     (void) XFillRectangle(display,window_info.pixmap,
14024       window_info.annotate_context,0,0,(unsigned int) width,
14025       (unsigned int) height);
14026   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14027     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14028     window_info.width,(unsigned int) window_info.height);
14029   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14030   (void) XClearWindow(display,window_info.id);
14031   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14032   XDelay(display,delay == 0UL ? 10UL : delay);
14033   (void) XSync(display,MagickFalse);
14034   return(window_info.id == root_window ? MagickTrue : MagickFalse);
14035 }
14036 
14037 /*
14038 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14039 %                                                                             %
14040 %                                                                             %
14041 %                                                                             %
14042 +   X D i s p l a y I m a g e                                                 %
14043 %                                                                             %
14044 %                                                                             %
14045 %                                                                             %
14046 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14047 %
14048 %  XDisplayImage() displays an image via X11.  A new image is created and
14049 %  returned if the user interactively transforms the displayed image.
14050 %
14051 %  The format of the XDisplayImage method is:
14052 %
14053 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14054 %        char **argv,int argc,Image **image,size_t *state,
14055 %        ExceptionInfo *exception)
14056 %
14057 %  A description of each parameter follows:
14058 %
14059 %    o nexus:  Method XDisplayImage returns an image when the
14060 %      user chooses 'Open Image' from the command menu or picks a tile
14061 %      from the image directory.  Otherwise a null image is returned.
14062 %
14063 %    o display: Specifies a connection to an X server;  returned from
14064 %      XOpenDisplay.
14065 %
14066 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14067 %
14068 %    o argv: Specifies the application's argument list.
14069 %
14070 %    o argc: Specifies the number of arguments.
14071 %
14072 %    o image: Specifies an address to an address of an Image structure;
14073 %
14074 %    o exception: return any errors or warnings in this structure.
14075 %
14076 */
XDisplayImage(Display * display,XResourceInfo * resource_info,char ** argv,int argc,Image ** image,size_t * state,ExceptionInfo * exception)14077 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14078   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14079 {
14080 #define MagnifySize  256  /* must be a power of 2 */
14081 #define MagickMenus  10
14082 #define MagickTitle  "Commands"
14083 
14084   const char
14085     *const CommandMenu[] =
14086     {
14087       "File",
14088       "Edit",
14089       "View",
14090       "Transform",
14091       "Enhance",
14092       "Effects",
14093       "F/X",
14094       "Image Edit",
14095       "Miscellany",
14096       "Help",
14097       (char *) NULL
14098     },
14099     *const FileMenu[] =
14100     {
14101       "Open...",
14102       "Next",
14103       "Former",
14104       "Select...",
14105       "Save...",
14106       "Print...",
14107       "Delete...",
14108       "New...",
14109       "Visual Directory...",
14110       "Quit",
14111       (char *) NULL
14112     },
14113     *const EditMenu[] =
14114     {
14115       "Undo",
14116       "Redo",
14117       "Cut",
14118       "Copy",
14119       "Paste",
14120       (char *) NULL
14121     },
14122     *const ViewMenu[] =
14123     {
14124       "Half Size",
14125       "Original Size",
14126       "Double Size",
14127       "Resize...",
14128       "Apply",
14129       "Refresh",
14130       "Restore",
14131       (char *) NULL
14132     },
14133     *const TransformMenu[] =
14134     {
14135       "Crop",
14136       "Chop",
14137       "Flop",
14138       "Flip",
14139       "Rotate Right",
14140       "Rotate Left",
14141       "Rotate...",
14142       "Shear...",
14143       "Roll...",
14144       "Trim Edges",
14145       (char *) NULL
14146     },
14147     *const EnhanceMenu[] =
14148     {
14149       "Hue...",
14150       "Saturation...",
14151       "Brightness...",
14152       "Gamma...",
14153       "Spiff",
14154       "Dull",
14155       "Contrast Stretch...",
14156       "Sigmoidal Contrast...",
14157       "Normalize",
14158       "Equalize",
14159       "Negate",
14160       "Grayscale",
14161       "Map...",
14162       "Quantize...",
14163       (char *) NULL
14164     },
14165     *const EffectsMenu[] =
14166     {
14167       "Despeckle",
14168       "Emboss",
14169       "Reduce Noise",
14170       "Add Noise...",
14171       "Sharpen...",
14172       "Blur...",
14173       "Threshold...",
14174       "Edge Detect...",
14175       "Spread...",
14176       "Shade...",
14177       "Raise...",
14178       "Segment...",
14179       (char *) NULL
14180     },
14181     *const FXMenu[] =
14182     {
14183       "Solarize...",
14184       "Sepia Tone...",
14185       "Swirl...",
14186       "Implode...",
14187       "Vignette...",
14188       "Wave...",
14189       "Oil Paint...",
14190       "Charcoal Draw...",
14191       (char *) NULL
14192     },
14193     *const ImageEditMenu[] =
14194     {
14195       "Annotate...",
14196       "Draw...",
14197       "Color...",
14198       "Matte...",
14199       "Composite...",
14200       "Add Border...",
14201       "Add Frame...",
14202       "Comment...",
14203       "Launch...",
14204       "Region of Interest...",
14205       (char *) NULL
14206     },
14207     *const MiscellanyMenu[] =
14208     {
14209       "Image Info",
14210       "Zoom Image",
14211       "Show Preview...",
14212       "Show Histogram",
14213       "Show Matte",
14214       "Background...",
14215       "Slide Show...",
14216       "Preferences...",
14217       (char *) NULL
14218     },
14219     *const HelpMenu[] =
14220     {
14221       "Overview",
14222       "Browse Documentation",
14223       "About Display",
14224       (char *) NULL
14225     },
14226     *const ShortCutsMenu[] =
14227     {
14228       "Next",
14229       "Former",
14230       "Open...",
14231       "Save...",
14232       "Print...",
14233       "Undo",
14234       "Restore",
14235       "Image Info",
14236       "Quit",
14237       (char *) NULL
14238     },
14239     *const VirtualMenu[] =
14240     {
14241       "Image Info",
14242       "Print",
14243       "Next",
14244       "Quit",
14245       (char *) NULL
14246     };
14247 
14248   const char
14249     *const *Menus[MagickMenus] =
14250     {
14251       FileMenu,
14252       EditMenu,
14253       ViewMenu,
14254       TransformMenu,
14255       EnhanceMenu,
14256       EffectsMenu,
14257       FXMenu,
14258       ImageEditMenu,
14259       MiscellanyMenu,
14260       HelpMenu
14261     };
14262 
14263   static CommandType
14264     CommandMenus[] =
14265     {
14266       NullCommand,
14267       NullCommand,
14268       NullCommand,
14269       NullCommand,
14270       NullCommand,
14271       NullCommand,
14272       NullCommand,
14273       NullCommand,
14274       NullCommand,
14275       NullCommand,
14276     },
14277     FileCommands[] =
14278     {
14279       OpenCommand,
14280       NextCommand,
14281       FormerCommand,
14282       SelectCommand,
14283       SaveCommand,
14284       PrintCommand,
14285       DeleteCommand,
14286       NewCommand,
14287       VisualDirectoryCommand,
14288       QuitCommand
14289     },
14290     EditCommands[] =
14291     {
14292       UndoCommand,
14293       RedoCommand,
14294       CutCommand,
14295       CopyCommand,
14296       PasteCommand
14297     },
14298     ViewCommands[] =
14299     {
14300       HalfSizeCommand,
14301       OriginalSizeCommand,
14302       DoubleSizeCommand,
14303       ResizeCommand,
14304       ApplyCommand,
14305       RefreshCommand,
14306       RestoreCommand
14307     },
14308     TransformCommands[] =
14309     {
14310       CropCommand,
14311       ChopCommand,
14312       FlopCommand,
14313       FlipCommand,
14314       RotateRightCommand,
14315       RotateLeftCommand,
14316       RotateCommand,
14317       ShearCommand,
14318       RollCommand,
14319       TrimCommand
14320     },
14321     EnhanceCommands[] =
14322     {
14323       HueCommand,
14324       SaturationCommand,
14325       BrightnessCommand,
14326       GammaCommand,
14327       SpiffCommand,
14328       DullCommand,
14329       ContrastStretchCommand,
14330       SigmoidalContrastCommand,
14331       NormalizeCommand,
14332       EqualizeCommand,
14333       NegateCommand,
14334       GrayscaleCommand,
14335       MapCommand,
14336       QuantizeCommand
14337     },
14338     EffectsCommands[] =
14339     {
14340       DespeckleCommand,
14341       EmbossCommand,
14342       ReduceNoiseCommand,
14343       AddNoiseCommand,
14344       SharpenCommand,
14345       BlurCommand,
14346       ThresholdCommand,
14347       EdgeDetectCommand,
14348       SpreadCommand,
14349       ShadeCommand,
14350       RaiseCommand,
14351       SegmentCommand
14352     },
14353     FXCommands[] =
14354     {
14355       SolarizeCommand,
14356       SepiaToneCommand,
14357       SwirlCommand,
14358       ImplodeCommand,
14359       VignetteCommand,
14360       WaveCommand,
14361       OilPaintCommand,
14362       CharcoalDrawCommand
14363     },
14364     ImageEditCommands[] =
14365     {
14366       AnnotateCommand,
14367       DrawCommand,
14368       ColorCommand,
14369       MatteCommand,
14370       CompositeCommand,
14371       AddBorderCommand,
14372       AddFrameCommand,
14373       CommentCommand,
14374       LaunchCommand,
14375       RegionofInterestCommand
14376     },
14377     MiscellanyCommands[] =
14378     {
14379       InfoCommand,
14380       ZoomCommand,
14381       ShowPreviewCommand,
14382       ShowHistogramCommand,
14383       ShowMatteCommand,
14384       BackgroundCommand,
14385       SlideShowCommand,
14386       PreferencesCommand
14387     },
14388     HelpCommands[] =
14389     {
14390       HelpCommand,
14391       BrowseDocumentationCommand,
14392       VersionCommand
14393     },
14394     ShortCutsCommands[] =
14395     {
14396       NextCommand,
14397       FormerCommand,
14398       OpenCommand,
14399       SaveCommand,
14400       PrintCommand,
14401       UndoCommand,
14402       RestoreCommand,
14403       InfoCommand,
14404       QuitCommand
14405     },
14406     VirtualCommands[] =
14407     {
14408       InfoCommand,
14409       PrintCommand,
14410       NextCommand,
14411       QuitCommand
14412     };
14413 
14414   static CommandType
14415     *Commands[MagickMenus] =
14416     {
14417       FileCommands,
14418       EditCommands,
14419       ViewCommands,
14420       TransformCommands,
14421       EnhanceCommands,
14422       EffectsCommands,
14423       FXCommands,
14424       ImageEditCommands,
14425       MiscellanyCommands,
14426       HelpCommands
14427     };
14428 
14429   char
14430     command[MagickPathExtent],
14431     *directory,
14432     geometry[MagickPathExtent],
14433     resource_name[MagickPathExtent];
14434 
14435   CommandType
14436     command_type;
14437 
14438   Image
14439     *display_image,
14440     *nexus;
14441 
14442   int
14443     entry,
14444     id;
14445 
14446   KeySym
14447     key_symbol;
14448 
14449   MagickStatusType
14450     context_mask,
14451     status;
14452 
14453   RectangleInfo
14454     geometry_info;
14455 
14456   int
14457     i;
14458 
14459   static char
14460     working_directory[MagickPathExtent];
14461 
14462   static XPoint
14463     vid_info;
14464 
14465   static XWindowInfo
14466     *magick_windows[MaxXWindows];
14467 
14468   static unsigned int
14469     number_windows;
14470 
14471   struct stat
14472     attributes;
14473 
14474   time_t
14475     timer,
14476     timestamp,
14477     update_time;
14478 
14479   unsigned int
14480     height,
14481     width;
14482 
14483   size_t
14484     delay;
14485 
14486   WarningHandler
14487     warning_handler;
14488 
14489   Window
14490     root_window;
14491 
14492   XClassHint
14493     *class_hints;
14494 
14495   XEvent
14496     event;
14497 
14498   XFontStruct
14499     *font_info;
14500 
14501   XGCValues
14502     context_values;
14503 
14504   XPixelInfo
14505     *icon_pixel,
14506     *pixel;
14507 
14508   XResourceInfo
14509     *icon_resources;
14510 
14511   XStandardColormap
14512     *icon_map,
14513     *map_info;
14514 
14515   XVisualInfo
14516     *icon_visual,
14517     *visual_info;
14518 
14519   XWindowChanges
14520     window_changes;
14521 
14522   XWindows
14523     *windows;
14524 
14525   XWMHints
14526     *manager_hints;
14527 
14528   assert(image != (Image **) NULL);
14529   assert((*image)->signature == MagickCoreSignature);
14530   if ((*image)->debug != MagickFalse)
14531     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14532   display_image=(*image);
14533   warning_handler=(WarningHandler) NULL;
14534   windows=XSetWindows((XWindows *) ~0);
14535   if (windows != (XWindows *) NULL)
14536     {
14537       int
14538         status;
14539 
14540       if (*working_directory == '\0')
14541         (void) CopyMagickString(working_directory,".",MagickPathExtent);
14542       status=chdir(working_directory);
14543       if (status == -1)
14544         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14545           "UnableToOpenFile","%s",working_directory);
14546       warning_handler=resource_info->display_warnings ?
14547         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14548       warning_handler=resource_info->display_warnings ?
14549         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14550     }
14551   else
14552     {
14553       /*
14554         Allocate windows structure.
14555       */
14556       resource_info->colors=display_image->colors;
14557       windows=XSetWindows(XInitializeWindows(display,resource_info));
14558       if (windows == (XWindows *) NULL)
14559         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14560           (*image)->filename);
14561       /*
14562         Initialize window id's.
14563       */
14564       number_windows=0;
14565       magick_windows[number_windows++]=(&windows->icon);
14566       magick_windows[number_windows++]=(&windows->backdrop);
14567       magick_windows[number_windows++]=(&windows->image);
14568       magick_windows[number_windows++]=(&windows->info);
14569       magick_windows[number_windows++]=(&windows->command);
14570       magick_windows[number_windows++]=(&windows->widget);
14571       magick_windows[number_windows++]=(&windows->popup);
14572       magick_windows[number_windows++]=(&windows->magnify);
14573       magick_windows[number_windows++]=(&windows->pan);
14574       for (i=0; i < (int) number_windows; i++)
14575         magick_windows[i]->id=(Window) NULL;
14576       vid_info.x=0;
14577       vid_info.y=0;
14578     }
14579   /*
14580     Initialize font info.
14581   */
14582   if (windows->font_info != (XFontStruct *) NULL)
14583     (void) XFreeFont(display,windows->font_info);
14584   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14585   if (windows->font_info == (XFontStruct *) NULL)
14586     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14587       resource_info->font);
14588   /*
14589     Initialize Standard Colormap.
14590   */
14591   map_info=windows->map_info;
14592   icon_map=windows->icon_map;
14593   visual_info=windows->visual_info;
14594   icon_visual=windows->icon_visual;
14595   pixel=windows->pixel_info;
14596   icon_pixel=windows->icon_pixel;
14597   font_info=windows->font_info;
14598   icon_resources=windows->icon_resources;
14599   class_hints=windows->class_hints;
14600   manager_hints=windows->manager_hints;
14601   root_window=XRootWindow(display,visual_info->screen);
14602   nexus=NewImageList();
14603   if (display_image->debug != MagickFalse)
14604     {
14605       (void) LogMagickEvent(X11Event,GetMagickModule(),
14606         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14607         (double) display_image->scene,(double) display_image->columns,
14608         (double) display_image->rows);
14609       if (display_image->colors != 0)
14610         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14611           display_image->colors);
14612       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14613         display_image->magick);
14614     }
14615   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14616     map_info,pixel,exception);
14617   display_image->taint=MagickFalse;
14618   /*
14619     Initialize graphic context.
14620   */
14621   windows->context.id=(Window) NULL;
14622   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14623     resource_info,&windows->context);
14624   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14625   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14626   class_hints->res_class[0]=(char) LocaleUppercase((int)
14627     class_hints->res_class[0]);
14628   manager_hints->flags=InputHint | StateHint;
14629   manager_hints->input=MagickFalse;
14630   manager_hints->initial_state=WithdrawnState;
14631   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14632     &windows->context);
14633   if (display_image->debug != MagickFalse)
14634     (void) LogMagickEvent(X11Event,GetMagickModule(),
14635       "Window id: 0x%lx (context)",windows->context.id);
14636   context_values.background=pixel->background_color.pixel;
14637   context_values.font=font_info->fid;
14638   context_values.foreground=pixel->foreground_color.pixel;
14639   context_values.graphics_exposures=MagickFalse;
14640   context_mask=(MagickStatusType)
14641     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14642   if (pixel->annotate_context != (GC) NULL)
14643     (void) XFreeGC(display,pixel->annotate_context);
14644   pixel->annotate_context=XCreateGC(display,windows->context.id,
14645     context_mask,&context_values);
14646   if (pixel->annotate_context == (GC) NULL)
14647     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14648       display_image->filename);
14649   context_values.background=pixel->depth_color.pixel;
14650   if (pixel->widget_context != (GC) NULL)
14651     (void) XFreeGC(display,pixel->widget_context);
14652   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14653     &context_values);
14654   if (pixel->widget_context == (GC) NULL)
14655     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14656       display_image->filename);
14657   context_values.background=pixel->foreground_color.pixel;
14658   context_values.foreground=pixel->background_color.pixel;
14659   context_values.plane_mask=context_values.background ^
14660     context_values.foreground;
14661   if (pixel->highlight_context != (GC) NULL)
14662     (void) XFreeGC(display,pixel->highlight_context);
14663   pixel->highlight_context=XCreateGC(display,windows->context.id,
14664     (size_t) (context_mask | GCPlaneMask),&context_values);
14665   if (pixel->highlight_context == (GC) NULL)
14666     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14667       display_image->filename);
14668   (void) XDestroyWindow(display,windows->context.id);
14669   /*
14670     Initialize icon window.
14671   */
14672   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14673     icon_resources,&windows->icon);
14674   windows->icon.geometry=resource_info->icon_geometry;
14675   XBestIconSize(display,&windows->icon,display_image);
14676   windows->icon.attributes.colormap=XDefaultColormap(display,
14677     icon_visual->screen);
14678   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14679   manager_hints->flags=InputHint | StateHint;
14680   manager_hints->input=MagickFalse;
14681   manager_hints->initial_state=IconicState;
14682   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14683     &windows->icon);
14684   if (display_image->debug != MagickFalse)
14685     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14686       windows->icon.id);
14687   /*
14688     Initialize graphic context for icon window.
14689   */
14690   if (icon_pixel->annotate_context != (GC) NULL)
14691     (void) XFreeGC(display,icon_pixel->annotate_context);
14692   context_values.background=icon_pixel->background_color.pixel;
14693   context_values.foreground=icon_pixel->foreground_color.pixel;
14694   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14695     (size_t) (GCBackground | GCForeground),&context_values);
14696   if (icon_pixel->annotate_context == (GC) NULL)
14697     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14698       display_image->filename);
14699   windows->icon.annotate_context=icon_pixel->annotate_context;
14700   /*
14701     Initialize Image window.
14702   */
14703   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14704     &windows->image);
14705   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14706   if (resource_info->use_shared_memory == MagickFalse)
14707     windows->image.shared_memory=MagickFalse;
14708   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14709     {
14710       char
14711         *title;
14712 
14713       title=InterpretImageProperties(resource_info->image_info,display_image,
14714         resource_info->title,exception);
14715       (void) CloneString(&windows->image.name,title);
14716       (void) CloneString(&windows->image.icon_name,title);
14717       title=DestroyString(title);
14718     }
14719   else
14720     {
14721       char
14722         filename[MagickPathExtent],
14723         window_name[MagickPathExtent];
14724 
14725       /*
14726         Window name is the base of the filename.
14727       */
14728       GetPathComponent(display_image->magick_filename,TailPath,filename);
14729       if (display_image->scene == 0)
14730         (void) FormatLocaleString(window_name,MagickPathExtent,"%s: %s",
14731           MagickPackageName,filename);
14732       else
14733         (void) FormatLocaleString(window_name,MagickPathExtent,
14734           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14735           (double) display_image->scene,(double) GetImageListLength(
14736           display_image));
14737       (void) CloneString(&windows->image.name,window_name);
14738       (void) CloneString(&windows->image.icon_name,filename);
14739     }
14740   if (resource_info->immutable)
14741     windows->image.immutable=MagickTrue;
14742   windows->image.use_pixmap=resource_info->use_pixmap;
14743   windows->image.geometry=resource_info->image_geometry;
14744   (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14745     XDisplayWidth(display,visual_info->screen),
14746     XDisplayHeight(display,visual_info->screen));
14747   geometry_info.width=display_image->columns;
14748   geometry_info.height=display_image->rows;
14749   geometry_info.x=0;
14750   geometry_info.y=0;
14751   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14752     &geometry_info.width,&geometry_info.height);
14753   windows->image.width=(unsigned int) geometry_info.width;
14754   windows->image.height=(unsigned int) geometry_info.height;
14755   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14756     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14757     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14758     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14759   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14760     resource_info,&windows->backdrop);
14761   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14762     {
14763       /*
14764         Initialize backdrop window.
14765       */
14766       windows->backdrop.x=0;
14767       windows->backdrop.y=0;
14768       (void) CloneString(&windows->backdrop.name,"Backdrop");
14769       windows->backdrop.flags=(size_t) (USSize | USPosition);
14770       windows->backdrop.width=(unsigned int)
14771         XDisplayWidth(display,visual_info->screen);
14772       windows->backdrop.height=(unsigned int)
14773         XDisplayHeight(display,visual_info->screen);
14774       windows->backdrop.border_width=0;
14775       windows->backdrop.immutable=MagickTrue;
14776       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14777         ButtonReleaseMask;
14778       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14779         StructureNotifyMask;
14780       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14781       manager_hints->icon_window=windows->icon.id;
14782       manager_hints->input=MagickTrue;
14783       manager_hints->initial_state=resource_info->iconic ? IconicState :
14784         NormalState;
14785       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14786         &windows->backdrop);
14787       if (display_image->debug != MagickFalse)
14788         (void) LogMagickEvent(X11Event,GetMagickModule(),
14789           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14790       (void) XMapWindow(display,windows->backdrop.id);
14791       (void) XClearWindow(display,windows->backdrop.id);
14792       if (windows->image.id != (Window) NULL)
14793         {
14794           (void) XDestroyWindow(display,windows->image.id);
14795           windows->image.id=(Window) NULL;
14796         }
14797       /*
14798         Position image in the center the backdrop.
14799       */
14800       windows->image.flags|=USPosition;
14801       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14802         (windows->image.width/2);
14803       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14804         (windows->image.height/2);
14805     }
14806   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14807   manager_hints->icon_window=windows->icon.id;
14808   manager_hints->input=MagickTrue;
14809   manager_hints->initial_state=resource_info->iconic ? IconicState :
14810     NormalState;
14811   if (windows->group_leader.id != (Window) NULL)
14812     {
14813       /*
14814         Follow the leader.
14815       */
14816       manager_hints->flags|=WindowGroupHint;
14817       manager_hints->window_group=windows->group_leader.id;
14818       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14819       if (display_image->debug != MagickFalse)
14820         (void) LogMagickEvent(X11Event,GetMagickModule(),
14821           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14822     }
14823   XMakeWindow(display,
14824     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14825     argv,argc,class_hints,manager_hints,&windows->image);
14826   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14827     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14828   if (windows->group_leader.id != (Window) NULL)
14829     (void) XSetTransientForHint(display,windows->image.id,
14830       windows->group_leader.id);
14831   if (display_image->debug != MagickFalse)
14832     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14833       windows->image.id);
14834   /*
14835     Initialize Info widget.
14836   */
14837   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14838     &windows->info);
14839   (void) CloneString(&windows->info.name,"Info");
14840   (void) CloneString(&windows->info.icon_name,"Info");
14841   windows->info.border_width=1;
14842   windows->info.x=2;
14843   windows->info.y=2;
14844   windows->info.flags|=PPosition;
14845   windows->info.attributes.win_gravity=UnmapGravity;
14846   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14847     StructureNotifyMask;
14848   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14849   manager_hints->input=MagickFalse;
14850   manager_hints->initial_state=NormalState;
14851   manager_hints->window_group=windows->image.id;
14852   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14853     &windows->info);
14854   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14855     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14856   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14857     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14858   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14859   if (windows->image.mapped != MagickFalse)
14860     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14861   if (display_image->debug != MagickFalse)
14862     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14863       windows->info.id);
14864   /*
14865     Initialize Command widget.
14866   */
14867   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14868     resource_info,&windows->command);
14869   windows->command.data=MagickMenus;
14870   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14871   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14872     resource_info->client_name);
14873   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14874     resource_name,"geometry",(char *) NULL);
14875   (void) CloneString(&windows->command.name,MagickTitle);
14876   windows->command.border_width=0;
14877   windows->command.flags|=PPosition;
14878   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14879     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14880     OwnerGrabButtonMask | StructureNotifyMask;
14881   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14882   manager_hints->input=MagickTrue;
14883   manager_hints->initial_state=NormalState;
14884   manager_hints->window_group=windows->image.id;
14885   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14886     &windows->command);
14887   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14888     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14889     HighlightHeight);
14890   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14891     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14892   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14893   if (windows->command.mapped != MagickFalse)
14894     (void) XMapRaised(display,windows->command.id);
14895   if (display_image->debug != MagickFalse)
14896     (void) LogMagickEvent(X11Event,GetMagickModule(),
14897       "Window id: 0x%lx (command)",windows->command.id);
14898   /*
14899     Initialize Widget window.
14900   */
14901   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14902     resource_info,&windows->widget);
14903   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14904     resource_info->client_name);
14905   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14906     resource_name,"geometry",(char *) NULL);
14907   windows->widget.border_width=0;
14908   windows->widget.flags|=PPosition;
14909   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14910     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14911     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14912     StructureNotifyMask;
14913   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14914   manager_hints->input=MagickTrue;
14915   manager_hints->initial_state=NormalState;
14916   manager_hints->window_group=windows->image.id;
14917   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14918     &windows->widget);
14919   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14920     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14921   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14922     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14923   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14924   if (display_image->debug != MagickFalse)
14925     (void) LogMagickEvent(X11Event,GetMagickModule(),
14926       "Window id: 0x%lx (widget)",windows->widget.id);
14927   /*
14928     Initialize popup window.
14929   */
14930   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14931     resource_info,&windows->popup);
14932   windows->popup.border_width=0;
14933   windows->popup.flags|=PPosition;
14934   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14935     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14936     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14937   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14938   manager_hints->input=MagickTrue;
14939   manager_hints->initial_state=NormalState;
14940   manager_hints->window_group=windows->image.id;
14941   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14942     &windows->popup);
14943   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14944     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14945   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14946     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14947   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14948   if (display_image->debug != MagickFalse)
14949     (void) LogMagickEvent(X11Event,GetMagickModule(),
14950       "Window id: 0x%lx (pop up)",windows->popup.id);
14951   /*
14952     Initialize Magnify window and cursor.
14953   */
14954   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14955     resource_info,&windows->magnify);
14956   if (resource_info->use_shared_memory == MagickFalse)
14957     windows->magnify.shared_memory=MagickFalse;
14958   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14959     resource_info->client_name);
14960   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14961     resource_name,"geometry",(char *) NULL);
14962   (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,
14963     "Magnify %uX",resource_info->magnify);
14964   if (windows->magnify.cursor != (Cursor) NULL)
14965     (void) XFreeCursor(display,windows->magnify.cursor);
14966   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14967     map_info->colormap,resource_info->background_color,
14968     resource_info->foreground_color);
14969   if (windows->magnify.cursor == (Cursor) NULL)
14970     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14971       display_image->filename);
14972   windows->magnify.width=MagnifySize;
14973   windows->magnify.height=MagnifySize;
14974   windows->magnify.flags|=PPosition;
14975   windows->magnify.min_width=MagnifySize;
14976   windows->magnify.min_height=MagnifySize;
14977   windows->magnify.width_inc=MagnifySize;
14978   windows->magnify.height_inc=MagnifySize;
14979   windows->magnify.data=resource_info->magnify;
14980   windows->magnify.attributes.cursor=windows->magnify.cursor;
14981   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14982     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14983     StructureNotifyMask;
14984   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14985   manager_hints->input=MagickTrue;
14986   manager_hints->initial_state=NormalState;
14987   manager_hints->window_group=windows->image.id;
14988   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14989     &windows->magnify);
14990   if (display_image->debug != MagickFalse)
14991     (void) LogMagickEvent(X11Event,GetMagickModule(),
14992       "Window id: 0x%lx (magnify)",windows->magnify.id);
14993   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14994   /*
14995     Initialize panning window.
14996   */
14997   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14998     resource_info,&windows->pan);
14999   (void) CloneString(&windows->pan.name,"Pan Icon");
15000   windows->pan.width=windows->icon.width;
15001   windows->pan.height=windows->icon.height;
15002   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
15003     resource_info->client_name);
15004   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15005     resource_name,"geometry",(char *) NULL);
15006   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15007     &windows->pan.width,&windows->pan.height);
15008   windows->pan.flags|=PPosition;
15009   windows->pan.immutable=MagickTrue;
15010   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15011     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15012     StructureNotifyMask;
15013   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15014   manager_hints->input=MagickFalse;
15015   manager_hints->initial_state=NormalState;
15016   manager_hints->window_group=windows->image.id;
15017   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15018     &windows->pan);
15019   if (display_image->debug != MagickFalse)
15020     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15021       windows->pan.id);
15022   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15023   if (windows->info.mapped != MagickFalse)
15024     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15025   if ((windows->image.mapped == MagickFalse) ||
15026       (windows->backdrop.id != (Window) NULL))
15027     (void) XMapWindow(display,windows->image.id);
15028   /*
15029     Set our progress monitor and warning handlers.
15030   */
15031   if (warning_handler == (WarningHandler) NULL)
15032     {
15033       warning_handler=resource_info->display_warnings ?
15034         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15035       warning_handler=resource_info->display_warnings ?
15036         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15037     }
15038   /*
15039     Initialize Image and Magnify X images.
15040   */
15041   windows->image.x=0;
15042   windows->image.y=0;
15043   windows->magnify.shape=MagickFalse;
15044   width=(unsigned int) display_image->columns;
15045   height=(unsigned int) display_image->rows;
15046   if ((display_image->columns != width) || (display_image->rows != height))
15047     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15048       display_image->filename);
15049   status=XMakeImage(display,resource_info,&windows->image,display_image,
15050     width,height,exception);
15051   if (status == MagickFalse)
15052     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15053       display_image->filename);
15054   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15055     windows->magnify.width,windows->magnify.height,exception);
15056   if (status == MagickFalse)
15057     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15058       display_image->filename);
15059   if (windows->magnify.mapped != MagickFalse)
15060     (void) XMapRaised(display,windows->magnify.id);
15061   if (windows->pan.mapped != MagickFalse)
15062     (void) XMapRaised(display,windows->pan.id);
15063   windows->image.window_changes.width=(int) display_image->columns;
15064   windows->image.window_changes.height=(int) display_image->rows;
15065   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15066   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15067   (void) XSync(display,MagickFalse);
15068   /*
15069     Respond to events.
15070   */
15071   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15072   timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15073   update_time=0;
15074   if (resource_info->update != MagickFalse)
15075     {
15076       MagickBooleanType
15077         status;
15078 
15079       /*
15080         Determine when file data was last modified.
15081       */
15082       status=GetPathAttributes(display_image->filename,&attributes);
15083       if (status != MagickFalse)
15084         update_time=attributes.st_mtime;
15085     }
15086   *state&=(~FormerImageState);
15087   *state&=(~MontageImageState);
15088   *state&=(~NextImageState);
15089   do
15090   {
15091     /*
15092       Handle a window event.
15093     */
15094     if (windows->image.mapped != MagickFalse)
15095       if ((display_image->delay != 0) || (resource_info->update != 0))
15096         {
15097           if (timer < GetMagickTime())
15098             {
15099               if (resource_info->update == MagickFalse)
15100                 *state|=NextImageState | ExitState;
15101               else
15102                 {
15103                   MagickBooleanType
15104                     status;
15105 
15106                   /*
15107                     Determine if image file was modified.
15108                   */
15109                   status=GetPathAttributes(display_image->filename,&attributes);
15110                   if (status != MagickFalse)
15111                     if (update_time != attributes.st_mtime)
15112                       {
15113                         /*
15114                           Redisplay image.
15115                         */
15116                         (void) FormatLocaleString(
15117                           resource_info->image_info->filename,MagickPathExtent,
15118                           "%s:%s",display_image->magick,
15119                           display_image->filename);
15120                         nexus=ReadImage(resource_info->image_info,exception);
15121                         if (nexus != (Image *) NULL)
15122                           *state|=NextImageState | ExitState;
15123                       }
15124                   delay=display_image->delay/MagickMax(
15125                     display_image->ticks_per_second,1L);
15126                   timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15127                 }
15128             }
15129           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15130             {
15131               /*
15132                 Do not block if delay > 0.
15133               */
15134               XDelay(display,SuspendTime << 2);
15135               continue;
15136             }
15137         }
15138     timestamp=GetMagickTime();
15139     (void) XNextEvent(display,&event);
15140     if ((windows->image.stasis == MagickFalse) ||
15141         (windows->magnify.stasis == MagickFalse))
15142       {
15143         if ((GetMagickTime()-timestamp) > 0)
15144           {
15145             windows->image.stasis=MagickTrue;
15146             windows->magnify.stasis=MagickTrue;
15147           }
15148       }
15149     if (event.xany.window == windows->command.id)
15150       {
15151         /*
15152           Select a command from the Command widget.
15153         */
15154         id=XCommandWidget(display,windows,CommandMenu,&event);
15155         if (id < 0)
15156           continue;
15157         (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15158         command_type=CommandMenus[id];
15159         if (id < MagickMenus)
15160           {
15161             /*
15162               Select a command from a pop-up menu.
15163             */
15164             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15165               command);
15166             if (entry < 0)
15167               continue;
15168             (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15169             command_type=Commands[id][entry];
15170           }
15171         if (command_type != NullCommand)
15172           nexus=XMagickCommand(display,resource_info,windows,command_type,
15173             &display_image,exception);
15174         continue;
15175       }
15176     switch (event.type)
15177     {
15178       case ButtonPress:
15179       {
15180         if (display_image->debug != MagickFalse)
15181           (void) LogMagickEvent(X11Event,GetMagickModule(),
15182             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15183             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15184         if ((event.xbutton.button == Button3) &&
15185             (event.xbutton.state & Mod1Mask))
15186           {
15187             /*
15188               Convert Alt-Button3 to Button2.
15189             */
15190             event.xbutton.button=Button2;
15191             event.xbutton.state&=(~Mod1Mask);
15192           }
15193         if (event.xbutton.window == windows->backdrop.id)
15194           {
15195             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15196               event.xbutton.time);
15197             break;
15198           }
15199         if (event.xbutton.window == windows->image.id)
15200           {
15201             switch (event.xbutton.button)
15202             {
15203               case Button1:
15204               {
15205                 if (resource_info->immutable)
15206                   {
15207                     /*
15208                       Select a command from the Virtual menu.
15209                     */
15210                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15211                       command);
15212                     if (entry >= 0)
15213                       nexus=XMagickCommand(display,resource_info,windows,
15214                         VirtualCommands[entry],&display_image,exception);
15215                     break;
15216                   }
15217                 /*
15218                   Map/unmap Command widget.
15219                 */
15220                 if (windows->command.mapped != MagickFalse)
15221                   (void) XWithdrawWindow(display,windows->command.id,
15222                     windows->command.screen);
15223                 else
15224                   {
15225                     (void) XCommandWidget(display,windows,CommandMenu,
15226                       (XEvent *) NULL);
15227                     (void) XMapRaised(display,windows->command.id);
15228                   }
15229                 break;
15230               }
15231               case Button2:
15232               {
15233                 /*
15234                   User pressed the image magnify button.
15235                 */
15236                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15237                   &display_image,exception);
15238                 XMagnifyImage(display,windows,&event,exception);
15239                 break;
15240               }
15241               case Button3:
15242               {
15243                 if (resource_info->immutable)
15244                   {
15245                     /*
15246                       Select a command from the Virtual menu.
15247                     */
15248                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15249                       command);
15250                     if (entry >= 0)
15251                       nexus=XMagickCommand(display,resource_info,windows,
15252                         VirtualCommands[entry],&display_image,exception);
15253                     break;
15254                   }
15255                 if (display_image->montage != (char *) NULL)
15256                   {
15257                     /*
15258                       Open or delete a tile from a visual image directory.
15259                     */
15260                     nexus=XTileImage(display,resource_info,windows,
15261                       display_image,&event,exception);
15262                     if (nexus != (Image *) NULL)
15263                       *state|=MontageImageState | NextImageState | ExitState;
15264                     vid_info.x=(short int) windows->image.x;
15265                     vid_info.y=(short int) windows->image.y;
15266                     break;
15267                   }
15268                 /*
15269                   Select a command from the Short Cuts menu.
15270                 */
15271                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15272                   command);
15273                 if (entry >= 0)
15274                   nexus=XMagickCommand(display,resource_info,windows,
15275                     ShortCutsCommands[entry],&display_image,exception);
15276                 break;
15277               }
15278               case Button4:
15279               {
15280                 /*
15281                   Wheel up.
15282                 */
15283                 XTranslateImage(display,windows,*image,XK_Up);
15284                 break;
15285               }
15286               case Button5:
15287               {
15288                 /*
15289                   Wheel down.
15290                 */
15291                 XTranslateImage(display,windows,*image,XK_Down);
15292                 break;
15293               }
15294               default:
15295                 break;
15296             }
15297             break;
15298           }
15299         if (event.xbutton.window == windows->magnify.id)
15300           {
15301             const char
15302               *const MagnifyMenu[] =
15303               {
15304                 "2",
15305                 "4",
15306                 "5",
15307                 "6",
15308                 "7",
15309                 "8",
15310                 "9",
15311                 "3",
15312                 (char *) NULL,
15313               };
15314 
15315             int
15316               factor;
15317 
15318             static KeySym
15319               MagnifyCommands[] =
15320               {
15321                 XK_2,
15322                 XK_4,
15323                 XK_5,
15324                 XK_6,
15325                 XK_7,
15326                 XK_8,
15327                 XK_9,
15328                 XK_3
15329               };
15330 
15331             /*
15332               Select a magnify factor from the pop-up menu.
15333             */
15334             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15335             if (factor >= 0)
15336               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15337                 exception);
15338             break;
15339           }
15340         if (event.xbutton.window == windows->pan.id)
15341           {
15342             switch (event.xbutton.button)
15343             {
15344               case Button4:
15345               {
15346                 /*
15347                   Wheel up.
15348                 */
15349                 XTranslateImage(display,windows,*image,XK_Up);
15350                 break;
15351               }
15352               case Button5:
15353               {
15354                 /*
15355                   Wheel down.
15356                 */
15357                 XTranslateImage(display,windows,*image,XK_Down);
15358                 break;
15359               }
15360               default:
15361               {
15362                 XPanImage(display,windows,&event,exception);
15363                 break;
15364               }
15365             }
15366             break;
15367           }
15368         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15369           1L);
15370         timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15371         break;
15372       }
15373       case ButtonRelease:
15374       {
15375         if (display_image->debug != MagickFalse)
15376           (void) LogMagickEvent(X11Event,GetMagickModule(),
15377             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15378             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15379         break;
15380       }
15381       case ClientMessage:
15382       {
15383         if (display_image->debug != MagickFalse)
15384           (void) LogMagickEvent(X11Event,GetMagickModule(),
15385             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15386             event.xclient.message_type,event.xclient.format,(unsigned long)
15387             event.xclient.data.l[0]);
15388         if (event.xclient.message_type == windows->im_protocols)
15389           {
15390             if (*event.xclient.data.l == (long) windows->im_update_widget)
15391               {
15392                 (void) CloneString(&windows->command.name,MagickTitle);
15393                 windows->command.data=MagickMenus;
15394                 (void) XCommandWidget(display,windows,CommandMenu,
15395                   (XEvent *) NULL);
15396                 break;
15397               }
15398             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15399               {
15400                 /*
15401                   Update graphic context and window colormap.
15402                 */
15403                 for (i=0; i < (int) number_windows; i++)
15404                 {
15405                   if (magick_windows[i]->id == windows->icon.id)
15406                     continue;
15407                   context_values.background=pixel->background_color.pixel;
15408                   context_values.foreground=pixel->foreground_color.pixel;
15409                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15410                     context_mask,&context_values);
15411                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15412                     context_mask,&context_values);
15413                   context_values.background=pixel->foreground_color.pixel;
15414                   context_values.foreground=pixel->background_color.pixel;
15415                   context_values.plane_mask=context_values.background ^
15416                     context_values.foreground;
15417                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15418                     (size_t) (context_mask | GCPlaneMask),
15419                     &context_values);
15420                   magick_windows[i]->attributes.background_pixel=
15421                     pixel->background_color.pixel;
15422                   magick_windows[i]->attributes.border_pixel=
15423                     pixel->border_color.pixel;
15424                   magick_windows[i]->attributes.colormap=map_info->colormap;
15425                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15426                     (unsigned long) magick_windows[i]->mask,
15427                     &magick_windows[i]->attributes);
15428                 }
15429                 if (windows->pan.mapped != MagickFalse)
15430                   {
15431                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15432                       windows->pan.pixmap);
15433                     (void) XClearWindow(display,windows->pan.id);
15434                     XDrawPanRectangle(display,windows);
15435                   }
15436                 if (windows->backdrop.id != (Window) NULL)
15437                   (void) XInstallColormap(display,map_info->colormap);
15438                 break;
15439               }
15440             if (*event.xclient.data.l == (long) windows->im_former_image)
15441               {
15442                 *state|=FormerImageState | ExitState;
15443                 break;
15444               }
15445             if (*event.xclient.data.l == (long) windows->im_next_image)
15446               {
15447                 *state|=NextImageState | ExitState;
15448                 break;
15449               }
15450             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15451               {
15452                 *state|=RetainColorsState;
15453                 break;
15454               }
15455             if (*event.xclient.data.l == (long) windows->im_exit)
15456               {
15457                 *state|=ExitState;
15458                 break;
15459               }
15460             break;
15461           }
15462         if (event.xclient.message_type == windows->dnd_protocols)
15463           {
15464             Atom
15465               selection,
15466               type;
15467 
15468             int
15469               format,
15470               status;
15471 
15472             unsigned char
15473               *data;
15474 
15475             unsigned long
15476               after,
15477               length;
15478 
15479             /*
15480               Display image named by the Drag-and-Drop selection.
15481             */
15482             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15483               break;
15484             selection=XInternAtom(display,"DndSelection",MagickFalse);
15485             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15486               MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15487               &length,&after,&data);
15488             if ((status != Success) || (length == 0))
15489               break;
15490             if (*event.xclient.data.l == 2)
15491               {
15492                 /*
15493                   Offix DND.
15494                 */
15495                 (void) CopyMagickString(resource_info->image_info->filename,
15496                   (char *) data,MagickPathExtent);
15497               }
15498             else
15499               {
15500                 /*
15501                   XDND.
15502                 */
15503                 if (strncmp((char *) data, "file:", 5) != 0)
15504                   {
15505                     (void) XFree((void *) data);
15506                     break;
15507                   }
15508                 (void) CopyMagickString(resource_info->image_info->filename,
15509                   ((char *) data)+5,MagickPathExtent);
15510               }
15511             nexus=ReadImage(resource_info->image_info,exception);
15512             CatchException(exception);
15513             if (nexus != (Image *) NULL)
15514               *state|=NextImageState | ExitState;
15515             (void) XFree((void *) data);
15516             break;
15517           }
15518         /*
15519           If client window delete message, exit.
15520         */
15521         if (event.xclient.message_type != windows->wm_protocols)
15522           break;
15523         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15524           break;
15525         (void) XWithdrawWindow(display,event.xclient.window,
15526           visual_info->screen);
15527         if (event.xclient.window == windows->image.id)
15528           {
15529             *state|=ExitState;
15530             break;
15531           }
15532         if (event.xclient.window == windows->pan.id)
15533           {
15534             /*
15535               Restore original image size when pan window is deleted.
15536             */
15537             windows->image.window_changes.width=windows->image.ximage->width;
15538             windows->image.window_changes.height=windows->image.ximage->height;
15539             (void) XConfigureImage(display,resource_info,windows,
15540               display_image,exception);
15541           }
15542         break;
15543       }
15544       case ConfigureNotify:
15545       {
15546         if (display_image->debug != MagickFalse)
15547           (void) LogMagickEvent(X11Event,GetMagickModule(),
15548             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15549             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15550             event.xconfigure.y,event.xconfigure.send_event);
15551         if (event.xconfigure.window == windows->image.id)
15552           {
15553             /*
15554               Image window has a new configuration.
15555             */
15556             if (event.xconfigure.send_event != 0)
15557               {
15558                 XWindowChanges
15559                   window_changes;
15560 
15561                 /*
15562                   Position the transient windows relative of the Image window.
15563                 */
15564                 if (windows->command.geometry == (char *) NULL)
15565                   if (windows->command.mapped == MagickFalse)
15566                     {
15567                       windows->command.x=event.xconfigure.x-
15568                         windows->command.width-25;
15569                       windows->command.y=event.xconfigure.y;
15570                       XConstrainWindowPosition(display,&windows->command);
15571                       window_changes.x=windows->command.x;
15572                       window_changes.y=windows->command.y;
15573                       (void) XReconfigureWMWindow(display,windows->command.id,
15574                         windows->command.screen,(unsigned int) (CWX | CWY),
15575                         &window_changes);
15576                     }
15577                 if (windows->widget.geometry == (char *) NULL)
15578                   if (windows->widget.mapped == MagickFalse)
15579                     {
15580                       windows->widget.x=event.xconfigure.x+
15581                         event.xconfigure.width/10;
15582                       windows->widget.y=event.xconfigure.y+
15583                         event.xconfigure.height/10;
15584                       XConstrainWindowPosition(display,&windows->widget);
15585                       window_changes.x=windows->widget.x;
15586                       window_changes.y=windows->widget.y;
15587                       (void) XReconfigureWMWindow(display,windows->widget.id,
15588                         windows->widget.screen,(unsigned int) (CWX | CWY),
15589                         &window_changes);
15590                     }
15591                 if (windows->magnify.geometry == (char *) NULL)
15592                   if (windows->magnify.mapped == MagickFalse)
15593                     {
15594                       windows->magnify.x=event.xconfigure.x+
15595                         event.xconfigure.width+25;
15596                       windows->magnify.y=event.xconfigure.y;
15597                       XConstrainWindowPosition(display,&windows->magnify);
15598                       window_changes.x=windows->magnify.x;
15599                       window_changes.y=windows->magnify.y;
15600                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15601                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15602                         &window_changes);
15603                     }
15604                 if (windows->pan.geometry == (char *) NULL)
15605                   if (windows->pan.mapped == MagickFalse)
15606                     {
15607                       windows->pan.x=event.xconfigure.x+
15608                         event.xconfigure.width+25;
15609                       windows->pan.y=event.xconfigure.y+
15610                         windows->magnify.height+50;
15611                       XConstrainWindowPosition(display,&windows->pan);
15612                       window_changes.x=windows->pan.x;
15613                       window_changes.y=windows->pan.y;
15614                       (void) XReconfigureWMWindow(display,windows->pan.id,
15615                         windows->pan.screen,(unsigned int) (CWX | CWY),
15616                         &window_changes);
15617                     }
15618               }
15619             if ((event.xconfigure.width == (int) windows->image.width) &&
15620                 (event.xconfigure.height == (int) windows->image.height))
15621               break;
15622             windows->image.width=(unsigned int) event.xconfigure.width;
15623             windows->image.height=(unsigned int) event.xconfigure.height;
15624             windows->image.x=0;
15625             windows->image.y=0;
15626             if (display_image->montage != (char *) NULL)
15627               {
15628                 windows->image.x=vid_info.x;
15629                 windows->image.y=vid_info.y;
15630               }
15631             if (windows->image.mapped != MagickFalse &&
15632                 windows->image.stasis != MagickFalse)
15633               {
15634                 /*
15635                   Update image window configuration.
15636                 */
15637                 windows->image.window_changes.width=event.xconfigure.width;
15638                 windows->image.window_changes.height=event.xconfigure.height;
15639                 (void) XConfigureImage(display,resource_info,windows,
15640                   display_image,exception);
15641               }
15642             /*
15643               Update pan window configuration.
15644             */
15645             if ((event.xconfigure.width < windows->image.ximage->width) ||
15646                 (event.xconfigure.height < windows->image.ximage->height))
15647               {
15648                 (void) XMapRaised(display,windows->pan.id);
15649                 XDrawPanRectangle(display,windows);
15650               }
15651             else
15652               if (windows->pan.mapped != MagickFalse)
15653                 (void) XWithdrawWindow(display,windows->pan.id,
15654                   windows->pan.screen);
15655             break;
15656           }
15657         if (event.xconfigure.window == windows->magnify.id)
15658           {
15659             unsigned int
15660               magnify;
15661 
15662             /*
15663               Magnify window has a new configuration.
15664             */
15665             windows->magnify.width=(unsigned int) event.xconfigure.width;
15666             windows->magnify.height=(unsigned int) event.xconfigure.height;
15667             if (windows->magnify.mapped == MagickFalse)
15668               break;
15669             magnify=1;
15670             while ((int) magnify <= event.xconfigure.width)
15671               magnify<<=1;
15672             while ((int) magnify <= event.xconfigure.height)
15673               magnify<<=1;
15674             magnify>>=1;
15675             if (((int) magnify != event.xconfigure.width) ||
15676                 ((int) magnify != event.xconfigure.height))
15677               {
15678                 window_changes.width=(int) magnify;
15679                 window_changes.height=(int) magnify;
15680                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15681                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15682                   &window_changes);
15683                 break;
15684               }
15685             if (windows->magnify.mapped != MagickFalse &&
15686                 windows->magnify.stasis != MagickFalse)
15687               {
15688                 status=XMakeImage(display,resource_info,&windows->magnify,
15689                   display_image,windows->magnify.width,windows->magnify.height,
15690                   exception);
15691                 XMakeMagnifyImage(display,windows,exception);
15692               }
15693             break;
15694           }
15695         if (windows->magnify.mapped != MagickFalse &&
15696             (event.xconfigure.window == windows->pan.id))
15697           {
15698             /*
15699               Pan icon window has a new configuration.
15700             */
15701             if (event.xconfigure.send_event != 0)
15702               {
15703                 windows->pan.x=event.xconfigure.x;
15704                 windows->pan.y=event.xconfigure.y;
15705               }
15706             windows->pan.width=(unsigned int) event.xconfigure.width;
15707             windows->pan.height=(unsigned int) event.xconfigure.height;
15708             break;
15709           }
15710         if (event.xconfigure.window == windows->icon.id)
15711           {
15712             /*
15713               Icon window has a new configuration.
15714             */
15715             windows->icon.width=(unsigned int) event.xconfigure.width;
15716             windows->icon.height=(unsigned int) event.xconfigure.height;
15717             break;
15718           }
15719         break;
15720       }
15721       case DestroyNotify:
15722       {
15723         /*
15724           Group leader has exited.
15725         */
15726         if (display_image->debug != MagickFalse)
15727           (void) LogMagickEvent(X11Event,GetMagickModule(),
15728             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15729         if (event.xdestroywindow.window == windows->group_leader.id)
15730           {
15731             *state|=ExitState;
15732             break;
15733           }
15734         break;
15735       }
15736       case EnterNotify:
15737       {
15738         /*
15739           Selectively install colormap.
15740         */
15741         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15742           if (event.xcrossing.mode != NotifyUngrab)
15743             XInstallColormap(display,map_info->colormap);
15744         break;
15745       }
15746       case Expose:
15747       {
15748         if (display_image->debug != MagickFalse)
15749           (void) LogMagickEvent(X11Event,GetMagickModule(),
15750             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15751             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15752             event.xexpose.y);
15753         /*
15754           Refresh windows that are now exposed.
15755         */
15756         if ((event.xexpose.window == windows->image.id) &&
15757             windows->image.mapped != MagickFalse)
15758           {
15759             XRefreshWindow(display,&windows->image,&event);
15760             delay=display_image->delay/MagickMax(
15761               display_image->ticks_per_second,1L);
15762             timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15763             break;
15764           }
15765         if ((event.xexpose.window == windows->magnify.id) &&
15766             windows->magnify.mapped != MagickFalse)
15767           {
15768             XMakeMagnifyImage(display,windows,exception);
15769             break;
15770           }
15771         if (event.xexpose.window == windows->pan.id)
15772           {
15773             XDrawPanRectangle(display,windows);
15774             break;
15775           }
15776         if (event.xexpose.window == windows->icon.id)
15777           {
15778             XRefreshWindow(display,&windows->icon,&event);
15779             break;
15780           }
15781         break;
15782       }
15783       case KeyPress:
15784       {
15785         int
15786           length;
15787 
15788         /*
15789           Respond to a user key press.
15790         */
15791         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15792           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15793         *(command+length)='\0';
15794         if (display_image->debug != MagickFalse)
15795           (void) LogMagickEvent(X11Event,GetMagickModule(),
15796             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15797             key_symbol,command);
15798         if (event.xkey.window == windows->image.id)
15799           {
15800             command_type=XImageWindowCommand(display,resource_info,windows,
15801               event.xkey.state,key_symbol,&display_image,exception);
15802             if (command_type != NullCommand)
15803               nexus=XMagickCommand(display,resource_info,windows,command_type,
15804                 &display_image,exception);
15805           }
15806         if (event.xkey.window == windows->magnify.id)
15807           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15808             exception);
15809         if (event.xkey.window == windows->pan.id)
15810           {
15811             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15812               (void) XWithdrawWindow(display,windows->pan.id,
15813                 windows->pan.screen);
15814             else
15815               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15816                 XTextViewHelp(display,resource_info,windows,MagickFalse,
15817                   "Help Viewer - Image Pan",ImagePanHelp);
15818               else
15819                 XTranslateImage(display,windows,*image,key_symbol);
15820           }
15821         delay=display_image->delay/MagickMax(
15822           display_image->ticks_per_second,1L);
15823         timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15824         break;
15825       }
15826       case KeyRelease:
15827       {
15828         /*
15829           Respond to a user key release.
15830         */
15831         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15832           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15833         if (display_image->debug != MagickFalse)
15834           (void) LogMagickEvent(X11Event,GetMagickModule(),
15835             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15836         break;
15837       }
15838       case LeaveNotify:
15839       {
15840         /*
15841           Selectively uninstall colormap.
15842         */
15843         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15844           if (event.xcrossing.mode != NotifyUngrab)
15845             XUninstallColormap(display,map_info->colormap);
15846         break;
15847       }
15848       case MapNotify:
15849       {
15850         if (display_image->debug != MagickFalse)
15851           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15852             event.xmap.window);
15853         if (event.xmap.window == windows->backdrop.id)
15854           {
15855             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15856               CurrentTime);
15857             windows->backdrop.mapped=MagickTrue;
15858             break;
15859           }
15860         if (event.xmap.window == windows->image.id)
15861           {
15862             if (windows->backdrop.id != (Window) NULL)
15863               (void) XInstallColormap(display,map_info->colormap);
15864             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15865               {
15866                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15867                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15868               }
15869             if (((int) windows->image.width < windows->image.ximage->width) ||
15870                 ((int) windows->image.height < windows->image.ximage->height))
15871               (void) XMapRaised(display,windows->pan.id);
15872             windows->image.mapped=MagickTrue;
15873             break;
15874           }
15875         if (event.xmap.window == windows->magnify.id)
15876           {
15877             XMakeMagnifyImage(display,windows,exception);
15878             windows->magnify.mapped=MagickTrue;
15879             (void) XWithdrawWindow(display,windows->info.id,
15880               windows->info.screen);
15881             break;
15882           }
15883         if (event.xmap.window == windows->pan.id)
15884           {
15885             XMakePanImage(display,resource_info,windows,display_image,
15886               exception);
15887             windows->pan.mapped=MagickTrue;
15888             break;
15889           }
15890         if (event.xmap.window == windows->info.id)
15891           {
15892             windows->info.mapped=MagickTrue;
15893             break;
15894           }
15895         if (event.xmap.window == windows->icon.id)
15896           {
15897             MagickBooleanType
15898               taint;
15899 
15900             /*
15901               Create an icon image.
15902             */
15903             taint=display_image->taint;
15904             XMakeStandardColormap(display,icon_visual,icon_resources,
15905               display_image,icon_map,icon_pixel,exception);
15906             (void) XMakeImage(display,icon_resources,&windows->icon,
15907               display_image,windows->icon.width,windows->icon.height,
15908               exception);
15909             display_image->taint=taint;
15910             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15911               windows->icon.pixmap);
15912             (void) XClearWindow(display,windows->icon.id);
15913             (void) XWithdrawWindow(display,windows->info.id,
15914               windows->info.screen);
15915             windows->icon.mapped=MagickTrue;
15916             break;
15917           }
15918         if (event.xmap.window == windows->command.id)
15919           {
15920             windows->command.mapped=MagickTrue;
15921             break;
15922           }
15923         if (event.xmap.window == windows->popup.id)
15924           {
15925             windows->popup.mapped=MagickTrue;
15926             break;
15927           }
15928         if (event.xmap.window == windows->widget.id)
15929           {
15930             windows->widget.mapped=MagickTrue;
15931             break;
15932           }
15933         break;
15934       }
15935       case MappingNotify:
15936       {
15937         (void) XRefreshKeyboardMapping(&event.xmapping);
15938         break;
15939       }
15940       case NoExpose:
15941         break;
15942       case PropertyNotify:
15943       {
15944         Atom
15945           type;
15946 
15947         int
15948           format,
15949           status;
15950 
15951         unsigned char
15952           *data;
15953 
15954         unsigned long
15955           after,
15956           length;
15957 
15958         if (display_image->debug != MagickFalse)
15959           (void) LogMagickEvent(X11Event,GetMagickModule(),
15960             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15961             event.xproperty.atom,event.xproperty.state);
15962         if (event.xproperty.atom != windows->im_remote_command)
15963           break;
15964         /*
15965           Display image named by the remote command protocol.
15966         */
15967         status=XGetWindowProperty(display,event.xproperty.window,
15968           event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15969           AnyPropertyType,&type,&format,&length,&after,&data);
15970         if ((status != Success) || (length == 0))
15971           break;
15972         if (LocaleCompare((char *) data,"-quit") == 0)
15973           {
15974             XClientMessage(display,windows->image.id,windows->im_protocols,
15975               windows->im_exit,CurrentTime);
15976             (void) XFree((void *) data);
15977             break;
15978           }
15979         (void) CopyMagickString(resource_info->image_info->filename,
15980           (char *) data,MagickPathExtent);
15981         (void) XFree((void *) data);
15982         nexus=ReadImage(resource_info->image_info,exception);
15983         CatchException(exception);
15984         if (nexus != (Image *) NULL)
15985           *state|=NextImageState | ExitState;
15986         break;
15987       }
15988       case ReparentNotify:
15989       {
15990         if (display_image->debug != MagickFalse)
15991           (void) LogMagickEvent(X11Event,GetMagickModule(),
15992             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15993             event.xreparent.window);
15994         break;
15995       }
15996       case UnmapNotify:
15997       {
15998         if (display_image->debug != MagickFalse)
15999           (void) LogMagickEvent(X11Event,GetMagickModule(),
16000             "Unmap Notify: 0x%lx",event.xunmap.window);
16001         if (event.xunmap.window == windows->backdrop.id)
16002           {
16003             windows->backdrop.mapped=MagickFalse;
16004             break;
16005           }
16006         if (event.xunmap.window == windows->image.id)
16007           {
16008             windows->image.mapped=MagickFalse;
16009             break;
16010           }
16011         if (event.xunmap.window == windows->magnify.id)
16012           {
16013             windows->magnify.mapped=MagickFalse;
16014             break;
16015           }
16016         if (event.xunmap.window == windows->pan.id)
16017           {
16018             windows->pan.mapped=MagickFalse;
16019             break;
16020           }
16021         if (event.xunmap.window == windows->info.id)
16022           {
16023             windows->info.mapped=MagickFalse;
16024             break;
16025           }
16026         if (event.xunmap.window == windows->icon.id)
16027           {
16028             if (map_info->colormap == icon_map->colormap)
16029               XConfigureImageColormap(display,resource_info,windows,
16030                 display_image,exception);
16031             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16032               icon_pixel);
16033             windows->icon.mapped=MagickFalse;
16034             break;
16035           }
16036         if (event.xunmap.window == windows->command.id)
16037           {
16038             windows->command.mapped=MagickFalse;
16039             break;
16040           }
16041         if (event.xunmap.window == windows->popup.id)
16042           {
16043             if (windows->backdrop.id != (Window) NULL)
16044               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16045                 CurrentTime);
16046             windows->popup.mapped=MagickFalse;
16047             break;
16048           }
16049         if (event.xunmap.window == windows->widget.id)
16050           {
16051             if (windows->backdrop.id != (Window) NULL)
16052               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16053                 CurrentTime);
16054             windows->widget.mapped=MagickFalse;
16055             break;
16056           }
16057         break;
16058       }
16059       default:
16060       {
16061         if (display_image->debug != MagickFalse)
16062           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16063             event.type);
16064         break;
16065       }
16066     }
16067   } while (!(*state & ExitState));
16068   if ((*state & ExitState) == 0)
16069     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16070       &display_image,exception);
16071   else
16072     if (resource_info->confirm_edit != MagickFalse)
16073       {
16074         /*
16075           Query user if image has changed.
16076         */
16077         if ((resource_info->immutable == MagickFalse) &&
16078             display_image->taint != MagickFalse)
16079           {
16080             int
16081               status;
16082 
16083             status=XConfirmWidget(display,windows,"Your image changed.",
16084               "Do you want to save it");
16085             if (status == 0)
16086               *state&=(~ExitState);
16087             else
16088               if (status > 0)
16089                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16090                   &display_image,exception);
16091           }
16092       }
16093   if ((windows->visual_info->klass == GrayScale) ||
16094       (windows->visual_info->klass == PseudoColor) ||
16095       (windows->visual_info->klass == DirectColor))
16096     {
16097       /*
16098         Withdraw pan and Magnify window.
16099       */
16100       if (windows->info.mapped != MagickFalse)
16101         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16102       if (windows->magnify.mapped != MagickFalse)
16103         (void) XWithdrawWindow(display,windows->magnify.id,
16104           windows->magnify.screen);
16105       if (windows->command.mapped != MagickFalse)
16106         (void) XWithdrawWindow(display,windows->command.id,
16107           windows->command.screen);
16108     }
16109   if (windows->pan.mapped != MagickFalse)
16110     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16111   if (resource_info->backdrop == MagickFalse)
16112     if (windows->backdrop.mapped)
16113       {
16114         (void) XWithdrawWindow(display,windows->backdrop.id,
16115           windows->backdrop.screen);
16116         (void) XDestroyWindow(display,windows->backdrop.id);
16117         windows->backdrop.id=(Window) NULL;
16118         (void) XWithdrawWindow(display,windows->image.id,
16119           windows->image.screen);
16120         (void) XDestroyWindow(display,windows->image.id);
16121         windows->image.id=(Window) NULL;
16122       }
16123   XSetCursorState(display,windows,MagickTrue);
16124   XCheckRefreshWindows(display,windows);
16125   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16126     *state&=(~ExitState);
16127   if (*state & ExitState)
16128     {
16129       /*
16130         Free Standard Colormap.
16131       */
16132       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16133       if (resource_info->map_type == (char *) NULL)
16134         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16135       /*
16136         Free X resources.
16137       */
16138       if (resource_info->copy_image != (Image *) NULL)
16139         {
16140           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16141           resource_info->copy_image=NewImageList();
16142         }
16143       DestroyXResources();
16144     }
16145   (void) XSync(display,MagickFalse);
16146   /*
16147     Restore our progress monitor and warning handlers.
16148   */
16149   (void) SetErrorHandler(warning_handler);
16150   (void) SetWarningHandler(warning_handler);
16151   /*
16152     Change to home directory.
16153   */
16154   directory=getcwd(working_directory,MagickPathExtent);
16155   (void) directory;
16156   {
16157     int
16158       status;
16159 
16160     if (*resource_info->home_directory == '\0')
16161       (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16162     status=chdir(resource_info->home_directory);
16163     if (status == -1)
16164       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16165         "UnableToOpenFile","%s",resource_info->home_directory);
16166   }
16167   *image=display_image;
16168   return(nexus);
16169 }
16170 #else
16171 
16172 /*
16173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16174 %                                                                             %
16175 %                                                                             %
16176 %                                                                             %
16177 +   D i s p l a y I m a g e s                                                 %
16178 %                                                                             %
16179 %                                                                             %
16180 %                                                                             %
16181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16182 %
16183 %  DisplayImages() displays an image sequence to any X window screen.  It
16184 %  returns a value other than 0 if successful.  Check the exception member
16185 %  of image to determine the reason for any failure.
16186 %
16187 %  The format of the DisplayImages method is:
16188 %
16189 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16190 %        Image *images,ExceptionInfo *exception)
16191 %
16192 %  A description of each parameter follows:
16193 %
16194 %    o image_info: the image info.
16195 %
16196 %    o image: the image.
16197 %
16198 %    o exception: return any errors or warnings in this structure.
16199 %
16200 */
DisplayImages(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)16201 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16202   Image *image,ExceptionInfo *exception)
16203 {
16204   assert(image_info != (const ImageInfo *) NULL);
16205   assert(image_info->signature == MagickCoreSignature);
16206   assert(image != (Image *) NULL);
16207   assert(image->signature == MagickCoreSignature);
16208   (void) image_info;
16209   if (image->debug != MagickFalse)
16210     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16211   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16212     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16213   return(MagickFalse);
16214 }
16215 
16216 /*
16217 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16218 %                                                                             %
16219 %                                                                             %
16220 %                                                                             %
16221 +   R e m o t e D i s p l a y C o m m a n d                                   %
16222 %                                                                             %
16223 %                                                                             %
16224 %                                                                             %
16225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16226 %
16227 %  RemoteDisplayCommand() encourages a remote display program to display the
16228 %  specified image filename.
16229 %
16230 %  The format of the RemoteDisplayCommand method is:
16231 %
16232 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16233 %        const char *window,const char *filename,ExceptionInfo *exception)
16234 %
16235 %  A description of each parameter follows:
16236 %
16237 %    o image_info: the image info.
16238 %
16239 %    o window: Specifies the name or id of an X window.
16240 %
16241 %    o filename: the name of the image filename to display.
16242 %
16243 %    o exception: return any errors or warnings in this structure.
16244 %
16245 */
RemoteDisplayCommand(const ImageInfo * image_info,const char * window,const char * filename,ExceptionInfo * exception)16246 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16247   const char *window,const char *filename,ExceptionInfo *exception)
16248 {
16249   assert(image_info != (const ImageInfo *) NULL);
16250   assert(image_info->signature == MagickCoreSignature);
16251   assert(filename != (char *) NULL);
16252   (void) window;
16253   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16254   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16255     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16256   return(MagickFalse);
16257 }
16258 #endif
16259