Home | History | Annotate | Download | only in MagickCore
      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-2019 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 /*
     41   Include declarations.
     42 */
     43 #include "MagickCore/studio.h"
     44 #include "MagickCore/artifact.h"
     45 #include "MagickCore/attribute.h"
     46 #include "MagickCore/blob.h"
     47 #include "MagickCore/cache.h"
     48 #include "MagickCore/cache-private.h"
     49 #include "MagickCore/channel.h"
     50 #include "MagickCore/client.h"
     51 #include "MagickCore/color.h"
     52 #include "MagickCore/colorspace.h"
     53 #include "MagickCore/composite.h"
     54 #include "MagickCore/constitute.h"
     55 #include "MagickCore/decorate.h"
     56 #include "MagickCore/delegate.h"
     57 #include "MagickCore/display.h"
     58 #include "MagickCore/display-private.h"
     59 #include "MagickCore/distort.h"
     60 #include "MagickCore/draw.h"
     61 #include "MagickCore/effect.h"
     62 #include "MagickCore/enhance.h"
     63 #include "MagickCore/exception.h"
     64 #include "MagickCore/exception-private.h"
     65 #include "MagickCore/fx.h"
     66 #include "MagickCore/geometry.h"
     67 #include "MagickCore/image.h"
     68 #include "MagickCore/image-private.h"
     69 #include "MagickCore/list.h"
     70 #include "MagickCore/log.h"
     71 #include "MagickCore/magick.h"
     72 #include "MagickCore/memory_.h"
     73 #include "MagickCore/monitor.h"
     74 #include "MagickCore/monitor-private.h"
     75 #include "MagickCore/montage.h"
     76 #include "MagickCore/nt-base-private.h"
     77 #include "MagickCore/option.h"
     78 #include "MagickCore/paint.h"
     79 #include "MagickCore/pixel.h"
     80 #include "MagickCore/pixel-accessor.h"
     81 #include "MagickCore/property.h"
     82 #include "MagickCore/quantum.h"
     83 #include "MagickCore/quantum-private.h"
     84 #include "MagickCore/resize.h"
     85 #include "MagickCore/resource_.h"
     86 #include "MagickCore/shear.h"
     87 #include "MagickCore/segment.h"
     88 #include "MagickCore/statistic.h"
     89 #include "MagickCore/string_.h"
     90 #include "MagickCore/string-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/widget.h"
     98 #include "MagickCore/widget-private.h"
     99 #include "MagickCore/xwindow.h"
    100 #include "MagickCore/xwindow-private.h"
    101 
    102 #if defined(MAGICKCORE_X11_DELEGATE)
    104 /*
    105   Define declarations.
    106 */
    107 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
    108 
    109 /*
    111   Constant declarations.
    112 */
    113 static const unsigned char
    114   HighlightBitmap[8] =
    115   {
    116     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
    117   },
    118   OpaqueBitmap[8] =
    119   {
    120     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    121   },
    122   ShadowBitmap[8] =
    123   {
    124     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    125   };
    126 
    127 static const char
    128   *PageSizes[] =
    129   {
    130     "Letter",
    131     "Tabloid",
    132     "Ledger",
    133     "Legal",
    134     "Statement",
    135     "Executive",
    136     "A3",
    137     "A4",
    138     "A5",
    139     "B4",
    140     "B5",
    141     "Folio",
    142     "Quarto",
    143     "10x14",
    144     (char *) NULL
    145   };
    146 
    147 /*
    149   Help widget declarations.
    150 */
    151 static const char
    152   *ImageAnnotateHelp[] =
    153   {
    154     "In annotate mode, the Command widget has these options:",
    155     "",
    156     "    Font Name",
    157     "      fixed",
    158     "      variable",
    159     "      5x8",
    160     "      6x10",
    161     "      7x13bold",
    162     "      8x13bold",
    163     "      9x15bold",
    164     "      10x20",
    165     "      12x24",
    166     "      Browser...",
    167     "    Font Color",
    168     "      black",
    169     "      blue",
    170     "      cyan",
    171     "      green",
    172     "      gray",
    173     "      red",
    174     "      magenta",
    175     "      yellow",
    176     "      white",
    177     "      transparent",
    178     "      Browser...",
    179     "    Font Color",
    180     "      black",
    181     "      blue",
    182     "      cyan",
    183     "      green",
    184     "      gray",
    185     "      red",
    186     "      magenta",
    187     "      yellow",
    188     "      white",
    189     "      transparent",
    190     "      Browser...",
    191     "    Rotate Text",
    192     "      -90",
    193     "      -45",
    194     "      -30",
    195     "      0",
    196     "      30",
    197     "      45",
    198     "      90",
    199     "      180",
    200     "      Dialog...",
    201     "    Help",
    202     "    Dismiss",
    203     "",
    204     "Choose a font name from the Font Name sub-menu.  Additional",
    205     "font names can be specified with the font browser.  You can",
    206     "change the menu names by setting the X resources font1",
    207     "through font9.",
    208     "",
    209     "Choose a font color from the Font Color sub-menu.",
    210     "Additional font colors can be specified with the color",
    211     "browser.  You can change the menu colors by setting the X",
    212     "resources pen1 through pen9.",
    213     "",
    214     "If you select the color browser and press Grab, you can",
    215     "choose the font color by moving the pointer to the desired",
    216     "color on the screen and press any button.",
    217     "",
    218     "If you choose to rotate the text, choose Rotate Text from the",
    219     "menu and select an angle.  Typically you will only want to",
    220     "rotate one line of text at a time.  Depending on the angle you",
    221     "choose, subsequent lines may end up overwriting each other.",
    222     "",
    223     "Choosing a font and its color is optional.  The default font",
    224     "is fixed and the default color is black.  However, you must",
    225     "choose a location to begin entering text and press button 1.",
    226     "An underscore character will appear at the location of the",
    227     "pointer.  The cursor changes to a pencil to indicate you are",
    228     "in text mode.  To exit immediately, press Dismiss.",
    229     "",
    230     "In text mode, any key presses will display the character at",
    231     "the location of the underscore and advance the underscore",
    232     "cursor.  Enter your text and once completed press Apply to",
    233     "finish your image annotation.  To correct errors press BACK",
    234     "SPACE.  To delete an entire line of text, press DELETE.  Any",
    235     "text that exceeds the boundaries of the image window is",
    236     "automagically continued onto the next line.",
    237     "",
    238     "The actual color you request for the font is saved in the",
    239     "image.  However, the color that appears in your image window",
    240     "may be different.  For example, on a monochrome screen the",
    241     "text will appear black or white even if you choose the color",
    242     "red as the font color.  However, the image saved to a file",
    243     "with -write is written with red lettering.  To assure the",
    244     "correct color text in the final image, any PseudoClass image",
    245     "is promoted to DirectClass (see miff(5)).  To force a",
    246     "PseudoClass image to remain PseudoClass, use -colors.",
    247     (char *) NULL,
    248   },
    249   *ImageChopHelp[] =
    250   {
    251     "In chop mode, the Command widget has these options:",
    252     "",
    253     "    Direction",
    254     "      horizontal",
    255     "      vertical",
    256     "    Help",
    257     "    Dismiss",
    258     "",
    259     "If the you choose the horizontal direction (this the",
    260     "default), the area of the image between the two horizontal",
    261     "endpoints of the chop line is removed.  Otherwise, the area",
    262     "of the image between the two vertical endpoints of the chop",
    263     "line is removed.",
    264     "",
    265     "Select a location within the image window to begin your chop,",
    266     "press and hold any button.  Next, move the pointer to",
    267     "another location in the image.  As you move a line will",
    268     "connect the initial location and the pointer.  When you",
    269     "release the button, the area within the image to chop is",
    270     "determined by which direction you choose from the Command",
    271     "widget.",
    272     "",
    273     "To cancel the image chopping, move the pointer back to the",
    274     "starting point of the line and release the button.",
    275     (char *) NULL,
    276   },
    277   *ImageColorEditHelp[] =
    278   {
    279     "In color edit mode, the Command widget has these options:",
    280     "",
    281     "    Method",
    282     "      point",
    283     "      replace",
    284     "      floodfill",
    285     "      filltoborder",
    286     "      reset",
    287     "    Pixel Color",
    288     "      black",
    289     "      blue",
    290     "      cyan",
    291     "      green",
    292     "      gray",
    293     "      red",
    294     "      magenta",
    295     "      yellow",
    296     "      white",
    297     "      Browser...",
    298     "    Border Color",
    299     "      black",
    300     "      blue",
    301     "      cyan",
    302     "      green",
    303     "      gray",
    304     "      red",
    305     "      magenta",
    306     "      yellow",
    307     "      white",
    308     "      Browser...",
    309     "    Fuzz",
    310     "      0%",
    311     "      2%",
    312     "      5%",
    313     "      10%",
    314     "      15%",
    315     "      Dialog...",
    316     "    Undo",
    317     "    Help",
    318     "    Dismiss",
    319     "",
    320     "Choose a color editing method from the Method sub-menu",
    321     "of the Command widget.  The point method recolors any pixel",
    322     "selected with the pointer until the button is released.  The",
    323     "replace method recolors any pixel that matches the color of",
    324     "the pixel you select with a button press.  Floodfill recolors",
    325     "any pixel that matches the color of the pixel you select with",
    326     "a button press and is a neighbor.  Whereas filltoborder recolors",
    327     "any neighbor pixel that is not the border color.  Finally reset",
    328     "changes the entire image to the designated color.",
    329     "",
    330     "Next, choose a pixel color from the Pixel Color sub-menu.",
    331     "Additional pixel colors can be specified with the color",
    332     "browser.  You can change the menu colors by setting the X",
    333     "resources pen1 through pen9.",
    334     "",
    335     "Now press button 1 to select a pixel within the image window",
    336     "to change its color.  Additional pixels may be recolored as",
    337     "prescribed by the method you choose.",
    338     "",
    339     "If the Magnify widget is mapped, it can be helpful in positioning",
    340     "your pointer within the image (refer to button 2).",
    341     "",
    342     "The actual color you request for the pixels is saved in the",
    343     "image.  However, the color that appears in your image window",
    344     "may be different.  For example, on a monochrome screen the",
    345     "pixel will appear black or white even if you choose the",
    346     "color red as the pixel color.  However, the image saved to a",
    347     "file with -write is written with red pixels.  To assure the",
    348     "correct color text in the final image, any PseudoClass image",
    349     "is promoted to DirectClass (see miff(5)).  To force a",
    350     "PseudoClass image to remain PseudoClass, use -colors.",
    351     (char *) NULL,
    352   },
    353   *ImageCompositeHelp[] =
    354   {
    355     "First a widget window is displayed requesting you to enter an",
    356     "image name. Press Composite, Grab or type a file name.",
    357     "Press Cancel if you choose not to create a composite image.",
    358     "When you choose Grab, move the pointer to the desired window",
    359     "and press any button.",
    360     "",
    361     "If the Composite image does not have any matte information,",
    362     "you are informed and the file browser is displayed again.",
    363     "Enter the name of a mask image.  The image is typically",
    364     "grayscale and the same size as the composite image.  If the",
    365     "image is not grayscale, it is converted to grayscale and the",
    366     "resulting intensities are used as matte information.",
    367     "",
    368     "A small window appears showing the location of the cursor in",
    369     "the image window. You are now in composite mode.  To exit",
    370     "immediately, press Dismiss.  In composite mode, the Command",
    371     "widget has these options:",
    372     "",
    373     "    Operators",
    374     "      Over",
    375     "      In",
    376     "      Out",
    377     "      Atop",
    378     "      Xor",
    379     "      Plus",
    380     "      Minus",
    381     "      Add",
    382     "      Subtract",
    383     "      Difference",
    384     "      Multiply",
    385     "      Bumpmap",
    386     "      Copy",
    387     "      CopyRed",
    388     "      CopyGreen",
    389     "      CopyBlue",
    390     "      CopyOpacity",
    391     "      Clear",
    392     "    Dissolve",
    393     "    Displace",
    394     "    Help",
    395     "    Dismiss",
    396     "",
    397     "Choose a composite operation from the Operators sub-menu of",
    398     "the Command widget.  How each operator behaves is described",
    399     "below.  Image window is the image currently displayed on",
    400     "your X server and image is the image obtained with the File",
    401     "Browser widget.",
    402     "",
    403     "Over     The result is the union of the two image shapes,",
    404     "         with image obscuring image window in the region of",
    405     "         overlap.",
    406     "",
    407     "In       The result is simply image cut by the shape of",
    408     "         image window.  None of the image data of image",
    409     "         window is in the result.",
    410     "",
    411     "Out      The resulting image is image with the shape of",
    412     "         image window cut out.",
    413     "",
    414     "Atop     The result is the same shape as image image window,",
    415     "         with image obscuring image window where the image",
    416     "         shapes overlap.  Note this differs from over",
    417     "         because the portion of image outside image window's",
    418     "         shape does not appear in the result.",
    419     "",
    420     "Xor      The result is the image data from both image and",
    421     "         image window that is outside the overlap region.",
    422     "         The overlap region is blank.",
    423     "",
    424     "Plus     The result is just the sum of the image data.",
    425     "         Output values are cropped to QuantumRange (no overflow).",
    426     "",
    427     "Minus    The result of image - image window, with underflow",
    428     "         cropped to zero.",
    429     "",
    430     "Add      The result of image + image window, with overflow",
    431     "         wrapping around (mod 256).",
    432     "",
    433     "Subtract The result of image - image window, with underflow",
    434     "         wrapping around (mod 256).  The add and subtract",
    435     "         operators can be used to perform reversible",
    436     "         transformations.",
    437     "",
    438     "Difference",
    439     "         The result of abs(image - image window).  This",
    440     "         useful for comparing two very similar images.",
    441     "",
    442     "Multiply",
    443     "         The result of image * image window.  This",
    444     "         useful for the creation of drop-shadows.",
    445     "",
    446     "Bumpmap  The result of surface normals from image * image",
    447     "         window.",
    448     "",
    449     "Copy     The resulting image is image window replaced with",
    450     "         image.  Here the matte information is ignored.",
    451     "",
    452     "CopyRed  The red layer of the image window is replace with",
    453     "         the red layer of the image.  The other layers are",
    454     "         untouched.",
    455     "",
    456     "CopyGreen",
    457     "         The green layer of the image window is replace with",
    458     "         the green layer of the image.  The other layers are",
    459     "         untouched.",
    460     "",
    461     "CopyBlue The blue layer of the image window is replace with",
    462     "         the blue layer of the image.  The other layers are",
    463     "         untouched.",
    464     "",
    465     "CopyOpacity",
    466     "         The matte layer of the image window is replace with",
    467     "         the matte layer of the image.  The other layers are",
    468     "         untouched.",
    469     "",
    470     "The image compositor requires a matte, or alpha channel in",
    471     "the image for some operations.  This extra channel usually",
    472     "defines a mask which represents a sort of a cookie-cutter",
    473     "for the image.  This the case when matte is opaque (full",
    474     "coverage) for pixels inside the shape, zero outside, and",
    475     "between 0 and QuantumRange on the boundary.  If image does not",
    476     "have a matte channel, it is initialized with 0 for any pixel",
    477     "matching in color to pixel location (0,0), otherwise QuantumRange.",
    478     "",
    479     "If you choose Dissolve, the composite operator becomes Over.  The",
    480     "image matte channel percent transparency is initialized to factor.",
    481     "The image window is initialized to (100-factor). Where factor is the",
    482     "value you specify in the Dialog widget.",
    483     "",
    484     "Displace shifts the image pixels as defined by a displacement",
    485     "map.  With this option, image is used as a displacement map.",
    486     "Black, within the displacement map, is a maximum positive",
    487     "displacement.  White is a maximum negative displacement and",
    488     "middle gray is neutral.  The displacement is scaled to determine",
    489     "the pixel shift.  By default, the displacement applies in both the",
    490     "horizontal and vertical directions.  However, if you specify a mask,",
    491     "image is the horizontal X displacement and mask the vertical Y",
    492     "displacement.",
    493     "",
    494     "Note that matte information for image window is not retained",
    495     "for colormapped X server visuals (e.g. StaticColor,",
    496     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
    497     "behavior may require a TrueColor or DirectColor visual or a",
    498     "Standard Colormap.",
    499     "",
    500     "Choosing a composite operator is optional.  The default",
    501     "operator is replace.  However, you must choose a location to",
    502     "composite your image and press button 1.  Press and hold the",
    503     "button before releasing and an outline of the image will",
    504     "appear to help you identify your location.",
    505     "",
    506     "The actual colors of the composite image is saved.  However,",
    507     "the color that appears in image window may be different.",
    508     "For example, on a monochrome screen image window will appear",
    509     "black or white even though your composited image may have",
    510     "many colors.  If the image is saved to a file it is written",
    511     "with the correct colors.  To assure the correct colors are",
    512     "saved in the final image, any PseudoClass image is promoted",
    513     "to DirectClass (see miff(5)).  To force a PseudoClass image",
    514     "to remain PseudoClass, use -colors.",
    515     (char *) NULL,
    516   },
    517   *ImageCutHelp[] =
    518   {
    519     "In cut mode, the Command widget has these options:",
    520     "",
    521     "    Help",
    522     "    Dismiss",
    523     "",
    524     "To define a cut region, press button 1 and drag.  The",
    525     "cut region is defined by a highlighted rectangle that",
    526     "expands or contracts as it follows the pointer.  Once you",
    527     "are satisfied with the cut region, release the button.",
    528     "You are now in rectify mode.  In rectify mode, the Command",
    529     "widget has these options:",
    530     "",
    531     "    Cut",
    532     "    Help",
    533     "    Dismiss",
    534     "",
    535     "You can make adjustments by moving the pointer to one of the",
    536     "cut rectangle corners, pressing a button, and dragging.",
    537     "Finally, press Cut to commit your copy region.  To",
    538     "exit without cutting the image, press Dismiss.",
    539     (char *) NULL,
    540   },
    541   *ImageCopyHelp[] =
    542   {
    543     "In copy mode, the Command widget has these options:",
    544     "",
    545     "    Help",
    546     "    Dismiss",
    547     "",
    548     "To define a copy region, press button 1 and drag.  The",
    549     "copy region is defined by a highlighted rectangle that",
    550     "expands or contracts as it follows the pointer.  Once you",
    551     "are satisfied with the copy region, release the button.",
    552     "You are now in rectify mode.  In rectify mode, the Command",
    553     "widget has these options:",
    554     "",
    555     "    Copy",
    556     "    Help",
    557     "    Dismiss",
    558     "",
    559     "You can make adjustments by moving the pointer to one of the",
    560     "copy rectangle corners, pressing a button, and dragging.",
    561     "Finally, press Copy to commit your copy region.  To",
    562     "exit without copying the image, press Dismiss.",
    563     (char *) NULL,
    564   },
    565   *ImageCropHelp[] =
    566   {
    567     "In crop mode, the Command widget has these options:",
    568     "",
    569     "    Help",
    570     "    Dismiss",
    571     "",
    572     "To define a cropping region, press button 1 and drag.  The",
    573     "cropping region is defined by a highlighted rectangle that",
    574     "expands or contracts as it follows the pointer.  Once you",
    575     "are satisfied with the cropping region, release the button.",
    576     "You are now in rectify mode.  In rectify mode, the Command",
    577     "widget has these options:",
    578     "",
    579     "    Crop",
    580     "    Help",
    581     "    Dismiss",
    582     "",
    583     "You can make adjustments by moving the pointer to one of the",
    584     "cropping rectangle corners, pressing a button, and dragging.",
    585     "Finally, press Crop to commit your cropping region.  To",
    586     "exit without cropping the image, press Dismiss.",
    587     (char *) NULL,
    588   },
    589   *ImageDrawHelp[] =
    590   {
    591     "The cursor changes to a crosshair to indicate you are in",
    592     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
    593     "the Command widget has these options:",
    594     "",
    595     "    Element",
    596     "      point",
    597     "      line",
    598     "      rectangle",
    599     "      fill rectangle",
    600     "      circle",
    601     "      fill circle",
    602     "      ellipse",
    603     "      fill ellipse",
    604     "      polygon",
    605     "      fill polygon",
    606     "    Color",
    607     "      black",
    608     "      blue",
    609     "      cyan",
    610     "      green",
    611     "      gray",
    612     "      red",
    613     "      magenta",
    614     "      yellow",
    615     "      white",
    616     "      transparent",
    617     "      Browser...",
    618     "    Stipple",
    619     "      Brick",
    620     "      Diagonal",
    621     "      Scales",
    622     "      Vertical",
    623     "      Wavy",
    624     "      Translucent",
    625     "      Opaque",
    626     "      Open...",
    627     "    Width",
    628     "      1",
    629     "      2",
    630     "      4",
    631     "      8",
    632     "      16",
    633     "      Dialog...",
    634     "    Undo",
    635     "    Help",
    636     "    Dismiss",
    637     "",
    638     "Choose a drawing primitive from the Element sub-menu.",
    639     "",
    640     "Choose a color from the Color sub-menu.  Additional",
    641     "colors can be specified with the color browser.",
    642     "",
    643     "If you choose the color browser and press Grab, you can",
    644     "select the color by moving the pointer to the desired",
    645     "color on the screen and press any button.  The transparent",
    646     "color updates the image matte channel and is useful for",
    647     "image compositing.",
    648     "",
    649     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
    650     "Additional stipples can be specified with the file browser.",
    651     "Stipples obtained from the file browser must be on disk in the",
    652     "X11 bitmap format.",
    653     "",
    654     "Choose a width, if appropriate, from the Width sub-menu.  To",
    655     "choose a specific width select the Dialog widget.",
    656     "",
    657     "Choose a point in the Image window and press button 1 and",
    658     "hold.  Next, move the pointer to another location in the",
    659     "image.  As you move, a line connects the initial location and",
    660     "the pointer.  When you release the button, the image is",
    661     "updated with the primitive you just drew.  For polygons, the",
    662     "image is updated when you press and release the button without",
    663     "moving the pointer.",
    664     "",
    665     "To cancel image drawing, move the pointer back to the",
    666     "starting point of the line and release the button.",
    667     (char *) NULL,
    668   },
    669   *DisplayHelp[] =
    670   {
    671     "BUTTONS",
    672     "  The effects of each button press is described below.  Three",
    673     "  buttons are required.  If you have a two button mouse,",
    674     "  button 1 and 3 are returned.  Press ALT and button 3 to",
    675     "  simulate button 2.",
    676     "",
    677     "  1    Press this button to map or unmap the Command widget.",
    678     "",
    679     "  2    Press and drag to define a region of the image to",
    680     "       magnify.",
    681     "",
    682     "  3    Press and drag to choose from a select set of commands.",
    683     "       This button behaves differently if the image being",
    684     "       displayed is a visual image directory.  Here, choose a",
    685     "       particular tile of the directory and press this button and",
    686     "       drag to select a command from a pop-up menu.  Choose from",
    687     "       these menu items:",
    688     "",
    689     "           Open",
    690     "           Next",
    691     "           Former",
    692     "           Delete",
    693     "           Update",
    694     "",
    695     "       If you choose Open, the image represented by the tile is",
    696     "       displayed.  To return to the visual image directory, choose",
    697     "       Next from the Command widget.  Next and Former moves to the",
    698     "       next or former image respectively.  Choose Delete to delete",
    699     "       a particular image tile.  Finally, choose Update to",
    700     "       synchronize all the image tiles with their respective",
    701     "       images.",
    702     "",
    703     "COMMAND WIDGET",
    704     "  The Command widget lists a number of sub-menus and commands.",
    705     "  They are",
    706     "",
    707     "      File",
    708     "        Open...",
    709     "        Next",
    710     "        Former",
    711     "        Select...",
    712     "        Save...",
    713     "        Print...",
    714     "        Delete...",
    715     "        New...",
    716     "        Visual Directory...",
    717     "        Quit",
    718     "      Edit",
    719     "        Undo",
    720     "        Redo",
    721     "        Cut",
    722     "        Copy",
    723     "        Paste",
    724     "      View",
    725     "        Half Size",
    726     "        Original Size",
    727     "        Double Size",
    728     "        Resize...",
    729     "        Apply",
    730     "        Refresh",
    731     "        Restore",
    732     "      Transform",
    733     "        Crop",
    734     "        Chop",
    735     "        Flop",
    736     "        Flip",
    737     "        Rotate Right",
    738     "        Rotate Left",
    739     "        Rotate...",
    740     "        Shear...",
    741     "        Roll...",
    742     "        Trim Edges",
    743     "      Enhance",
    744     "        Brightness...",
    745     "        Saturation...",
    746     "        Hue...",
    747     "        Gamma...",
    748     "        Sharpen...",
    749     "        Dull",
    750     "        Contrast Stretch...",
    751     "        Sigmoidal Contrast...",
    752     "        Normalize",
    753     "        Equalize",
    754     "        Negate",
    755     "        Grayscale",
    756     "        Map...",
    757     "        Quantize...",
    758     "      Effects",
    759     "        Despeckle",
    760     "        Emboss",
    761     "        Reduce Noise",
    762     "        Add Noise",
    763     "        Sharpen...",
    764     "        Blur...",
    765     "        Threshold...",
    766     "        Edge Detect...",
    767     "        Spread...",
    768     "        Shade...",
    769     "        Painting...",
    770     "        Segment...",
    771     "      F/X",
    772     "        Solarize...",
    773     "        Sepia Tone...",
    774     "        Swirl...",
    775     "        Implode...",
    776     "        Vignette...",
    777     "        Wave...",
    778     "        Oil Painting...",
    779     "        Charcoal Drawing...",
    780     "      Image Edit",
    781     "        Annotate...",
    782     "        Draw...",
    783     "        Color...",
    784     "        Matte...",
    785     "        Composite...",
    786     "        Add Border...",
    787     "        Add Frame...",
    788     "        Comment...",
    789     "        Launch...",
    790     "        Region of Interest...",
    791     "      Miscellany",
    792     "        Image Info",
    793     "        Zoom Image",
    794     "        Show Preview...",
    795     "        Show Histogram",
    796     "        Show Matte",
    797     "        Background...",
    798     "        Slide Show",
    799     "        Preferences...",
    800     "      Help",
    801     "        Overview",
    802     "        Browse Documentation",
    803     "        About Display",
    804     "",
    805     "  Menu items with a indented triangle have a sub-menu.  They",
    806     "  are represented above as the indented items.  To access a",
    807     "  sub-menu item, move the pointer to the appropriate menu and",
    808     "  press a button and drag.  When you find the desired sub-menu",
    809     "  item, release the button and the command is executed.  Move",
    810     "  the pointer away from the sub-menu if you decide not to",
    811     "  execute a particular command.",
    812     "",
    813     "KEYBOARD ACCELERATORS",
    814     "  Accelerators are one or two key presses that effect a",
    815     "  particular command.  The keyboard accelerators that",
    816     "  display(1) understands is:",
    817     "",
    818     "  Ctl+O     Press to open an image from a file.",
    819     "",
    820     "  space     Press to display the next image.",
    821     "",
    822     "            If the image is a multi-paged document such as a Postscript",
    823     "            document, you can skip ahead several pages by preceding",
    824     "            this command with a number.  For example to display the",
    825     "            third page beyond the current page, press 3<space>.",
    826     "",
    827     "  backspace Press to display the former image.",
    828     "",
    829     "            If the image is a multi-paged document such as a Postscript",
    830     "            document, you can skip behind several pages by preceding",
    831     "            this command with a number.  For example to display the",
    832     "            third page preceding the current page, press 3<backspace>.",
    833     "",
    834     "  Ctl+S     Press to write the image to a file.",
    835     "",
    836     "  Ctl+P     Press to print the image to a Postscript printer.",
    837     "",
    838     "  Ctl+D     Press to delete an image file.",
    839     "",
    840     "  Ctl+N     Press to create a blank canvas.",
    841     "",
    842     "  Ctl+Q     Press to discard all images and exit program.",
    843     "",
    844     "  Ctl+Z     Press to undo last image transformation.",
    845     "",
    846     "  Ctl+R     Press to redo last image transformation.",
    847     "",
    848     "  Ctl+X     Press to cut a region of the image.",
    849     "",
    850     "  Ctl+C     Press to copy a region of the image.",
    851     "",
    852     "  Ctl+V     Press to paste a region to the image.",
    853     "",
    854     "  <         Press to half the image size.",
    855     "",
    856     "  -         Press to return to the original image size.",
    857     "",
    858     "  >         Press to double the image size.",
    859     "",
    860     "  %         Press to resize the image to a width and height you",
    861     "            specify.",
    862     "",
    863     "Cmd-A       Press to make any image transformations permanent."
    864     "",
    865     "            By default, any image size transformations are applied",
    866     "            to the original image to create the image displayed on",
    867     "            the X server.  However, the transformations are not",
    868     "            permanent (i.e. the original image does not change",
    869     "            size only the X image does).  For example, if you",
    870     "            press > the X image will appear to double in size,",
    871     "            but the original image will in fact remain the same size.",
    872     "            To force the original image to double in size, press >",
    873     "            followed by Cmd-A.",
    874     "",
    875     "  @         Press to refresh the image window.",
    876     "",
    877     "  C         Press to cut out a rectangular region of the image.",
    878     "",
    879     "  [         Press to chop the image.",
    880     "",
    881     "  H         Press to flop image in the horizontal direction.",
    882     "",
    883     "  V         Press to flip image in the vertical direction.",
    884     "",
    885     "  /         Press to rotate the image 90 degrees clockwise.",
    886     "",
    887     " \\         Press to rotate the image 90 degrees counter-clockwise.",
    888     "",
    889     "  *         Press to rotate the image the number of degrees you",
    890     "            specify.",
    891     "",
    892     "  S         Press to shear the image the number of degrees you",
    893     "            specify.",
    894     "",
    895     "  R         Press to roll the image.",
    896     "",
    897     "  T         Press to trim the image edges.",
    898     "",
    899     "  Shft-H    Press to vary the image hue.",
    900     "",
    901     "  Shft-S    Press to vary the color saturation.",
    902     "",
    903     "  Shft-L    Press to vary the color brightness.",
    904     "",
    905     "  Shft-G    Press to gamma correct the image.",
    906     "",
    907     "  Shft-C    Press to sharpen the image contrast.",
    908     "",
    909     "  Shft-Z    Press to dull the image contrast.",
    910     "",
    911     "  =         Press to perform histogram equalization on the image.",
    912     "",
    913     "  Shft-N    Press to perform histogram normalization on the image.",
    914     "",
    915     "  Shft-~    Press to negate the colors of the image.",
    916     "",
    917     "  .         Press to convert the image colors to gray.",
    918     "",
    919     "  Shft-#    Press to set the maximum number of unique colors in the",
    920     "            image.",
    921     "",
    922     "  F2        Press to reduce the speckles in an image.",
    923     "",
    924     "  F3        Press to eliminate peak noise from an image.",
    925     "",
    926     "  F4        Press to add noise to an image.",
    927     "",
    928     "  F5        Press to sharpen an image.",
    929     "",
    930     "  F6        Press to delete an image file.",
    931     "",
    932     "  F7        Press to threshold the image.",
    933     "",
    934     "  F8        Press to detect edges within an image.",
    935     "",
    936     "  F9        Press to emboss an image.",
    937     "",
    938     "  F10       Press to displace pixels by a random amount.",
    939     "",
    940     "  F11       Press to negate all pixels above the threshold level.",
    941     "",
    942     "  F12       Press to shade the image using a distant light source.",
    943     "",
    944     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
    945     "",
    946     "  F14       Press to segment the image by color.",
    947     "",
    948     "  Meta-S    Press to swirl image pixels about the center.",
    949     "",
    950     "  Meta-I    Press to implode image pixels about the center.",
    951     "",
    952     "  Meta-W    Press to alter an image along a sine wave.",
    953     "",
    954     "  Meta-P    Press to simulate an oil painting.",
    955     "",
    956     "  Meta-C    Press to simulate a charcoal drawing.",
    957     "",
    958     "  Alt-A     Press to annotate the image with text.",
    959     "",
    960     "  Alt-D     Press to draw on an image.",
    961     "",
    962     "  Alt-P     Press to edit an image pixel color.",
    963     "",
    964     "  Alt-M     Press to edit the image matte information.",
    965     "",
    966     "  Alt-V     Press to composite the image with another.",
    967     "",
    968     "  Alt-B     Press to add a border to the image.",
    969     "",
    970     "  Alt-F     Press to add an ornamental border to the image.",
    971     "",
    972     "  Alt-Shft-!",
    973     "            Press to add an image comment.",
    974     "",
    975     "  Ctl-A     Press to apply image processing techniques to a region",
    976     "            of interest.",
    977     "",
    978     "  Shft-?    Press to display information about the image.",
    979     "",
    980     "  Shft-+    Press to map the zoom image window.",
    981     "",
    982     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
    983     "",
    984     "  F1        Press to display helpful information about display(1).",
    985     "",
    986     "  Find      Press to browse documentation about ImageMagick.",
    987     "",
    988     "  1-9       Press to change the level of magnification.",
    989     "",
    990     "  Use the arrow keys to move the image one pixel up, down,",
    991     "  left, or right within the magnify window.  Be sure to first",
    992     "  map the magnify window by pressing button 2.",
    993     "",
    994     "  Press ALT and one of the arrow keys to trim off one pixel",
    995     "  from any side of the image.",
    996     (char *) NULL,
    997   },
    998   *ImageMatteEditHelp[] =
    999   {
   1000     "Matte information within an image is useful for some",
   1001     "operations such as image compositing (See IMAGE",
   1002     "COMPOSITING).  This extra channel usually defines a mask",
   1003     "which represents a sort of a cookie-cutter for the image.",
   1004     "This the case when matte is opaque (full coverage) for",
   1005     "pixels inside the shape, zero outside, and between 0 and",
   1006     "QuantumRange on the boundary.",
   1007     "",
   1008     "A small window appears showing the location of the cursor in",
   1009     "the image window. You are now in matte edit mode.  To exit",
   1010     "immediately, press Dismiss.  In matte edit mode, the Command",
   1011     "widget has these options:",
   1012     "",
   1013     "    Method",
   1014     "      point",
   1015     "      replace",
   1016     "      floodfill",
   1017     "      filltoborder",
   1018     "      reset",
   1019     "    Border Color",
   1020     "      black",
   1021     "      blue",
   1022     "      cyan",
   1023     "      green",
   1024     "      gray",
   1025     "      red",
   1026     "      magenta",
   1027     "      yellow",
   1028     "      white",
   1029     "      Browser...",
   1030     "    Fuzz",
   1031     "      0%",
   1032     "      2%",
   1033     "      5%",
   1034     "      10%",
   1035     "      15%",
   1036     "      Dialog...",
   1037     "    Matte",
   1038     "      Opaque",
   1039     "      Transparent",
   1040     "      Dialog...",
   1041     "    Undo",
   1042     "    Help",
   1043     "    Dismiss",
   1044     "",
   1045     "Choose a matte editing method from the Method sub-menu of",
   1046     "the Command widget.  The point method changes the matte value",
   1047     "of any pixel selected with the pointer until the button is",
   1048     "is released.  The replace method changes the matte value of",
   1049     "any pixel that matches the color of the pixel you select with",
   1050     "a button press.  Floodfill changes the matte value of any pixel",
   1051     "that matches the color of the pixel you select with a button",
   1052     "press and is a neighbor.  Whereas filltoborder changes the matte",
   1053     "value any neighbor pixel that is not the border color.  Finally",
   1054     "reset changes the entire image to the designated matte value.",
   1055     "",
   1056     "Choose Matte Value and pick Opaque or Transarent.  For other values",
   1057     "select the Dialog entry.  Here a dialog appears requesting a matte",
   1058     "value.  The value you select is assigned as the opacity value of the",
   1059     "selected pixel or pixels.",
   1060     "",
   1061     "Now, press any button to select a pixel within the image",
   1062     "window to change its matte value.",
   1063     "",
   1064     "If the Magnify widget is mapped, it can be helpful in positioning",
   1065     "your pointer within the image (refer to button 2).",
   1066     "",
   1067     "Matte information is only valid in a DirectClass image.",
   1068     "Therefore, any PseudoClass image is promoted to DirectClass",
   1069     "(see miff(5)).  Note that matte information for PseudoClass",
   1070     "is not retained for colormapped X server visuals (e.g.",
   1071     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
   1072     "immediately save your image to a file (refer to Write).",
   1073     "Correct matte editing behavior may require a TrueColor or",
   1074     "DirectColor visual or a Standard Colormap.",
   1075     (char *) NULL,
   1076   },
   1077   *ImagePanHelp[] =
   1078   {
   1079     "When an image exceeds the width or height of the X server",
   1080     "screen, display maps a small panning icon.  The rectangle",
   1081     "within the panning icon shows the area that is currently",
   1082     "displayed in the image window.  To pan about the image,",
   1083     "press any button and drag the pointer within the panning",
   1084     "icon.  The pan rectangle moves with the pointer and the",
   1085     "image window is updated to reflect the location of the",
   1086     "rectangle within the panning icon.  When you have selected",
   1087     "the area of the image you wish to view, release the button.",
   1088     "",
   1089     "Use the arrow keys to pan the image one pixel up, down,",
   1090     "left, or right within the image window.",
   1091     "",
   1092     "The panning icon is withdrawn if the image becomes smaller",
   1093     "than the dimensions of the X server screen.",
   1094     (char *) NULL,
   1095   },
   1096   *ImagePasteHelp[] =
   1097   {
   1098     "A small window appears showing the location of the cursor in",
   1099     "the image window. You are now in paste mode.  To exit",
   1100     "immediately, press Dismiss.  In paste mode, the Command",
   1101     "widget has these options:",
   1102     "",
   1103     "    Operators",
   1104     "      over",
   1105     "      in",
   1106     "      out",
   1107     "      atop",
   1108     "      xor",
   1109     "      plus",
   1110     "      minus",
   1111     "      add",
   1112     "      subtract",
   1113     "      difference",
   1114     "      replace",
   1115     "    Help",
   1116     "    Dismiss",
   1117     "",
   1118     "Choose a composite operation from the Operators sub-menu of",
   1119     "the Command widget.  How each operator behaves is described",
   1120     "below.  Image window is the image currently displayed on",
   1121     "your X server and image is the image obtained with the File",
   1122     "Browser widget.",
   1123     "",
   1124     "Over     The result is the union of the two image shapes,",
   1125     "         with image obscuring image window in the region of",
   1126     "         overlap.",
   1127     "",
   1128     "In       The result is simply image cut by the shape of",
   1129     "         image window.  None of the image data of image",
   1130     "         window is in the result.",
   1131     "",
   1132     "Out      The resulting image is image with the shape of",
   1133     "         image window cut out.",
   1134     "",
   1135     "Atop     The result is the same shape as image image window,",
   1136     "         with image obscuring image window where the image",
   1137     "         shapes overlap.  Note this differs from over",
   1138     "         because the portion of image outside image window's",
   1139     "         shape does not appear in the result.",
   1140     "",
   1141     "Xor      The result is the image data from both image and",
   1142     "         image window that is outside the overlap region.",
   1143     "         The overlap region is blank.",
   1144     "",
   1145     "Plus     The result is just the sum of the image data.",
   1146     "         Output values are cropped to QuantumRange (no overflow).",
   1147     "         This operation is independent of the matte",
   1148     "         channels.",
   1149     "",
   1150     "Minus    The result of image - image window, with underflow",
   1151     "         cropped to zero.",
   1152     "",
   1153     "Add      The result of image + image window, with overflow",
   1154     "         wrapping around (mod 256).",
   1155     "",
   1156     "Subtract The result of image - image window, with underflow",
   1157     "         wrapping around (mod 256).  The add and subtract",
   1158     "         operators can be used to perform reversible",
   1159     "         transformations.",
   1160     "",
   1161     "Difference",
   1162     "         The result of abs(image - image window).  This",
   1163     "         useful for comparing two very similar images.",
   1164     "",
   1165     "Copy     The resulting image is image window replaced with",
   1166     "         image.  Here the matte information is ignored.",
   1167     "",
   1168     "CopyRed  The red layer of the image window is replace with",
   1169     "         the red layer of the image.  The other layers are",
   1170     "         untouched.",
   1171     "",
   1172     "CopyGreen",
   1173     "         The green layer of the image window is replace with",
   1174     "         the green layer of the image.  The other layers are",
   1175     "         untouched.",
   1176     "",
   1177     "CopyBlue The blue layer of the image window is replace with",
   1178     "         the blue layer of the image.  The other layers are",
   1179     "         untouched.",
   1180     "",
   1181     "CopyOpacity",
   1182     "         The matte layer of the image window is replace with",
   1183     "         the matte layer of the image.  The other layers are",
   1184     "         untouched.",
   1185     "",
   1186     "The image compositor requires a matte, or alpha channel in",
   1187     "the image for some operations.  This extra channel usually",
   1188     "defines a mask which represents a sort of a cookie-cutter",
   1189     "for the image.  This the case when matte is opaque (full",
   1190     "coverage) for pixels inside the shape, zero outside, and",
   1191     "between 0 and QuantumRange on the boundary.  If image does not",
   1192     "have a matte channel, it is initialized with 0 for any pixel",
   1193     "matching in color to pixel location (0,0), otherwise QuantumRange.",
   1194     "",
   1195     "Note that matte information for image window is not retained",
   1196     "for colormapped X server visuals (e.g. StaticColor,",
   1197     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
   1198     "behavior may require a TrueColor or DirectColor visual or a",
   1199     "Standard Colormap.",
   1200     "",
   1201     "Choosing a composite operator is optional.  The default",
   1202     "operator is replace.  However, you must choose a location to",
   1203     "paste your image and press button 1.  Press and hold the",
   1204     "button before releasing and an outline of the image will",
   1205     "appear to help you identify your location.",
   1206     "",
   1207     "The actual colors of the pasted image is saved.  However,",
   1208     "the color that appears in image window may be different.",
   1209     "For example, on a monochrome screen image window will appear",
   1210     "black or white even though your pasted image may have",
   1211     "many colors.  If the image is saved to a file it is written",
   1212     "with the correct colors.  To assure the correct colors are",
   1213     "saved in the final image, any PseudoClass image is promoted",
   1214     "to DirectClass (see miff(5)).  To force a PseudoClass image",
   1215     "to remain PseudoClass, use -colors.",
   1216     (char *) NULL,
   1217   },
   1218   *ImageROIHelp[] =
   1219   {
   1220     "In region of interest mode, the Command widget has these",
   1221     "options:",
   1222     "",
   1223     "    Help",
   1224     "    Dismiss",
   1225     "",
   1226     "To define a region of interest, press button 1 and drag.",
   1227     "The region of interest is defined by a highlighted rectangle",
   1228     "that expands or contracts as it follows the pointer.  Once",
   1229     "you are satisfied with the region of interest, release the",
   1230     "button.  You are now in apply mode.  In apply mode the",
   1231     "Command widget has these options:",
   1232     "",
   1233     "      File",
   1234     "        Save...",
   1235     "        Print...",
   1236     "      Edit",
   1237     "        Undo",
   1238     "        Redo",
   1239     "      Transform",
   1240     "        Flop",
   1241     "        Flip",
   1242     "        Rotate Right",
   1243     "        Rotate Left",
   1244     "      Enhance",
   1245     "        Hue...",
   1246     "        Saturation...",
   1247     "        Brightness...",
   1248     "        Gamma...",
   1249     "        Spiff",
   1250     "        Dull",
   1251     "        Contrast Stretch",
   1252     "        Sigmoidal Contrast...",
   1253     "        Normalize",
   1254     "        Equalize",
   1255     "        Negate",
   1256     "        Grayscale",
   1257     "        Map...",
   1258     "        Quantize...",
   1259     "      Effects",
   1260     "        Despeckle",
   1261     "        Emboss",
   1262     "        Reduce Noise",
   1263     "        Sharpen...",
   1264     "        Blur...",
   1265     "        Threshold...",
   1266     "        Edge Detect...",
   1267     "        Spread...",
   1268     "        Shade...",
   1269     "        Raise...",
   1270     "        Segment...",
   1271     "      F/X",
   1272     "        Solarize...",
   1273     "        Sepia Tone...",
   1274     "        Swirl...",
   1275     "        Implode...",
   1276     "        Vignette...",
   1277     "        Wave...",
   1278     "        Oil Painting...",
   1279     "        Charcoal Drawing...",
   1280     "      Miscellany",
   1281     "        Image Info",
   1282     "        Zoom Image",
   1283     "        Show Preview...",
   1284     "        Show Histogram",
   1285     "        Show Matte",
   1286     "      Help",
   1287     "      Dismiss",
   1288     "",
   1289     "You can make adjustments to the region of interest by moving",
   1290     "the pointer to one of the rectangle corners, pressing a",
   1291     "button, and dragging.  Finally, choose an image processing",
   1292     "technique from the Command widget.  You can choose more than",
   1293     "one image processing technique to apply to an area.",
   1294     "Alternatively, you can move the region of interest before",
   1295     "applying another image processing technique.  To exit, press",
   1296     "Dismiss.",
   1297     (char *) NULL,
   1298   },
   1299   *ImageRotateHelp[] =
   1300   {
   1301     "In rotate mode, the Command widget has these options:",
   1302     "",
   1303     "    Pixel Color",
   1304     "      black",
   1305     "      blue",
   1306     "      cyan",
   1307     "      green",
   1308     "      gray",
   1309     "      red",
   1310     "      magenta",
   1311     "      yellow",
   1312     "      white",
   1313     "      Browser...",
   1314     "    Direction",
   1315     "      horizontal",
   1316     "      vertical",
   1317     "    Help",
   1318     "    Dismiss",
   1319     "",
   1320     "Choose a background color from the Pixel Color sub-menu.",
   1321     "Additional background colors can be specified with the color",
   1322     "browser.  You can change the menu colors by setting the X",
   1323     "resources pen1 through pen9.",
   1324     "",
   1325     "If you choose the color browser and press Grab, you can",
   1326     "select the background color by moving the pointer to the",
   1327     "desired color on the screen and press any button.",
   1328     "",
   1329     "Choose a point in the image window and press this button and",
   1330     "hold.  Next, move the pointer to another location in the",
   1331     "image.  As you move a line connects the initial location and",
   1332     "the pointer.  When you release the button, the degree of",
   1333     "image rotation is determined by the slope of the line you",
   1334     "just drew.  The slope is relative to the direction you",
   1335     "choose from the Direction sub-menu of the Command widget.",
   1336     "",
   1337     "To cancel the image rotation, move the pointer back to the",
   1338     "starting point of the line and release the button.",
   1339     (char *) NULL,
   1340   };
   1341 
   1342 /*
   1344   Enumeration declarations.
   1345 */
   1346 typedef enum
   1347 {
   1348   CopyMode,
   1349   CropMode,
   1350   CutMode
   1351 } ClipboardMode;
   1352 
   1353 typedef enum
   1354 {
   1355   OpenCommand,
   1356   NextCommand,
   1357   FormerCommand,
   1358   SelectCommand,
   1359   SaveCommand,
   1360   PrintCommand,
   1361   DeleteCommand,
   1362   NewCommand,
   1363   VisualDirectoryCommand,
   1364   QuitCommand,
   1365   UndoCommand,
   1366   RedoCommand,
   1367   CutCommand,
   1368   CopyCommand,
   1369   PasteCommand,
   1370   HalfSizeCommand,
   1371   OriginalSizeCommand,
   1372   DoubleSizeCommand,
   1373   ResizeCommand,
   1374   ApplyCommand,
   1375   RefreshCommand,
   1376   RestoreCommand,
   1377   CropCommand,
   1378   ChopCommand,
   1379   FlopCommand,
   1380   FlipCommand,
   1381   RotateRightCommand,
   1382   RotateLeftCommand,
   1383   RotateCommand,
   1384   ShearCommand,
   1385   RollCommand,
   1386   TrimCommand,
   1387   HueCommand,
   1388   SaturationCommand,
   1389   BrightnessCommand,
   1390   GammaCommand,
   1391   SpiffCommand,
   1392   DullCommand,
   1393   ContrastStretchCommand,
   1394   SigmoidalContrastCommand,
   1395   NormalizeCommand,
   1396   EqualizeCommand,
   1397   NegateCommand,
   1398   GrayscaleCommand,
   1399   MapCommand,
   1400   QuantizeCommand,
   1401   DespeckleCommand,
   1402   EmbossCommand,
   1403   ReduceNoiseCommand,
   1404   AddNoiseCommand,
   1405   SharpenCommand,
   1406   BlurCommand,
   1407   ThresholdCommand,
   1408   EdgeDetectCommand,
   1409   SpreadCommand,
   1410   ShadeCommand,
   1411   RaiseCommand,
   1412   SegmentCommand,
   1413   SolarizeCommand,
   1414   SepiaToneCommand,
   1415   SwirlCommand,
   1416   ImplodeCommand,
   1417   VignetteCommand,
   1418   WaveCommand,
   1419   OilPaintCommand,
   1420   CharcoalDrawCommand,
   1421   AnnotateCommand,
   1422   DrawCommand,
   1423   ColorCommand,
   1424   MatteCommand,
   1425   CompositeCommand,
   1426   AddBorderCommand,
   1427   AddFrameCommand,
   1428   CommentCommand,
   1429   LaunchCommand,
   1430   RegionofInterestCommand,
   1431   ROIHelpCommand,
   1432   ROIDismissCommand,
   1433   InfoCommand,
   1434   ZoomCommand,
   1435   ShowPreviewCommand,
   1436   ShowHistogramCommand,
   1437   ShowMatteCommand,
   1438   BackgroundCommand,
   1439   SlideShowCommand,
   1440   PreferencesCommand,
   1441   HelpCommand,
   1442   BrowseDocumentationCommand,
   1443   VersionCommand,
   1444   SaveToUndoBufferCommand,
   1445   FreeBuffersCommand,
   1446   NullCommand
   1447 } CommandType;
   1448 
   1449 typedef enum
   1450 {
   1451   AnnotateNameCommand,
   1452   AnnotateFontColorCommand,
   1453   AnnotateBackgroundColorCommand,
   1454   AnnotateRotateCommand,
   1455   AnnotateHelpCommand,
   1456   AnnotateDismissCommand,
   1457   TextHelpCommand,
   1458   TextApplyCommand,
   1459   ChopDirectionCommand,
   1460   ChopHelpCommand,
   1461   ChopDismissCommand,
   1462   HorizontalChopCommand,
   1463   VerticalChopCommand,
   1464   ColorEditMethodCommand,
   1465   ColorEditColorCommand,
   1466   ColorEditBorderCommand,
   1467   ColorEditFuzzCommand,
   1468   ColorEditUndoCommand,
   1469   ColorEditHelpCommand,
   1470   ColorEditDismissCommand,
   1471   CompositeOperatorsCommand,
   1472   CompositeDissolveCommand,
   1473   CompositeDisplaceCommand,
   1474   CompositeHelpCommand,
   1475   CompositeDismissCommand,
   1476   CropHelpCommand,
   1477   CropDismissCommand,
   1478   RectifyCopyCommand,
   1479   RectifyHelpCommand,
   1480   RectifyDismissCommand,
   1481   DrawElementCommand,
   1482   DrawColorCommand,
   1483   DrawStippleCommand,
   1484   DrawWidthCommand,
   1485   DrawUndoCommand,
   1486   DrawHelpCommand,
   1487   DrawDismissCommand,
   1488   MatteEditMethod,
   1489   MatteEditBorderCommand,
   1490   MatteEditFuzzCommand,
   1491   MatteEditValueCommand,
   1492   MatteEditUndoCommand,
   1493   MatteEditHelpCommand,
   1494   MatteEditDismissCommand,
   1495   PasteOperatorsCommand,
   1496   PasteHelpCommand,
   1497   PasteDismissCommand,
   1498   RotateColorCommand,
   1499   RotateDirectionCommand,
   1500   RotateCropCommand,
   1501   RotateSharpenCommand,
   1502   RotateHelpCommand,
   1503   RotateDismissCommand,
   1504   HorizontalRotateCommand,
   1505   VerticalRotateCommand,
   1506   TileLoadCommand,
   1507   TileNextCommand,
   1508   TileFormerCommand,
   1509   TileDeleteCommand,
   1510   TileUpdateCommand
   1511 } ModeType;
   1512 
   1513 /*
   1515   Stipples.
   1516 */
   1517 #define BricksWidth  20
   1518 #define BricksHeight  20
   1519 #define DiagonalWidth  16
   1520 #define DiagonalHeight  16
   1521 #define HighlightWidth  8
   1522 #define HighlightHeight  8
   1523 #define OpaqueWidth  8
   1524 #define OpaqueHeight  8
   1525 #define ScalesWidth  16
   1526 #define ScalesHeight  16
   1527 #define ShadowWidth  8
   1528 #define ShadowHeight  8
   1529 #define VerticalWidth  16
   1530 #define VerticalHeight  16
   1531 #define WavyWidth  16
   1532 #define WavyHeight  16
   1533 
   1534 /*
   1536   Constant declaration.
   1537 */
   1538 static const int
   1539   RoiDelta = 8;
   1540 
   1541 static const unsigned char
   1542   BricksBitmap[] =
   1543   {
   1544     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
   1545     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
   1546     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
   1547     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
   1548     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
   1549   },
   1550   DiagonalBitmap[] =
   1551   {
   1552     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
   1553     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
   1554     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
   1555   },
   1556   ScalesBitmap[] =
   1557   {
   1558     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
   1559     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
   1560     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
   1561   },
   1562   VerticalBitmap[] =
   1563   {
   1564     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
   1565     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
   1566     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
   1567   },
   1568   WavyBitmap[] =
   1569   {
   1570     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
   1571     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
   1572     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
   1573   };
   1574 
   1575 /*
   1577   Function prototypes.
   1578 */
   1579 static CommandType
   1580   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
   1581     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
   1582 
   1583 static Image
   1584   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
   1585     Image **,ExceptionInfo *),
   1586   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
   1587   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
   1588     ExceptionInfo *),
   1589   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
   1590     ExceptionInfo *);
   1591 
   1592 static MagickBooleanType
   1593   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
   1594     ExceptionInfo *),
   1595   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
   1596     ExceptionInfo *),
   1597   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
   1598     ExceptionInfo *),
   1599   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
   1600     ExceptionInfo *),
   1601   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
   1602     ExceptionInfo *),
   1603   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
   1604     ExceptionInfo *),
   1605   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
   1606   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
   1607     ExceptionInfo *),
   1608   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
   1609     ExceptionInfo *),
   1610   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
   1611   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
   1612   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
   1613     ExceptionInfo *),
   1614   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
   1615   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
   1616   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
   1617 
   1618 static void
   1619   XDrawPanRectangle(Display *,XWindows *),
   1620   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
   1621     ExceptionInfo *),
   1622   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
   1623   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
   1624   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
   1625   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
   1626     const KeySym,ExceptionInfo *),
   1627   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
   1628   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
   1629   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
   1630 
   1631 /*
   1633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1634 %                                                                             %
   1635 %                                                                             %
   1636 %                                                                             %
   1637 %   D i s p l a y I m a g e s                                                 %
   1638 %                                                                             %
   1639 %                                                                             %
   1640 %                                                                             %
   1641 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1642 %
   1643 %  DisplayImages() displays an image sequence to any X window screen.  It
   1644 %  returns a value other than 0 if successful.  Check the exception member
   1645 %  of image to determine the reason for any failure.
   1646 %
   1647 %  The format of the DisplayImages method is:
   1648 %
   1649 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
   1650 %        Image *images,ExceptionInfo *exception)
   1651 %
   1652 %  A description of each parameter follows:
   1653 %
   1654 %    o image_info: the image info.
   1655 %
   1656 %    o image: the image.
   1657 %
   1658 %    o exception: return any errors or warnings in this structure.
   1659 %
   1660 */
   1661 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
   1662   Image *images,ExceptionInfo *exception)
   1663 {
   1664   char
   1665     *argv[1];
   1666 
   1667   Display
   1668     *display;
   1669 
   1670   Image
   1671     *image;
   1672 
   1673   register ssize_t
   1674     i;
   1675 
   1676   size_t
   1677     state;
   1678 
   1679   XrmDatabase
   1680     resource_database;
   1681 
   1682   XResourceInfo
   1683     resource_info;
   1684 
   1685   assert(image_info != (const ImageInfo *) NULL);
   1686   assert(image_info->signature == MagickCoreSignature);
   1687   assert(images != (Image *) NULL);
   1688   assert(images->signature == MagickCoreSignature);
   1689   if (images->debug != MagickFalse )
   1690     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
   1691   display=XOpenDisplay(image_info->server_name);
   1692   if (display == (Display *) NULL)
   1693     {
   1694       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
   1695         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
   1696       return(MagickFalse);
   1697     }
   1698   if (exception->severity != UndefinedException)
   1699     CatchException(exception);
   1700   (void) XSetErrorHandler(XError);
   1701   resource_database=XGetResourceDatabase(display,GetClientName());
   1702   (void) memset(&resource_info,0,sizeof(resource_info));
   1703   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
   1704   if (image_info->page != (char *) NULL)
   1705     resource_info.image_geometry=AcquireString(image_info->page);
   1706   resource_info.immutable=MagickTrue;
   1707   argv[0]=AcquireString(GetClientName());
   1708   state=DefaultState;
   1709   for (i=0; (state & ExitState) == 0; i++)
   1710   {
   1711     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
   1712       break;
   1713     image=GetImageFromList(images,i % GetImageListLength(images));
   1714     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
   1715   }
   1716   (void) SetErrorHandler((ErrorHandler) NULL);
   1717   (void) SetWarningHandler((WarningHandler) NULL);
   1718   argv[0]=DestroyString(argv[0]);
   1719   (void) XCloseDisplay(display);
   1720   XDestroyResourceInfo(&resource_info);
   1721   if (exception->severity != UndefinedException)
   1722     return(MagickFalse);
   1723   return(MagickTrue);
   1724 }
   1725 
   1726 /*
   1728 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1729 %                                                                             %
   1730 %                                                                             %
   1731 %                                                                             %
   1732 %   R e m o t e D i s p l a y C o m m a n d                                   %
   1733 %                                                                             %
   1734 %                                                                             %
   1735 %                                                                             %
   1736 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1737 %
   1738 %  RemoteDisplayCommand() encourages a remote display program to display the
   1739 %  specified image filename.
   1740 %
   1741 %  The format of the RemoteDisplayCommand method is:
   1742 %
   1743 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
   1744 %        const char *window,const char *filename,ExceptionInfo *exception)
   1745 %
   1746 %  A description of each parameter follows:
   1747 %
   1748 %    o image_info: the image info.
   1749 %
   1750 %    o window: Specifies the name or id of an X window.
   1751 %
   1752 %    o filename: the name of the image filename to display.
   1753 %
   1754 %    o exception: return any errors or warnings in this structure.
   1755 %
   1756 */
   1757 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
   1758   const char *window,const char *filename,ExceptionInfo *exception)
   1759 {
   1760   Display
   1761     *display;
   1762 
   1763   MagickStatusType
   1764     status;
   1765 
   1766   assert(image_info != (const ImageInfo *) NULL);
   1767   assert(image_info->signature == MagickCoreSignature);
   1768   assert(filename != (char *) NULL);
   1769   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
   1770   display=XOpenDisplay(image_info->server_name);
   1771   if (display == (Display *) NULL)
   1772     {
   1773       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
   1774         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
   1775       return(MagickFalse);
   1776     }
   1777   (void) XSetErrorHandler(XError);
   1778   status=XRemoteCommand(display,window,filename);
   1779   (void) XCloseDisplay(display);
   1780   return(status != 0 ? MagickTrue : MagickFalse);
   1781 }
   1782 
   1783 /*
   1785 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1786 %                                                                             %
   1787 %                                                                             %
   1788 %                                                                             %
   1789 +   X A n n o t a t e E d i t I m a g e                                       %
   1790 %                                                                             %
   1791 %                                                                             %
   1792 %                                                                             %
   1793 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1794 %
   1795 %  XAnnotateEditImage() annotates the image with text.
   1796 %
   1797 %  The format of the XAnnotateEditImage method is:
   1798 %
   1799 %      MagickBooleanType XAnnotateEditImage(Display *display,
   1800 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
   1801 %        ExceptionInfo *exception)
   1802 %
   1803 %  A description of each parameter follows:
   1804 %
   1805 %    o display: Specifies a connection to an X server;  returned from
   1806 %      XOpenDisplay.
   1807 %
   1808 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   1809 %
   1810 %    o windows: Specifies a pointer to a XWindows structure.
   1811 %
   1812 %    o image: the image; returned from ReadImage.
   1813 %
   1814 */
   1815 
   1816 static MagickBooleanType XAnnotateEditImage(Display *display,
   1817   XResourceInfo *resource_info,XWindows *windows,Image *image,
   1818   ExceptionInfo *exception)
   1819 {
   1820   static const char
   1821     *AnnotateMenu[] =
   1822     {
   1823       "Font Name",
   1824       "Font Color",
   1825       "Box Color",
   1826       "Rotate Text",
   1827       "Help",
   1828       "Dismiss",
   1829       (char *) NULL
   1830     },
   1831     *TextMenu[] =
   1832     {
   1833       "Help",
   1834       "Apply",
   1835       (char *) NULL
   1836     };
   1837 
   1838   static const ModeType
   1839     AnnotateCommands[] =
   1840     {
   1841       AnnotateNameCommand,
   1842       AnnotateFontColorCommand,
   1843       AnnotateBackgroundColorCommand,
   1844       AnnotateRotateCommand,
   1845       AnnotateHelpCommand,
   1846       AnnotateDismissCommand
   1847     },
   1848     TextCommands[] =
   1849     {
   1850       TextHelpCommand,
   1851       TextApplyCommand
   1852     };
   1853 
   1854   static MagickBooleanType
   1855     transparent_box = MagickTrue,
   1856     transparent_pen = MagickFalse;
   1857 
   1858   static double
   1859     degrees = 0.0;
   1860 
   1861   static unsigned int
   1862     box_id = MaxNumberPens-2,
   1863     font_id = 0,
   1864     pen_id = 0;
   1865 
   1866   char
   1867     command[MagickPathExtent],
   1868     text[MagickPathExtent];
   1869 
   1870   const char
   1871     *ColorMenu[MaxNumberPens+1];
   1872 
   1873   Cursor
   1874     cursor;
   1875 
   1876   GC
   1877     annotate_context;
   1878 
   1879   int
   1880     id,
   1881     pen_number,
   1882     status,
   1883     x,
   1884     y;
   1885 
   1886   KeySym
   1887     key_symbol;
   1888 
   1889   register char
   1890     *p;
   1891 
   1892   register ssize_t
   1893     i;
   1894 
   1895   unsigned int
   1896     height,
   1897     width;
   1898 
   1899   size_t
   1900     state;
   1901 
   1902   XAnnotateInfo
   1903     *annotate_info,
   1904     *previous_info;
   1905 
   1906   XColor
   1907     color;
   1908 
   1909   XFontStruct
   1910     *font_info;
   1911 
   1912   XEvent
   1913     event,
   1914     text_event;
   1915 
   1916   /*
   1917     Map Command widget.
   1918   */
   1919   (void) CloneString(&windows->command.name,"Annotate");
   1920   windows->command.data=4;
   1921   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
   1922   (void) XMapRaised(display,windows->command.id);
   1923   XClientMessage(display,windows->image.id,windows->im_protocols,
   1924     windows->im_update_widget,CurrentTime);
   1925   /*
   1926     Track pointer until button 1 is pressed.
   1927   */
   1928   XQueryPosition(display,windows->image.id,&x,&y);
   1929   (void) XSelectInput(display,windows->image.id,
   1930     windows->image.attributes.event_mask | PointerMotionMask);
   1931   cursor=XCreateFontCursor(display,XC_left_side);
   1932   (void) XCheckDefineCursor(display,windows->image.id,cursor);
   1933   state=DefaultState;
   1934   do
   1935   {
   1936     if (windows->info.mapped != MagickFalse )
   1937       {
   1938         /*
   1939           Display pointer position.
   1940         */
   1941         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
   1942           x+windows->image.x,y+windows->image.y);
   1943         XInfoWidget(display,windows,text);
   1944       }
   1945     /*
   1946       Wait for next event.
   1947     */
   1948     XScreenEvent(display,windows,&event,exception);
   1949     if (event.xany.window == windows->command.id)
   1950       {
   1951         /*
   1952           Select a command from the Command widget.
   1953         */
   1954         id=XCommandWidget(display,windows,AnnotateMenu,&event);
   1955         (void) XCheckDefineCursor(display,windows->image.id,cursor);
   1956         if (id < 0)
   1957           continue;
   1958         switch (AnnotateCommands[id])
   1959         {
   1960           case AnnotateNameCommand:
   1961           {
   1962             const char
   1963               *FontMenu[MaxNumberFonts];
   1964 
   1965             int
   1966               font_number;
   1967 
   1968             /*
   1969               Initialize menu selections.
   1970             */
   1971             for (i=0; i < MaxNumberFonts; i++)
   1972               FontMenu[i]=resource_info->font_name[i];
   1973             FontMenu[MaxNumberFonts-2]="Browser...";
   1974             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
   1975             /*
   1976               Select a font name from the pop-up menu.
   1977             */
   1978             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
   1979               (const char **) FontMenu,command);
   1980             if (font_number < 0)
   1981               break;
   1982             if (font_number == (MaxNumberFonts-2))
   1983               {
   1984                 static char
   1985                   font_name[MagickPathExtent] = "fixed";
   1986 
   1987                 /*
   1988                   Select a font name from a browser.
   1989                 */
   1990                 resource_info->font_name[font_number]=font_name;
   1991                 XFontBrowserWidget(display,windows,"Select",font_name);
   1992                 if (*font_name == '\0')
   1993                   break;
   1994               }
   1995             /*
   1996               Initialize font info.
   1997             */
   1998             font_info=XLoadQueryFont(display,resource_info->font_name[
   1999               font_number]);
   2000             if (font_info == (XFontStruct *) NULL)
   2001               {
   2002                 XNoticeWidget(display,windows,"Unable to load font:",
   2003                   resource_info->font_name[font_number]);
   2004                 break;
   2005               }
   2006             font_id=(unsigned int) font_number;
   2007             (void) XFreeFont(display,font_info);
   2008             break;
   2009           }
   2010           case AnnotateFontColorCommand:
   2011           {
   2012             /*
   2013               Initialize menu selections.
   2014             */
   2015             for (i=0; i < (int) (MaxNumberPens-2); i++)
   2016               ColorMenu[i]=resource_info->pen_colors[i];
   2017             ColorMenu[MaxNumberPens-2]="transparent";
   2018             ColorMenu[MaxNumberPens-1]="Browser...";
   2019             ColorMenu[MaxNumberPens]=(const char *) NULL;
   2020             /*
   2021               Select a pen color from the pop-up menu.
   2022             */
   2023             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
   2024               (const char **) ColorMenu,command);
   2025             if (pen_number < 0)
   2026               break;
   2027             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
   2028               MagickFalse;
   2029             if (transparent_pen != MagickFalse )
   2030               break;
   2031             if (pen_number == (MaxNumberPens-1))
   2032               {
   2033                 static char
   2034                   color_name[MagickPathExtent] = "gray";
   2035 
   2036                 /*
   2037                   Select a pen color from a dialog.
   2038                 */
   2039                 resource_info->pen_colors[pen_number]=color_name;
   2040                 XColorBrowserWidget(display,windows,"Select",color_name);
   2041                 if (*color_name == '\0')
   2042                   break;
   2043               }
   2044             /*
   2045               Set pen color.
   2046             */
   2047             (void) XParseColor(display,windows->map_info->colormap,
   2048               resource_info->pen_colors[pen_number],&color);
   2049             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
   2050               (unsigned int) MaxColors,&color);
   2051             windows->pixel_info->pen_colors[pen_number]=color;
   2052             pen_id=(unsigned int) pen_number;
   2053             break;
   2054           }
   2055           case AnnotateBackgroundColorCommand:
   2056           {
   2057             /*
   2058               Initialize menu selections.
   2059             */
   2060             for (i=0; i < (int) (MaxNumberPens-2); i++)
   2061               ColorMenu[i]=resource_info->pen_colors[i];
   2062             ColorMenu[MaxNumberPens-2]="transparent";
   2063             ColorMenu[MaxNumberPens-1]="Browser...";
   2064             ColorMenu[MaxNumberPens]=(const char *) NULL;
   2065             /*
   2066               Select a pen color from the pop-up menu.
   2067             */
   2068             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
   2069               (const char **) ColorMenu,command);
   2070             if (pen_number < 0)
   2071               break;
   2072             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
   2073               MagickFalse;
   2074             if (transparent_box != MagickFalse )
   2075               break;
   2076             if (pen_number == (MaxNumberPens-1))
   2077               {
   2078                 static char
   2079                   color_name[MagickPathExtent] = "gray";
   2080 
   2081                 /*
   2082                   Select a pen color from a dialog.
   2083                 */
   2084                 resource_info->pen_colors[pen_number]=color_name;
   2085                 XColorBrowserWidget(display,windows,"Select",color_name);
   2086                 if (*color_name == '\0')
   2087                   break;
   2088               }
   2089             /*
   2090               Set pen color.
   2091             */
   2092             (void) XParseColor(display,windows->map_info->colormap,
   2093               resource_info->pen_colors[pen_number],&color);
   2094             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
   2095               (unsigned int) MaxColors,&color);
   2096             windows->pixel_info->pen_colors[pen_number]=color;
   2097             box_id=(unsigned int) pen_number;
   2098             break;
   2099           }
   2100           case AnnotateRotateCommand:
   2101           {
   2102             int
   2103               entry;
   2104 
   2105             static char
   2106               angle[MagickPathExtent] = "30.0";
   2107 
   2108             static const char
   2109               *RotateMenu[] =
   2110               {
   2111                 "-90",
   2112                 "-45",
   2113                 "-30",
   2114                 "0",
   2115                 "30",
   2116                 "45",
   2117                 "90",
   2118                 "180",
   2119                 "Dialog...",
   2120                 (char *) NULL,
   2121               };
   2122 
   2123             /*
   2124               Select a command from the pop-up menu.
   2125             */
   2126             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
   2127               command);
   2128             if (entry < 0)
   2129               break;
   2130             if (entry != 8)
   2131               {
   2132                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
   2133                 break;
   2134               }
   2135             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
   2136               angle);
   2137             if (*angle == '\0')
   2138               break;
   2139             degrees=StringToDouble(angle,(char **) NULL);
   2140             break;
   2141           }
   2142           case AnnotateHelpCommand:
   2143           {
   2144             XTextViewWidget(display,resource_info,windows,MagickFalse,
   2145               "Help Viewer - Image Annotation",ImageAnnotateHelp);
   2146             break;
   2147           }
   2148           case AnnotateDismissCommand:
   2149           {
   2150             /*
   2151               Prematurely exit.
   2152             */
   2153             state|=EscapeState;
   2154             state|=ExitState;
   2155             break;
   2156           }
   2157           default:
   2158             break;
   2159         }
   2160         continue;
   2161       }
   2162     switch (event.type)
   2163     {
   2164       case ButtonPress:
   2165       {
   2166         if (event.xbutton.button != Button1)
   2167           break;
   2168         if (event.xbutton.window != windows->image.id)
   2169           break;
   2170         /*
   2171           Change to text entering mode.
   2172         */
   2173         x=event.xbutton.x;
   2174         y=event.xbutton.y;
   2175         state|=ExitState;
   2176         break;
   2177       }
   2178       case ButtonRelease:
   2179         break;
   2180       case Expose:
   2181         break;
   2182       case KeyPress:
   2183       {
   2184         if (event.xkey.window != windows->image.id)
   2185           break;
   2186         /*
   2187           Respond to a user key press.
   2188         */
   2189         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   2190           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   2191         switch ((int) key_symbol)
   2192         {
   2193           case XK_Escape:
   2194           case XK_F20:
   2195           {
   2196             /*
   2197               Prematurely exit.
   2198             */
   2199             state|=EscapeState;
   2200             state|=ExitState;
   2201             break;
   2202           }
   2203           case XK_F1:
   2204           case XK_Help:
   2205           {
   2206             XTextViewWidget(display,resource_info,windows,MagickFalse,
   2207               "Help Viewer - Image Annotation",ImageAnnotateHelp);
   2208             break;
   2209           }
   2210           default:
   2211           {
   2212             (void) XBell(display,0);
   2213             break;
   2214           }
   2215         }
   2216         break;
   2217       }
   2218       case MotionNotify:
   2219       {
   2220         /*
   2221           Map and unmap Info widget as cursor crosses its boundaries.
   2222         */
   2223         x=event.xmotion.x;
   2224         y=event.xmotion.y;
   2225         if (windows->info.mapped != MagickFalse )
   2226           {
   2227             if ((x < (int) (windows->info.x+windows->info.width)) &&
   2228                 (y < (int) (windows->info.y+windows->info.height)))
   2229               (void) XWithdrawWindow(display,windows->info.id,
   2230                 windows->info.screen);
   2231           }
   2232         else
   2233           if ((x > (int) (windows->info.x+windows->info.width)) ||
   2234               (y > (int) (windows->info.y+windows->info.height)))
   2235             (void) XMapWindow(display,windows->info.id);
   2236         break;
   2237       }
   2238       default:
   2239         break;
   2240     }
   2241   } while ((state & ExitState) == 0);
   2242   (void) XSelectInput(display,windows->image.id,
   2243     windows->image.attributes.event_mask);
   2244   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   2245   if ((state & EscapeState) != 0)
   2246     return(MagickTrue);
   2247   /*
   2248     Set font info and check boundary conditions.
   2249   */
   2250   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
   2251   if (font_info == (XFontStruct *) NULL)
   2252     {
   2253       XNoticeWidget(display,windows,"Unable to load font:",
   2254         resource_info->font_name[font_id]);
   2255       font_info=windows->font_info;
   2256     }
   2257   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
   2258     x=(int) windows->image.width-font_info->max_bounds.width;
   2259   if (y < (int) (font_info->ascent+font_info->descent))
   2260     y=(int) font_info->ascent+font_info->descent;
   2261   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
   2262       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
   2263     return(MagickFalse);
   2264   /*
   2265     Initialize annotate structure.
   2266   */
   2267   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
   2268   if (annotate_info == (XAnnotateInfo *) NULL)
   2269     return(MagickFalse);
   2270   XGetAnnotateInfo(annotate_info);
   2271   annotate_info->x=x;
   2272   annotate_info->y=y;
   2273   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
   2274     annotate_info->stencil=OpaqueStencil;
   2275   else
   2276     if (transparent_box == MagickFalse)
   2277       annotate_info->stencil=BackgroundStencil;
   2278     else
   2279       annotate_info->stencil=ForegroundStencil;
   2280   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
   2281   annotate_info->degrees=degrees;
   2282   annotate_info->font_info=font_info;
   2283   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
   2284     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
   2285     sizeof(*annotate_info->text));
   2286   if (annotate_info->text == (char *) NULL)
   2287     return(MagickFalse);
   2288   /*
   2289     Create cursor and set graphic context.
   2290   */
   2291   cursor=XCreateFontCursor(display,XC_pencil);
   2292   (void) XCheckDefineCursor(display,windows->image.id,cursor);
   2293   annotate_context=windows->image.annotate_context;
   2294   (void) XSetFont(display,annotate_context,font_info->fid);
   2295   (void) XSetBackground(display,annotate_context,
   2296     windows->pixel_info->pen_colors[box_id].pixel);
   2297   (void) XSetForeground(display,annotate_context,
   2298     windows->pixel_info->pen_colors[pen_id].pixel);
   2299   /*
   2300     Begin annotating the image with text.
   2301   */
   2302   (void) CloneString(&windows->command.name,"Text");
   2303   windows->command.data=0;
   2304   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
   2305   state=DefaultState;
   2306   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
   2307   text_event.xexpose.width=(int) font_info->max_bounds.width;
   2308   text_event.xexpose.height=font_info->max_bounds.ascent+
   2309     font_info->max_bounds.descent;
   2310   p=annotate_info->text;
   2311   do
   2312   {
   2313     /*
   2314       Display text cursor.
   2315     */
   2316     *p='\0';
   2317     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
   2318     /*
   2319       Wait for next event.
   2320     */
   2321     XScreenEvent(display,windows,&event,exception);
   2322     if (event.xany.window == windows->command.id)
   2323       {
   2324         /*
   2325           Select a command from the Command widget.
   2326         */
   2327         (void) XSetBackground(display,annotate_context,
   2328           windows->pixel_info->background_color.pixel);
   2329         (void) XSetForeground(display,annotate_context,
   2330           windows->pixel_info->foreground_color.pixel);
   2331         id=XCommandWidget(display,windows,AnnotateMenu,&event);
   2332         (void) XSetBackground(display,annotate_context,
   2333           windows->pixel_info->pen_colors[box_id].pixel);
   2334         (void) XSetForeground(display,annotate_context,
   2335           windows->pixel_info->pen_colors[pen_id].pixel);
   2336         if (id < 0)
   2337           continue;
   2338         switch (TextCommands[id])
   2339         {
   2340           case TextHelpCommand:
   2341           {
   2342             XTextViewWidget(display,resource_info,windows,MagickFalse,
   2343               "Help Viewer - Image Annotation",ImageAnnotateHelp);
   2344             (void) XCheckDefineCursor(display,windows->image.id,cursor);
   2345             break;
   2346           }
   2347           case TextApplyCommand:
   2348           {
   2349             /*
   2350               Finished annotating.
   2351             */
   2352             annotate_info->width=(unsigned int) XTextWidth(font_info,
   2353               annotate_info->text,(int) strlen(annotate_info->text));
   2354             XRefreshWindow(display,&windows->image,&text_event);
   2355             state|=ExitState;
   2356             break;
   2357           }
   2358           default:
   2359             break;
   2360         }
   2361         continue;
   2362       }
   2363     /*
   2364       Erase text cursor.
   2365     */
   2366     text_event.xexpose.x=x;
   2367     text_event.xexpose.y=y-font_info->max_bounds.ascent;
   2368     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
   2369       (unsigned int) text_event.xexpose.width,(unsigned int)
   2370       text_event.xexpose.height,MagickFalse);
   2371     XRefreshWindow(display,&windows->image,&text_event);
   2372     switch (event.type)
   2373     {
   2374       case ButtonPress:
   2375       {
   2376         if (event.xbutton.window != windows->image.id)
   2377           break;
   2378         if (event.xbutton.button == Button2)
   2379           {
   2380             /*
   2381               Request primary selection.
   2382             */
   2383             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
   2384               windows->image.id,CurrentTime);
   2385             break;
   2386           }
   2387         break;
   2388       }
   2389       case Expose:
   2390       {
   2391         if (event.xexpose.count == 0)
   2392           {
   2393             XAnnotateInfo
   2394               *text_info;
   2395 
   2396             /*
   2397               Refresh Image window.
   2398             */
   2399             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
   2400             text_info=annotate_info;
   2401             while (text_info != (XAnnotateInfo *) NULL)
   2402             {
   2403               if (annotate_info->stencil == ForegroundStencil)
   2404                 (void) XDrawString(display,windows->image.id,annotate_context,
   2405                   text_info->x,text_info->y,text_info->text,
   2406                   (int) strlen(text_info->text));
   2407               else
   2408                 (void) XDrawImageString(display,windows->image.id,
   2409                   annotate_context,text_info->x,text_info->y,text_info->text,
   2410                   (int) strlen(text_info->text));
   2411               text_info=text_info->previous;
   2412             }
   2413             (void) XDrawString(display,windows->image.id,annotate_context,
   2414               x,y,"_",1);
   2415           }
   2416         break;
   2417       }
   2418       case KeyPress:
   2419       {
   2420         int
   2421           length;
   2422 
   2423         if (event.xkey.window != windows->image.id)
   2424           break;
   2425         /*
   2426           Respond to a user key press.
   2427         */
   2428         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
   2429           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   2430         *(command+length)='\0';
   2431         if (((event.xkey.state & ControlMask) != 0) ||
   2432             ((event.xkey.state & Mod1Mask) != 0))
   2433           state|=ModifierState;
   2434         if ((state & ModifierState) != 0)
   2435           switch ((int) key_symbol)
   2436           {
   2437             case XK_u:
   2438             case XK_U:
   2439             {
   2440               key_symbol=DeleteCommand;
   2441               break;
   2442             }
   2443             default:
   2444               break;
   2445           }
   2446         switch ((int) key_symbol)
   2447         {
   2448           case XK_BackSpace:
   2449           {
   2450             /*
   2451               Erase one character.
   2452             */
   2453             if (p == annotate_info->text)
   2454               {
   2455                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
   2456                   break;
   2457                 else
   2458                   {
   2459                     /*
   2460                       Go to end of the previous line of text.
   2461                     */
   2462                     annotate_info=annotate_info->previous;
   2463                     p=annotate_info->text;
   2464                     x=annotate_info->x+annotate_info->width;
   2465                     y=annotate_info->y;
   2466                     if (annotate_info->width != 0)
   2467                       p+=strlen(annotate_info->text);
   2468                     break;
   2469                   }
   2470               }
   2471             p--;
   2472             x-=XTextWidth(font_info,p,1);
   2473             text_event.xexpose.x=x;
   2474             text_event.xexpose.y=y-font_info->max_bounds.ascent;
   2475             XRefreshWindow(display,&windows->image,&text_event);
   2476             break;
   2477           }
   2478           case XK_bracketleft:
   2479           {
   2480             key_symbol=XK_Escape;
   2481             break;
   2482           }
   2483           case DeleteCommand:
   2484           {
   2485             /*
   2486               Erase the entire line of text.
   2487             */
   2488             while (p != annotate_info->text)
   2489             {
   2490               p--;
   2491               x-=XTextWidth(font_info,p,1);
   2492               text_event.xexpose.x=x;
   2493               XRefreshWindow(display,&windows->image,&text_event);
   2494             }
   2495             break;
   2496           }
   2497           case XK_Escape:
   2498           case XK_F20:
   2499           {
   2500             /*
   2501               Finished annotating.
   2502             */
   2503             annotate_info->width=(unsigned int) XTextWidth(font_info,
   2504               annotate_info->text,(int) strlen(annotate_info->text));
   2505             XRefreshWindow(display,&windows->image,&text_event);
   2506             state|=ExitState;
   2507             break;
   2508           }
   2509           default:
   2510           {
   2511             /*
   2512               Draw a single character on the Image window.
   2513             */
   2514             if ((state & ModifierState) != 0)
   2515               break;
   2516             if (*command == '\0')
   2517               break;
   2518             *p=(*command);
   2519             if (annotate_info->stencil == ForegroundStencil)
   2520               (void) XDrawString(display,windows->image.id,annotate_context,
   2521                 x,y,p,1);
   2522             else
   2523               (void) XDrawImageString(display,windows->image.id,
   2524                 annotate_context,x,y,p,1);
   2525             x+=XTextWidth(font_info,p,1);
   2526             p++;
   2527             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
   2528               break;
   2529           }
   2530           case XK_Return:
   2531           case XK_KP_Enter:
   2532           {
   2533             /*
   2534               Advance to the next line of text.
   2535             */
   2536             *p='\0';
   2537             annotate_info->width=(unsigned int) XTextWidth(font_info,
   2538               annotate_info->text,(int) strlen(annotate_info->text));
   2539             if (annotate_info->next != (XAnnotateInfo *) NULL)
   2540               {
   2541                 /*
   2542                   Line of text already exists.
   2543                 */
   2544                 annotate_info=annotate_info->next;
   2545                 x=annotate_info->x;
   2546                 y=annotate_info->y;
   2547                 p=annotate_info->text;
   2548                 break;
   2549               }
   2550             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
   2551               sizeof(*annotate_info->next));
   2552             if (annotate_info->next == (XAnnotateInfo *) NULL)
   2553               return(MagickFalse);
   2554             *annotate_info->next=(*annotate_info);
   2555             annotate_info->next->previous=annotate_info;
   2556             annotate_info=annotate_info->next;
   2557             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
   2558               windows->image.width/MagickMax((ssize_t)
   2559               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
   2560             if (annotate_info->text == (char *) NULL)
   2561               return(MagickFalse);
   2562             annotate_info->y+=annotate_info->height;
   2563             if (annotate_info->y > (int) windows->image.height)
   2564               annotate_info->y=(int) annotate_info->height;
   2565             annotate_info->next=(XAnnotateInfo *) NULL;
   2566             x=annotate_info->x;
   2567             y=annotate_info->y;
   2568             p=annotate_info->text;
   2569             break;
   2570           }
   2571         }
   2572         break;
   2573       }
   2574       case KeyRelease:
   2575       {
   2576         /*
   2577           Respond to a user key release.
   2578         */
   2579         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   2580           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   2581         state&=(~ModifierState);
   2582         break;
   2583       }
   2584       case SelectionNotify:
   2585       {
   2586         Atom
   2587           type;
   2588 
   2589         int
   2590           format;
   2591 
   2592         unsigned char
   2593           *data;
   2594 
   2595         unsigned long
   2596           after,
   2597           length;
   2598 
   2599         /*
   2600           Obtain response from primary selection.
   2601         */
   2602         if (event.xselection.property == (Atom) None)
   2603           break;
   2604         status=XGetWindowProperty(display,event.xselection.requestor,
   2605           event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
   2606           &type,&format,&length,&after,&data);
   2607         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
   2608             (length == 0))
   2609           break;
   2610         /*
   2611           Annotate Image window with primary selection.
   2612         */
   2613         for (i=0; i < (ssize_t) length; i++)
   2614         {
   2615           if ((char) data[i] != '\n')
   2616             {
   2617               /*
   2618                 Draw a single character on the Image window.
   2619               */
   2620               *p=(char) data[i];
   2621               (void) XDrawString(display,windows->image.id,annotate_context,
   2622                 x,y,p,1);
   2623               x+=XTextWidth(font_info,p,1);
   2624               p++;
   2625               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
   2626                 continue;
   2627             }
   2628           /*
   2629             Advance to the next line of text.
   2630           */
   2631           *p='\0';
   2632           annotate_info->width=(unsigned int) XTextWidth(font_info,
   2633             annotate_info->text,(int) strlen(annotate_info->text));
   2634           if (annotate_info->next != (XAnnotateInfo *) NULL)
   2635             {
   2636               /*
   2637                 Line of text already exists.
   2638               */
   2639               annotate_info=annotate_info->next;
   2640               x=annotate_info->x;
   2641               y=annotate_info->y;
   2642               p=annotate_info->text;
   2643               continue;
   2644             }
   2645           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
   2646             sizeof(*annotate_info->next));
   2647           if (annotate_info->next == (XAnnotateInfo *) NULL)
   2648             return(MagickFalse);
   2649           *annotate_info->next=(*annotate_info);
   2650           annotate_info->next->previous=annotate_info;
   2651           annotate_info=annotate_info->next;
   2652           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
   2653             windows->image.width/MagickMax((ssize_t)
   2654             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
   2655           if (annotate_info->text == (char *) NULL)
   2656             return(MagickFalse);
   2657           annotate_info->y+=annotate_info->height;
   2658           if (annotate_info->y > (int) windows->image.height)
   2659             annotate_info->y=(int) annotate_info->height;
   2660           annotate_info->next=(XAnnotateInfo *) NULL;
   2661           x=annotate_info->x;
   2662           y=annotate_info->y;
   2663           p=annotate_info->text;
   2664         }
   2665         (void) XFree((void *) data);
   2666         break;
   2667       }
   2668       default:
   2669         break;
   2670     }
   2671   } while ((state & ExitState) == 0);
   2672   (void) XFreeCursor(display,cursor);
   2673   /*
   2674     Annotation is relative to image configuration.
   2675   */
   2676   width=(unsigned int) image->columns;
   2677   height=(unsigned int) image->rows;
   2678   x=0;
   2679   y=0;
   2680   if (windows->image.crop_geometry != (char *) NULL)
   2681     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
   2682   /*
   2683     Initialize annotated image.
   2684   */
   2685   XSetCursorState(display,windows,MagickTrue);
   2686   XCheckRefreshWindows(display,windows);
   2687   while (annotate_info != (XAnnotateInfo *) NULL)
   2688   {
   2689     if (annotate_info->width == 0)
   2690       {
   2691         /*
   2692           No text on this line--  go to the next line of text.
   2693         */
   2694         previous_info=annotate_info->previous;
   2695         annotate_info->text=(char *)
   2696           RelinquishMagickMemory(annotate_info->text);
   2697         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
   2698         annotate_info=previous_info;
   2699         continue;
   2700       }
   2701     /*
   2702       Determine pixel index for box and pen color.
   2703     */
   2704     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
   2705     if (windows->pixel_info->colors != 0)
   2706       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
   2707         if (windows->pixel_info->pixels[i] ==
   2708             windows->pixel_info->pen_colors[box_id].pixel)
   2709           {
   2710             windows->pixel_info->box_index=(unsigned short) i;
   2711             break;
   2712           }
   2713     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
   2714     if (windows->pixel_info->colors != 0)
   2715       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
   2716         if (windows->pixel_info->pixels[i] ==
   2717             windows->pixel_info->pen_colors[pen_id].pixel)
   2718           {
   2719             windows->pixel_info->pen_index=(unsigned short) i;
   2720             break;
   2721           }
   2722     /*
   2723       Define the annotate geometry string.
   2724     */
   2725     annotate_info->x=(int)
   2726       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
   2727     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
   2728       windows->image.y)/windows->image.ximage->height;
   2729     (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
   2730       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
   2731       height*annotate_info->height/windows->image.ximage->height,
   2732       annotate_info->x+x,annotate_info->y+y);
   2733     /*
   2734       Annotate image with text.
   2735     */
   2736     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
   2737       exception);
   2738     if (status == 0)
   2739       return(MagickFalse);
   2740     /*
   2741       Free up memory.
   2742     */
   2743     previous_info=annotate_info->previous;
   2744     annotate_info->text=DestroyString(annotate_info->text);
   2745     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
   2746     annotate_info=previous_info;
   2747   }
   2748   (void) XSetForeground(display,annotate_context,
   2749     windows->pixel_info->foreground_color.pixel);
   2750   (void) XSetBackground(display,annotate_context,
   2751     windows->pixel_info->background_color.pixel);
   2752   (void) XSetFont(display,annotate_context,windows->font_info->fid);
   2753   XSetCursorState(display,windows,MagickFalse);
   2754   (void) XFreeFont(display,font_info);
   2755   /*
   2756     Update image configuration.
   2757   */
   2758   XConfigureImageColormap(display,resource_info,windows,image,exception);
   2759   (void) XConfigureImage(display,resource_info,windows,image,exception);
   2760   return(MagickTrue);
   2761 }
   2762 
   2763 /*
   2765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2766 %                                                                             %
   2767 %                                                                             %
   2768 %                                                                             %
   2769 +   X B a c k g r o u n d I m a g e                                           %
   2770 %                                                                             %
   2771 %                                                                             %
   2772 %                                                                             %
   2773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2774 %
   2775 %  XBackgroundImage() displays the image in the background of a window.
   2776 %
   2777 %  The format of the XBackgroundImage method is:
   2778 %
   2779 %      MagickBooleanType XBackgroundImage(Display *display,
   2780 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
   2781 %        ExceptionInfo *exception)
   2782 %
   2783 %  A description of each parameter follows:
   2784 %
   2785 %    o display: Specifies a connection to an X server; returned from
   2786 %      XOpenDisplay.
   2787 %
   2788 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   2789 %
   2790 %    o windows: Specifies a pointer to a XWindows structure.
   2791 %
   2792 %    o image: the image.
   2793 %
   2794 %    o exception: return any errors or warnings in this structure.
   2795 %
   2796 */
   2797 static MagickBooleanType XBackgroundImage(Display *display,
   2798   XResourceInfo *resource_info,XWindows *windows,Image **image,
   2799   ExceptionInfo *exception)
   2800 {
   2801 #define BackgroundImageTag  "Background/Image"
   2802 
   2803   int
   2804     status;
   2805 
   2806   static char
   2807     window_id[MagickPathExtent] = "root";
   2808 
   2809   XResourceInfo
   2810     background_resources;
   2811 
   2812   /*
   2813     Put image in background.
   2814   */
   2815   status=XDialogWidget(display,windows,"Background",
   2816     "Enter window id (id 0x00 selects window with pointer):",window_id);
   2817   if (*window_id == '\0')
   2818     return(MagickFalse);
   2819   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
   2820     exception);
   2821   XInfoWidget(display,windows,BackgroundImageTag);
   2822   XSetCursorState(display,windows,MagickTrue);
   2823   XCheckRefreshWindows(display,windows);
   2824   background_resources=(*resource_info);
   2825   background_resources.window_id=window_id;
   2826   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
   2827   status=XDisplayBackgroundImage(display,&background_resources,*image,
   2828     exception);
   2829   if (status != MagickFalse)
   2830     XClientMessage(display,windows->image.id,windows->im_protocols,
   2831       windows->im_retain_colors,CurrentTime);
   2832   XSetCursorState(display,windows,MagickFalse);
   2833   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
   2834     exception);
   2835   return(MagickTrue);
   2836 }
   2837 
   2838 /*
   2840 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2841 %                                                                             %
   2842 %                                                                             %
   2843 %                                                                             %
   2844 +   X C h o p I m a g e                                                       %
   2845 %                                                                             %
   2846 %                                                                             %
   2847 %                                                                             %
   2848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2849 %
   2850 %  XChopImage() chops the X image.
   2851 %
   2852 %  The format of the XChopImage method is:
   2853 %
   2854 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
   2855 %      XWindows *windows,Image **image,ExceptionInfo *exception)
   2856 %
   2857 %  A description of each parameter follows:
   2858 %
   2859 %    o display: Specifies a connection to an X server; returned from
   2860 %      XOpenDisplay.
   2861 %
   2862 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   2863 %
   2864 %    o windows: Specifies a pointer to a XWindows structure.
   2865 %
   2866 %    o image: the image.
   2867 %
   2868 %    o exception: return any errors or warnings in this structure.
   2869 %
   2870 */
   2871 static MagickBooleanType XChopImage(Display *display,
   2872   XResourceInfo *resource_info,XWindows *windows,Image **image,
   2873   ExceptionInfo *exception)
   2874 {
   2875   static const char
   2876     *ChopMenu[] =
   2877     {
   2878       "Direction",
   2879       "Help",
   2880       "Dismiss",
   2881       (char *) NULL
   2882     };
   2883 
   2884   static ModeType
   2885     direction = HorizontalChopCommand;
   2886 
   2887   static const ModeType
   2888     ChopCommands[] =
   2889     {
   2890       ChopDirectionCommand,
   2891       ChopHelpCommand,
   2892       ChopDismissCommand
   2893     },
   2894     DirectionCommands[] =
   2895     {
   2896       HorizontalChopCommand,
   2897       VerticalChopCommand
   2898     };
   2899 
   2900   char
   2901     text[MagickPathExtent];
   2902 
   2903   Image
   2904     *chop_image;
   2905 
   2906   int
   2907     id,
   2908     x,
   2909     y;
   2910 
   2911   double
   2912     scale_factor;
   2913 
   2914   RectangleInfo
   2915     chop_info;
   2916 
   2917   unsigned int
   2918     distance,
   2919     height,
   2920     width;
   2921 
   2922   size_t
   2923     state;
   2924 
   2925   XEvent
   2926     event;
   2927 
   2928   XSegment
   2929     segment_info;
   2930 
   2931   /*
   2932     Map Command widget.
   2933   */
   2934   (void) CloneString(&windows->command.name,"Chop");
   2935   windows->command.data=1;
   2936   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
   2937   (void) XMapRaised(display,windows->command.id);
   2938   XClientMessage(display,windows->image.id,windows->im_protocols,
   2939     windows->im_update_widget,CurrentTime);
   2940   /*
   2941     Track pointer until button 1 is pressed.
   2942   */
   2943   XQueryPosition(display,windows->image.id,&x,&y);
   2944   (void) XSelectInput(display,windows->image.id,
   2945     windows->image.attributes.event_mask | PointerMotionMask);
   2946   state=DefaultState;
   2947   (void) memset(&segment_info,0,sizeof(segment_info));
   2948   do
   2949   {
   2950     if (windows->info.mapped != MagickFalse )
   2951       {
   2952         /*
   2953           Display pointer position.
   2954         */
   2955         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
   2956           x+windows->image.x,y+windows->image.y);
   2957         XInfoWidget(display,windows,text);
   2958       }
   2959     /*
   2960       Wait for next event.
   2961     */
   2962     XScreenEvent(display,windows,&event,exception);
   2963     if (event.xany.window == windows->command.id)
   2964       {
   2965         /*
   2966           Select a command from the Command widget.
   2967         */
   2968         id=XCommandWidget(display,windows,ChopMenu,&event);
   2969         if (id < 0)
   2970           continue;
   2971         switch (ChopCommands[id])
   2972         {
   2973           case ChopDirectionCommand:
   2974           {
   2975             char
   2976               command[MagickPathExtent];
   2977 
   2978             static const char
   2979               *Directions[] =
   2980               {
   2981                 "horizontal",
   2982                 "vertical",
   2983                 (char *) NULL,
   2984               };
   2985 
   2986             /*
   2987               Select a command from the pop-up menu.
   2988             */
   2989             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
   2990             if (id >= 0)
   2991               direction=DirectionCommands[id];
   2992             break;
   2993           }
   2994           case ChopHelpCommand:
   2995           {
   2996             XTextViewWidget(display,resource_info,windows,MagickFalse,
   2997               "Help Viewer - Image Chop",ImageChopHelp);
   2998             break;
   2999           }
   3000           case ChopDismissCommand:
   3001           {
   3002             /*
   3003               Prematurely exit.
   3004             */
   3005             state|=EscapeState;
   3006             state|=ExitState;
   3007             break;
   3008           }
   3009           default:
   3010             break;
   3011         }
   3012         continue;
   3013       }
   3014     switch (event.type)
   3015     {
   3016       case ButtonPress:
   3017       {
   3018         if (event.xbutton.button != Button1)
   3019           break;
   3020         if (event.xbutton.window != windows->image.id)
   3021           break;
   3022         /*
   3023           User has committed to start point of chopping line.
   3024         */
   3025         segment_info.x1=(short int) event.xbutton.x;
   3026         segment_info.x2=(short int) event.xbutton.x;
   3027         segment_info.y1=(short int) event.xbutton.y;
   3028         segment_info.y2=(short int) event.xbutton.y;
   3029         state|=ExitState;
   3030         break;
   3031       }
   3032       case ButtonRelease:
   3033         break;
   3034       case Expose:
   3035         break;
   3036       case KeyPress:
   3037       {
   3038         char
   3039           command[MagickPathExtent];
   3040 
   3041         KeySym
   3042           key_symbol;
   3043 
   3044         if (event.xkey.window != windows->image.id)
   3045           break;
   3046         /*
   3047           Respond to a user key press.
   3048         */
   3049         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   3050           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   3051         switch ((int) key_symbol)
   3052         {
   3053           case XK_Escape:
   3054           case XK_F20:
   3055           {
   3056             /*
   3057               Prematurely exit.
   3058             */
   3059             state|=EscapeState;
   3060             state|=ExitState;
   3061             break;
   3062           }
   3063           case XK_F1:
   3064           case XK_Help:
   3065           {
   3066             (void) XSetFunction(display,windows->image.highlight_context,
   3067               GXcopy);
   3068             XTextViewWidget(display,resource_info,windows,MagickFalse,
   3069               "Help Viewer - Image Chop",ImageChopHelp);
   3070             (void) XSetFunction(display,windows->image.highlight_context,
   3071               GXinvert);
   3072             break;
   3073           }
   3074           default:
   3075           {
   3076             (void) XBell(display,0);
   3077             break;
   3078           }
   3079         }
   3080         break;
   3081       }
   3082       case MotionNotify:
   3083       {
   3084         /*
   3085           Map and unmap Info widget as text cursor crosses its boundaries.
   3086         */
   3087         x=event.xmotion.x;
   3088         y=event.xmotion.y;
   3089         if (windows->info.mapped != MagickFalse )
   3090           {
   3091             if ((x < (int) (windows->info.x+windows->info.width)) &&
   3092                 (y < (int) (windows->info.y+windows->info.height)))
   3093               (void) XWithdrawWindow(display,windows->info.id,
   3094                 windows->info.screen);
   3095           }
   3096         else
   3097           if ((x > (int) (windows->info.x+windows->info.width)) ||
   3098               (y > (int) (windows->info.y+windows->info.height)))
   3099             (void) XMapWindow(display,windows->info.id);
   3100       }
   3101     }
   3102   } while ((state & ExitState) == 0);
   3103   (void) XSelectInput(display,windows->image.id,
   3104     windows->image.attributes.event_mask);
   3105   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   3106   if ((state & EscapeState) != 0)
   3107     return(MagickTrue);
   3108   /*
   3109     Draw line as pointer moves until the mouse button is released.
   3110   */
   3111   chop_info.width=0;
   3112   chop_info.height=0;
   3113   chop_info.x=0;
   3114   chop_info.y=0;
   3115   distance=0;
   3116   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
   3117   state=DefaultState;
   3118   do
   3119   {
   3120     if (distance > 9)
   3121       {
   3122         /*
   3123           Display info and draw chopping line.
   3124         */
   3125         if (windows->info.mapped == MagickFalse)
   3126           (void) XMapWindow(display,windows->info.id);
   3127         (void) FormatLocaleString(text,MagickPathExtent,
   3128           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
   3129           chop_info.height,(double) chop_info.x,(double) chop_info.y);
   3130         XInfoWidget(display,windows,text);
   3131         XHighlightLine(display,windows->image.id,
   3132           windows->image.highlight_context,&segment_info);
   3133       }
   3134     else
   3135       if (windows->info.mapped != MagickFalse )
   3136         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   3137     /*
   3138       Wait for next event.
   3139     */
   3140     XScreenEvent(display,windows,&event,exception);
   3141     if (distance > 9)
   3142       XHighlightLine(display,windows->image.id,
   3143         windows->image.highlight_context,&segment_info);
   3144     switch (event.type)
   3145     {
   3146       case ButtonPress:
   3147       {
   3148         segment_info.x2=(short int) event.xmotion.x;
   3149         segment_info.y2=(short int) event.xmotion.y;
   3150         break;
   3151       }
   3152       case ButtonRelease:
   3153       {
   3154         /*
   3155           User has committed to chopping line.
   3156         */
   3157         segment_info.x2=(short int) event.xbutton.x;
   3158         segment_info.y2=(short int) event.xbutton.y;
   3159         state|=ExitState;
   3160         break;
   3161       }
   3162       case Expose:
   3163         break;
   3164       case MotionNotify:
   3165       {
   3166         segment_info.x2=(short int) event.xmotion.x;
   3167         segment_info.y2=(short int) event.xmotion.y;
   3168       }
   3169       default:
   3170         break;
   3171     }
   3172     /*
   3173       Check boundary conditions.
   3174     */
   3175     if (segment_info.x2 < 0)
   3176       segment_info.x2=0;
   3177     else
   3178       if (segment_info.x2 > windows->image.ximage->width)
   3179         segment_info.x2=windows->image.ximage->width;
   3180     if (segment_info.y2 < 0)
   3181       segment_info.y2=0;
   3182     else
   3183       if (segment_info.y2 > windows->image.ximage->height)
   3184         segment_info.y2=windows->image.ximage->height;
   3185     distance=(unsigned int)
   3186       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
   3187        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
   3188     /*
   3189       Compute chopping geometry.
   3190     */
   3191     if (direction == HorizontalChopCommand)
   3192       {
   3193         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
   3194         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
   3195         chop_info.height=0;
   3196         chop_info.y=0;
   3197         if (segment_info.x1 > (int) segment_info.x2)
   3198           {
   3199             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
   3200             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
   3201           }
   3202       }
   3203     else
   3204       {
   3205         chop_info.width=0;
   3206         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
   3207         chop_info.x=0;
   3208         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
   3209         if (segment_info.y1 > segment_info.y2)
   3210           {
   3211             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
   3212             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
   3213           }
   3214       }
   3215   } while ((state & ExitState) == 0);
   3216   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
   3217   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   3218   if (distance <= 9)
   3219     return(MagickTrue);
   3220   /*
   3221     Image chopping is relative to image configuration.
   3222   */
   3223   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
   3224     exception);
   3225   XSetCursorState(display,windows,MagickTrue);
   3226   XCheckRefreshWindows(display,windows);
   3227   windows->image.window_changes.width=windows->image.ximage->width-
   3228     (unsigned int) chop_info.width;
   3229   windows->image.window_changes.height=windows->image.ximage->height-
   3230     (unsigned int) chop_info.height;
   3231   width=(unsigned int) (*image)->columns;
   3232   height=(unsigned int) (*image)->rows;
   3233   x=0;
   3234   y=0;
   3235   if (windows->image.crop_geometry != (char *) NULL)
   3236     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
   3237   scale_factor=(double) width/windows->image.ximage->width;
   3238   chop_info.x+=x;
   3239   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
   3240   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
   3241   scale_factor=(double) height/windows->image.ximage->height;
   3242   chop_info.y+=y;
   3243   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
   3244   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
   3245   /*
   3246     Chop image.
   3247   */
   3248   chop_image=ChopImage(*image,&chop_info,exception);
   3249   XSetCursorState(display,windows,MagickFalse);
   3250   if (chop_image == (Image *) NULL)
   3251     return(MagickFalse);
   3252   *image=DestroyImage(*image);
   3253   *image=chop_image;
   3254   /*
   3255     Update image configuration.
   3256   */
   3257   XConfigureImageColormap(display,resource_info,windows,*image,exception);
   3258   (void) XConfigureImage(display,resource_info,windows,*image,exception);
   3259   return(MagickTrue);
   3260 }
   3261 
   3262 /*
   3264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3265 %                                                                             %
   3266 %                                                                             %
   3267 %                                                                             %
   3268 +   X C o l o r E d i t I m a g e                                             %
   3269 %                                                                             %
   3270 %                                                                             %
   3271 %                                                                             %
   3272 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3273 %
   3274 %  XColorEditImage() allows the user to interactively change the color of one
   3275 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
   3276 %
   3277 %  The format of the XColorEditImage method is:
   3278 %
   3279 %      MagickBooleanType XColorEditImage(Display *display,
   3280 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
   3281 %          ExceptionInfo *exception)
   3282 %
   3283 %  A description of each parameter follows:
   3284 %
   3285 %    o display: Specifies a connection to an X server;  returned from
   3286 %      XOpenDisplay.
   3287 %
   3288 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   3289 %
   3290 %    o windows: Specifies a pointer to a XWindows structure.
   3291 %
   3292 %    o image: the image; returned from ReadImage.
   3293 %
   3294 %    o exception: return any errors or warnings in this structure.
   3295 %
   3296 */
   3297 static MagickBooleanType XColorEditImage(Display *display,
   3298   XResourceInfo *resource_info,XWindows *windows,Image **image,
   3299   ExceptionInfo *exception)
   3300 {
   3301   static const char
   3302     *ColorEditMenu[] =
   3303     {
   3304       "Method",
   3305       "Pixel Color",
   3306       "Border Color",
   3307       "Fuzz",
   3308       "Undo",
   3309       "Help",
   3310       "Dismiss",
   3311       (char *) NULL
   3312     };
   3313 
   3314   static const ModeType
   3315     ColorEditCommands[] =
   3316     {
   3317       ColorEditMethodCommand,
   3318       ColorEditColorCommand,
   3319       ColorEditBorderCommand,
   3320       ColorEditFuzzCommand,
   3321       ColorEditUndoCommand,
   3322       ColorEditHelpCommand,
   3323       ColorEditDismissCommand
   3324     };
   3325 
   3326   static PaintMethod
   3327     method = PointMethod;
   3328 
   3329   static unsigned int
   3330     pen_id = 0;
   3331 
   3332   static XColor
   3333     border_color = { 0, 0, 0, 0, 0, 0 };
   3334 
   3335   char
   3336     command[MagickPathExtent],
   3337     text[MagickPathExtent];
   3338 
   3339   Cursor
   3340     cursor;
   3341 
   3342   int
   3343     entry,
   3344     id,
   3345     x,
   3346     x_offset,
   3347     y,
   3348     y_offset;
   3349 
   3350   register Quantum
   3351     *q;
   3352 
   3353   register ssize_t
   3354     i;
   3355 
   3356   unsigned int
   3357     height,
   3358     width;
   3359 
   3360   size_t
   3361     state;
   3362 
   3363   XColor
   3364     color;
   3365 
   3366   XEvent
   3367     event;
   3368 
   3369   /*
   3370     Map Command widget.
   3371   */
   3372   (void) CloneString(&windows->command.name,"Color Edit");
   3373   windows->command.data=4;
   3374   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
   3375   (void) XMapRaised(display,windows->command.id);
   3376   XClientMessage(display,windows->image.id,windows->im_protocols,
   3377     windows->im_update_widget,CurrentTime);
   3378   /*
   3379     Make cursor.
   3380   */
   3381   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
   3382     resource_info->background_color,resource_info->foreground_color);
   3383   (void) XCheckDefineCursor(display,windows->image.id,cursor);
   3384   /*
   3385     Track pointer until button 1 is pressed.
   3386   */
   3387   XQueryPosition(display,windows->image.id,&x,&y);
   3388   (void) XSelectInput(display,windows->image.id,
   3389     windows->image.attributes.event_mask | PointerMotionMask);
   3390   state=DefaultState;
   3391   do
   3392   {
   3393     if (windows->info.mapped != MagickFalse )
   3394       {
   3395         /*
   3396           Display pointer position.
   3397         */
   3398         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
   3399           x+windows->image.x,y+windows->image.y);
   3400         XInfoWidget(display,windows,text);
   3401       }
   3402     /*
   3403       Wait for next event.
   3404     */
   3405     XScreenEvent(display,windows,&event,exception);
   3406     if (event.xany.window == windows->command.id)
   3407       {
   3408         /*
   3409           Select a command from the Command widget.
   3410         */
   3411         id=XCommandWidget(display,windows,ColorEditMenu,&event);
   3412         if (id < 0)
   3413           {
   3414             (void) XCheckDefineCursor(display,windows->image.id,cursor);
   3415             continue;
   3416           }
   3417         switch (ColorEditCommands[id])
   3418         {
   3419           case ColorEditMethodCommand:
   3420           {
   3421             char
   3422               **methods;
   3423 
   3424             /*
   3425               Select a method from the pop-up menu.
   3426             */
   3427             methods=(char **) GetCommandOptions(MagickMethodOptions);
   3428             if (methods == (char **) NULL)
   3429               break;
   3430             entry=XMenuWidget(display,windows,ColorEditMenu[id],
   3431               (const char **) methods,command);
   3432             if (entry >= 0)
   3433               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
   3434                 MagickFalse,methods[entry]);
   3435             methods=DestroyStringList(methods);
   3436             break;
   3437           }
   3438           case ColorEditColorCommand:
   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 pen color.
   3475             */
   3476             (void) XParseColor(display,windows->map_info->colormap,
   3477               resource_info->pen_colors[pen_number],&color);
   3478             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
   3479               (unsigned int) MaxColors,&color);
   3480             windows->pixel_info->pen_colors[pen_number]=color;
   3481             pen_id=(unsigned int) pen_number;
   3482             break;
   3483           }
   3484           case ColorEditBorderCommand:
   3485           {
   3486             const char
   3487               *ColorMenu[MaxNumberPens];
   3488 
   3489             int
   3490               pen_number;
   3491 
   3492             /*
   3493               Initialize menu selections.
   3494             */
   3495             for (i=0; i < (int) (MaxNumberPens-2); i++)
   3496               ColorMenu[i]=resource_info->pen_colors[i];
   3497             ColorMenu[MaxNumberPens-2]="Browser...";
   3498             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
   3499             /*
   3500               Select a pen color from the pop-up menu.
   3501             */
   3502             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
   3503               (const char **) ColorMenu,command);
   3504             if (pen_number < 0)
   3505               break;
   3506             if (pen_number == (MaxNumberPens-2))
   3507               {
   3508                 static char
   3509                   color_name[MagickPathExtent] = "gray";
   3510 
   3511                 /*
   3512                   Select a pen color from a dialog.
   3513                 */
   3514                 resource_info->pen_colors[pen_number]=color_name;
   3515                 XColorBrowserWidget(display,windows,"Select",color_name);
   3516                 if (*color_name == '\0')
   3517                   break;
   3518               }
   3519             /*
   3520               Set border color.
   3521             */
   3522             (void) XParseColor(display,windows->map_info->colormap,
   3523               resource_info->pen_colors[pen_number],&border_color);
   3524             break;
   3525           }
   3526           case ColorEditFuzzCommand:
   3527           {
   3528             static char
   3529               fuzz[MagickPathExtent];
   3530 
   3531             static const char
   3532               *FuzzMenu[] =
   3533               {
   3534                 "0%",
   3535                 "2%",
   3536                 "5%",
   3537                 "10%",
   3538                 "15%",
   3539                 "Dialog...",
   3540                 (char *) NULL,
   3541               };
   3542 
   3543             /*
   3544               Select a command from the pop-up menu.
   3545             */
   3546             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
   3547               command);
   3548             if (entry < 0)
   3549               break;
   3550             if (entry != 5)
   3551               {
   3552                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
   3553                   QuantumRange+1.0);
   3554                 break;
   3555               }
   3556             (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
   3557             (void) XDialogWidget(display,windows,"Ok",
   3558               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
   3559             if (*fuzz == '\0')
   3560               break;
   3561             (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
   3562             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
   3563               1.0);
   3564             break;
   3565           }
   3566           case ColorEditUndoCommand:
   3567           {
   3568             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
   3569               image,exception);
   3570             break;
   3571           }
   3572           case ColorEditHelpCommand:
   3573           default:
   3574           {
   3575             XTextViewWidget(display,resource_info,windows,MagickFalse,
   3576               "Help Viewer - Image Annotation",ImageColorEditHelp);
   3577             break;
   3578           }
   3579           case ColorEditDismissCommand:
   3580           {
   3581             /*
   3582               Prematurely exit.
   3583             */
   3584             state|=EscapeState;
   3585             state|=ExitState;
   3586             break;
   3587           }
   3588         }
   3589         (void) XCheckDefineCursor(display,windows->image.id,cursor);
   3590         continue;
   3591       }
   3592     switch (event.type)
   3593     {
   3594       case ButtonPress:
   3595       {
   3596         if (event.xbutton.button != Button1)
   3597           break;
   3598         if ((event.xbutton.window != windows->image.id) &&
   3599             (event.xbutton.window != windows->magnify.id))
   3600           break;
   3601         /*
   3602           exit loop.
   3603         */
   3604         x=event.xbutton.x;
   3605         y=event.xbutton.y;
   3606         (void) XMagickCommand(display,resource_info,windows,
   3607           SaveToUndoBufferCommand,image,exception);
   3608         state|=UpdateConfigurationState;
   3609         break;
   3610       }
   3611       case ButtonRelease:
   3612       {
   3613         if (event.xbutton.button != Button1)
   3614           break;
   3615         if ((event.xbutton.window != windows->image.id) &&
   3616             (event.xbutton.window != windows->magnify.id))
   3617           break;
   3618         /*
   3619           Update colormap information.
   3620         */
   3621         x=event.xbutton.x;
   3622         y=event.xbutton.y;
   3623         XConfigureImageColormap(display,resource_info,windows,*image,exception);
   3624         (void) XConfigureImage(display,resource_info,windows,*image,exception);
   3625         XInfoWidget(display,windows,text);
   3626         (void) XCheckDefineCursor(display,windows->image.id,cursor);
   3627         state&=(~UpdateConfigurationState);
   3628         break;
   3629       }
   3630       case Expose:
   3631         break;
   3632       case KeyPress:
   3633       {
   3634         KeySym
   3635           key_symbol;
   3636 
   3637         if (event.xkey.window == windows->magnify.id)
   3638           {
   3639             Window
   3640               window;
   3641 
   3642             window=windows->magnify.id;
   3643             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
   3644           }
   3645         if (event.xkey.window != windows->image.id)
   3646           break;
   3647         /*
   3648           Respond to a user key press.
   3649         */
   3650         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   3651           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   3652         switch ((int) key_symbol)
   3653         {
   3654           case XK_Escape:
   3655           case XK_F20:
   3656           {
   3657             /*
   3658               Prematurely exit.
   3659             */
   3660             state|=ExitState;
   3661             break;
   3662           }
   3663           case XK_F1:
   3664           case XK_Help:
   3665           {
   3666             XTextViewWidget(display,resource_info,windows,MagickFalse,
   3667               "Help Viewer - Image Annotation",ImageColorEditHelp);
   3668             break;
   3669           }
   3670           default:
   3671           {
   3672             (void) XBell(display,0);
   3673             break;
   3674           }
   3675         }
   3676         break;
   3677       }
   3678       case MotionNotify:
   3679       {
   3680         /*
   3681           Map and unmap Info widget as cursor crosses its boundaries.
   3682         */
   3683         x=event.xmotion.x;
   3684         y=event.xmotion.y;
   3685         if (windows->info.mapped != MagickFalse )
   3686           {
   3687             if ((x < (int) (windows->info.x+windows->info.width)) &&
   3688                 (y < (int) (windows->info.y+windows->info.height)))
   3689               (void) XWithdrawWindow(display,windows->info.id,
   3690                 windows->info.screen);
   3691           }
   3692         else
   3693           if ((x > (int) (windows->info.x+windows->info.width)) ||
   3694               (y > (int) (windows->info.y+windows->info.height)))
   3695             (void) XMapWindow(display,windows->info.id);
   3696         break;
   3697       }
   3698       default:
   3699         break;
   3700     }
   3701     if (event.xany.window == windows->magnify.id)
   3702       {
   3703         x=windows->magnify.x-windows->image.x;
   3704         y=windows->magnify.y-windows->image.y;
   3705       }
   3706     x_offset=x;
   3707     y_offset=y;
   3708     if ((state & UpdateConfigurationState) != 0)
   3709       {
   3710         CacheView
   3711           *image_view;
   3712 
   3713         int
   3714           x,
   3715           y;
   3716 
   3717         /*
   3718           Pixel edit is relative to image configuration.
   3719         */
   3720         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
   3721           MagickTrue);
   3722         color=windows->pixel_info->pen_colors[pen_id];
   3723         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
   3724         width=(unsigned int) (*image)->columns;
   3725         height=(unsigned int) (*image)->rows;
   3726         x=0;
   3727         y=0;
   3728         if (windows->image.crop_geometry != (char *) NULL)
   3729           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
   3730             &width,&height);
   3731         x_offset=(int)
   3732           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
   3733         y_offset=(int)
   3734           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
   3735         if ((x_offset < 0) || (y_offset < 0))
   3736           continue;
   3737         if ((x_offset >= (int) (*image)->columns) ||
   3738             (y_offset >= (int) (*image)->rows))
   3739           continue;
   3740         image_view=AcquireAuthenticCacheView(*image,exception);
   3741         switch (method)
   3742         {
   3743           case PointMethod:
   3744           default:
   3745           {
   3746             /*
   3747               Update color information using point algorithm.
   3748             */
   3749             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
   3750               return(MagickFalse);
   3751             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
   3752               (ssize_t) y_offset,1,1,exception);
   3753             if (q == (Quantum *) NULL)
   3754               break;
   3755             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
   3756             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
   3757             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
   3758             (void) SyncCacheViewAuthenticPixels(image_view,exception);
   3759             break;
   3760           }
   3761           case ReplaceMethod:
   3762           {
   3763             PixelInfo
   3764               pixel,
   3765               target;
   3766 
   3767             /*
   3768               Update color information using replace algorithm.
   3769             */
   3770             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
   3771               x_offset,(ssize_t) y_offset,&target,exception);
   3772             if ((*image)->storage_class == DirectClass)
   3773               {
   3774                 for (y=0; y < (int) (*image)->rows; y++)
   3775                 {
   3776                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
   3777                     (*image)->columns,1,exception);
   3778                   if (q == (Quantum *) NULL)
   3779                     break;
   3780                   for (x=0; x < (int) (*image)->columns; x++)
   3781                   {
   3782                     GetPixelInfoPixel(*image,q,&pixel);
   3783                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
   3784                       {
   3785                         SetPixelRed(*image,ScaleShortToQuantum(
   3786                           color.red),q);
   3787                         SetPixelGreen(*image,ScaleShortToQuantum(
   3788                           color.green),q);
   3789                         SetPixelBlue(*image,ScaleShortToQuantum(
   3790                           color.blue),q);
   3791                       }
   3792                     q+=GetPixelChannels(*image);
   3793                   }
   3794                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   3795                     break;
   3796                 }
   3797               }
   3798             else
   3799               {
   3800                 for (i=0; i < (ssize_t) (*image)->colors; i++)
   3801                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
   3802                     {
   3803                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
   3804                         color.red);
   3805                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
   3806                         color.green);
   3807                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
   3808                         color.blue);
   3809                     }
   3810                 (void) SyncImage(*image,exception);
   3811               }
   3812             break;
   3813           }
   3814           case FloodfillMethod:
   3815           case FillToBorderMethod:
   3816           {
   3817             DrawInfo
   3818               *draw_info;
   3819 
   3820             PixelInfo
   3821               target;
   3822 
   3823             /*
   3824               Update color information using floodfill algorithm.
   3825             */
   3826             (void) GetOneVirtualPixelInfo(*image,
   3827               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
   3828               y_offset,&target,exception);
   3829             if (method == FillToBorderMethod)
   3830               {
   3831                 target.red=(double)
   3832                   ScaleShortToQuantum(border_color.red);
   3833                 target.green=(double)
   3834                   ScaleShortToQuantum(border_color.green);
   3835                 target.blue=(double)
   3836                   ScaleShortToQuantum(border_color.blue);
   3837               }
   3838             draw_info=CloneDrawInfo(resource_info->image_info,
   3839               (DrawInfo *) NULL);
   3840             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
   3841               AllCompliance,&draw_info->fill,exception);
   3842             (void) FloodfillPaintImage(*image,draw_info,&target,
   3843               (ssize_t)x_offset,(ssize_t)y_offset,
   3844               method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
   3845             draw_info=DestroyDrawInfo(draw_info);
   3846             break;
   3847           }
   3848           case ResetMethod:
   3849           {
   3850             /*
   3851               Update color information using reset algorithm.
   3852             */
   3853             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
   3854               return(MagickFalse);
   3855             for (y=0; y < (int) (*image)->rows; y++)
   3856             {
   3857               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
   3858                 (*image)->columns,1,exception);
   3859               if (q == (Quantum *) NULL)
   3860                 break;
   3861               for (x=0; x < (int) (*image)->columns; x++)
   3862               {
   3863                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
   3864                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
   3865                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
   3866                 q+=GetPixelChannels(*image);
   3867               }
   3868               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   3869                 break;
   3870             }
   3871             break;
   3872           }
   3873         }
   3874         image_view=DestroyCacheView(image_view);
   3875         state&=(~UpdateConfigurationState);
   3876       }
   3877   } while ((state & ExitState) == 0);
   3878   (void) XSelectInput(display,windows->image.id,
   3879     windows->image.attributes.event_mask);
   3880   XSetCursorState(display,windows,MagickFalse);
   3881   (void) XFreeCursor(display,cursor);
   3882   return(MagickTrue);
   3883 }
   3884 
   3885 /*
   3887 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3888 %                                                                             %
   3889 %                                                                             %
   3890 %                                                                             %
   3891 +   X C o m p o s i t e I m a g e                                             %
   3892 %                                                                             %
   3893 %                                                                             %
   3894 %                                                                             %
   3895 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3896 %
   3897 %  XCompositeImage() requests an image name from the user, reads the image and
   3898 %  composites it with the X window image at a location the user chooses with
   3899 %  the pointer.
   3900 %
   3901 %  The format of the XCompositeImage method is:
   3902 %
   3903 %      MagickBooleanType XCompositeImage(Display *display,
   3904 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
   3905 %        ExceptionInfo *exception)
   3906 %
   3907 %  A description of each parameter follows:
   3908 %
   3909 %    o display: Specifies a connection to an X server;  returned from
   3910 %      XOpenDisplay.
   3911 %
   3912 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   3913 %
   3914 %    o windows: Specifies a pointer to a XWindows structure.
   3915 %
   3916 %    o image: the image; returned from ReadImage.
   3917 %
   3918 %    o exception: return any errors or warnings in this structure.
   3919 %
   3920 */
   3921 static MagickBooleanType XCompositeImage(Display *display,
   3922   XResourceInfo *resource_info,XWindows *windows,Image *image,
   3923   ExceptionInfo *exception)
   3924 {
   3925   static char
   3926     displacement_geometry[MagickPathExtent] = "30x30",
   3927     filename[MagickPathExtent] = "\0";
   3928 
   3929   static const char
   3930     *CompositeMenu[] =
   3931     {
   3932       "Operators",
   3933       "Dissolve",
   3934       "Displace",
   3935       "Help",
   3936       "Dismiss",
   3937       (char *) NULL
   3938     };
   3939 
   3940   static CompositeOperator
   3941     compose = CopyCompositeOp;
   3942 
   3943   static const ModeType
   3944     CompositeCommands[] =
   3945     {
   3946       CompositeOperatorsCommand,
   3947       CompositeDissolveCommand,
   3948       CompositeDisplaceCommand,
   3949       CompositeHelpCommand,
   3950       CompositeDismissCommand
   3951     };
   3952 
   3953   char
   3954     text[MagickPathExtent];
   3955 
   3956   Cursor
   3957     cursor;
   3958 
   3959   Image
   3960     *composite_image;
   3961 
   3962   int
   3963     entry,
   3964     id,
   3965     x,
   3966     y;
   3967 
   3968   double
   3969     blend,
   3970     scale_factor;
   3971 
   3972   RectangleInfo
   3973     highlight_info,
   3974     composite_info;
   3975 
   3976   unsigned int
   3977     height,
   3978     width;
   3979 
   3980   size_t
   3981     state;
   3982 
   3983   XEvent
   3984     event;
   3985 
   3986   /*
   3987     Request image file name from user.
   3988   */
   3989   XFileBrowserWidget(display,windows,"Composite",filename);
   3990   if (*filename == '\0')
   3991     return(MagickTrue);
   3992   /*
   3993     Read image.
   3994   */
   3995   XSetCursorState(display,windows,MagickTrue);
   3996   XCheckRefreshWindows(display,windows);
   3997   (void) CopyMagickString(resource_info->image_info->filename,filename,
   3998     MagickPathExtent);
   3999   composite_image=ReadImage(resource_info->image_info,exception);
   4000   CatchException(exception);
   4001   XSetCursorState(display,windows,MagickFalse);
   4002   if (composite_image == (Image *) NULL)
   4003     return(MagickFalse);
   4004   /*
   4005     Map Command widget.
   4006   */
   4007   (void) CloneString(&windows->command.name,"Composite");
   4008   windows->command.data=1;
   4009   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
   4010   (void) XMapRaised(display,windows->command.id);
   4011   XClientMessage(display,windows->image.id,windows->im_protocols,
   4012     windows->im_update_widget,CurrentTime);
   4013   /*
   4014     Track pointer until button 1 is pressed.
   4015   */
   4016   XQueryPosition(display,windows->image.id,&x,&y);
   4017   (void) XSelectInput(display,windows->image.id,
   4018     windows->image.attributes.event_mask | PointerMotionMask);
   4019   composite_info.x=(ssize_t) windows->image.x+x;
   4020   composite_info.y=(ssize_t) windows->image.y+y;
   4021   composite_info.width=0;
   4022   composite_info.height=0;
   4023   cursor=XCreateFontCursor(display,XC_ul_angle);
   4024   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
   4025   blend=0.0;
   4026   state=DefaultState;
   4027   do
   4028   {
   4029     if (windows->info.mapped != MagickFalse )
   4030       {
   4031         /*
   4032           Display pointer position.
   4033         */
   4034         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
   4035           (long) composite_info.x,(long) composite_info.y);
   4036         XInfoWidget(display,windows,text);
   4037       }
   4038     highlight_info=composite_info;
   4039     highlight_info.x=composite_info.x-windows->image.x;
   4040     highlight_info.y=composite_info.y-windows->image.y;
   4041     XHighlightRectangle(display,windows->image.id,
   4042       windows->image.highlight_context,&highlight_info);
   4043     /*
   4044       Wait for next event.
   4045     */
   4046     XScreenEvent(display,windows,&event,exception);
   4047     XHighlightRectangle(display,windows->image.id,
   4048       windows->image.highlight_context,&highlight_info);
   4049     if (event.xany.window == windows->command.id)
   4050       {
   4051         /*
   4052           Select a command from the Command widget.
   4053         */
   4054         id=XCommandWidget(display,windows,CompositeMenu,&event);
   4055         if (id < 0)
   4056           continue;
   4057         switch (CompositeCommands[id])
   4058         {
   4059           case CompositeOperatorsCommand:
   4060           {
   4061             char
   4062               command[MagickPathExtent],
   4063               **operators;
   4064 
   4065             /*
   4066               Select a command from the pop-up menu.
   4067             */
   4068             operators=GetCommandOptions(MagickComposeOptions);
   4069             if (operators == (char **) NULL)
   4070               break;
   4071             entry=XMenuWidget(display,windows,CompositeMenu[id],
   4072               (const char **) operators,command);
   4073             if (entry >= 0)
   4074               compose=(CompositeOperator) ParseCommandOption(
   4075                 MagickComposeOptions,MagickFalse,operators[entry]);
   4076             operators=DestroyStringList(operators);
   4077             break;
   4078           }
   4079           case CompositeDissolveCommand:
   4080           {
   4081             static char
   4082               factor[MagickPathExtent] = "20.0";
   4083 
   4084             /*
   4085               Dissolve the two images a given percent.
   4086             */
   4087             (void) XSetFunction(display,windows->image.highlight_context,
   4088               GXcopy);
   4089             (void) XDialogWidget(display,windows,"Dissolve",
   4090               "Enter the blend factor (0.0 - 99.9%):",factor);
   4091             (void) XSetFunction(display,windows->image.highlight_context,
   4092               GXinvert);
   4093             if (*factor == '\0')
   4094               break;
   4095             blend=StringToDouble(factor,(char **) NULL);
   4096             compose=DissolveCompositeOp;
   4097             break;
   4098           }
   4099           case CompositeDisplaceCommand:
   4100           {
   4101             /*
   4102               Get horizontal and vertical scale displacement geometry.
   4103             */
   4104             (void) XSetFunction(display,windows->image.highlight_context,
   4105               GXcopy);
   4106             (void) XDialogWidget(display,windows,"Displace",
   4107               "Enter the horizontal and vertical scale:",displacement_geometry);
   4108             (void) XSetFunction(display,windows->image.highlight_context,
   4109               GXinvert);
   4110             if (*displacement_geometry == '\0')
   4111               break;
   4112             compose=DisplaceCompositeOp;
   4113             break;
   4114           }
   4115           case CompositeHelpCommand:
   4116           {
   4117             (void) XSetFunction(display,windows->image.highlight_context,
   4118               GXcopy);
   4119             XTextViewWidget(display,resource_info,windows,MagickFalse,
   4120               "Help Viewer - Image Composite",ImageCompositeHelp);
   4121             (void) XSetFunction(display,windows->image.highlight_context,
   4122               GXinvert);
   4123             break;
   4124           }
   4125           case CompositeDismissCommand:
   4126           {
   4127             /*
   4128               Prematurely exit.
   4129             */
   4130             state|=EscapeState;
   4131             state|=ExitState;
   4132             break;
   4133           }
   4134           default:
   4135             break;
   4136         }
   4137         continue;
   4138       }
   4139     switch (event.type)
   4140     {
   4141       case ButtonPress:
   4142       {
   4143         if (image->debug != MagickFalse )
   4144           (void) LogMagickEvent(X11Event,GetMagickModule(),
   4145             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
   4146             event.xbutton.button,event.xbutton.x,event.xbutton.y);
   4147         if (event.xbutton.button != Button1)
   4148           break;
   4149         if (event.xbutton.window != windows->image.id)
   4150           break;
   4151         /*
   4152           Change cursor.
   4153         */
   4154         composite_info.width=composite_image->columns;
   4155         composite_info.height=composite_image->rows;
   4156         (void) XCheckDefineCursor(display,windows->image.id,cursor);
   4157         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
   4158         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
   4159         break;
   4160       }
   4161       case ButtonRelease:
   4162       {
   4163         if (image->debug != MagickFalse )
   4164           (void) LogMagickEvent(X11Event,GetMagickModule(),
   4165             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
   4166             event.xbutton.button,event.xbutton.x,event.xbutton.y);
   4167         if (event.xbutton.button != Button1)
   4168           break;
   4169         if (event.xbutton.window != windows->image.id)
   4170           break;
   4171         if ((composite_info.width != 0) && (composite_info.height != 0))
   4172           {
   4173             /*
   4174               User has selected the location of the composite image.
   4175             */
   4176             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
   4177             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
   4178             state|=ExitState;
   4179           }
   4180         break;
   4181       }
   4182       case Expose:
   4183         break;
   4184       case KeyPress:
   4185       {
   4186         char
   4187           command[MagickPathExtent];
   4188 
   4189         KeySym
   4190           key_symbol;
   4191 
   4192         int
   4193           length;
   4194 
   4195         if (event.xkey.window != windows->image.id)
   4196           break;
   4197         /*
   4198           Respond to a user key press.
   4199         */
   4200         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
   4201           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   4202         *(command+length)='\0';
   4203         if (image->debug != MagickFalse )
   4204           (void) LogMagickEvent(X11Event,GetMagickModule(),
   4205             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
   4206         switch ((int) key_symbol)
   4207         {
   4208           case XK_Escape:
   4209           case XK_F20:
   4210           {
   4211             /*
   4212               Prematurely exit.
   4213             */
   4214             composite_image=DestroyImage(composite_image);
   4215             state|=EscapeState;
   4216             state|=ExitState;
   4217             break;
   4218           }
   4219           case XK_F1:
   4220           case XK_Help:
   4221           {
   4222             (void) XSetFunction(display,windows->image.highlight_context,
   4223               GXcopy);
   4224             XTextViewWidget(display,resource_info,windows,MagickFalse,
   4225               "Help Viewer - Image Composite",ImageCompositeHelp);
   4226             (void) XSetFunction(display,windows->image.highlight_context,
   4227               GXinvert);
   4228             break;
   4229           }
   4230           default:
   4231           {
   4232             (void) XBell(display,0);
   4233             break;
   4234           }
   4235         }
   4236         break;
   4237       }
   4238       case MotionNotify:
   4239       {
   4240         /*
   4241           Map and unmap Info widget as text cursor crosses its boundaries.
   4242         */
   4243         x=event.xmotion.x;
   4244         y=event.xmotion.y;
   4245         if (windows->info.mapped != MagickFalse )
   4246           {
   4247             if ((x < (int) (windows->info.x+windows->info.width)) &&
   4248                 (y < (int) (windows->info.y+windows->info.height)))
   4249               (void) XWithdrawWindow(display,windows->info.id,
   4250                 windows->info.screen);
   4251           }
   4252         else
   4253           if ((x > (int) (windows->info.x+windows->info.width)) ||
   4254               (y > (int) (windows->info.y+windows->info.height)))
   4255             (void) XMapWindow(display,windows->info.id);
   4256         composite_info.x=(ssize_t) windows->image.x+x;
   4257         composite_info.y=(ssize_t) windows->image.y+y;
   4258         break;
   4259       }
   4260       default:
   4261       {
   4262         if (image->debug != MagickFalse )
   4263           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
   4264             event.type);
   4265         break;
   4266       }
   4267     }
   4268   } while ((state & ExitState) == 0);
   4269   (void) XSelectInput(display,windows->image.id,
   4270     windows->image.attributes.event_mask);
   4271   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
   4272   XSetCursorState(display,windows,MagickFalse);
   4273   (void) XFreeCursor(display,cursor);
   4274   if ((state & EscapeState) != 0)
   4275     return(MagickTrue);
   4276   /*
   4277     Image compositing is relative to image configuration.
   4278   */
   4279   XSetCursorState(display,windows,MagickTrue);
   4280   XCheckRefreshWindows(display,windows);
   4281   width=(unsigned int) image->columns;
   4282   height=(unsigned int) image->rows;
   4283   x=0;
   4284   y=0;
   4285   if (windows->image.crop_geometry != (char *) NULL)
   4286     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
   4287   scale_factor=(double) width/windows->image.ximage->width;
   4288   composite_info.x+=x;
   4289   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
   4290   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
   4291   scale_factor=(double) height/windows->image.ximage->height;
   4292   composite_info.y+=y;
   4293   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
   4294   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
   4295   if ((composite_info.width != composite_image->columns) ||
   4296       (composite_info.height != composite_image->rows))
   4297     {
   4298       Image
   4299         *resize_image;
   4300 
   4301       /*
   4302         Scale composite image.
   4303       */
   4304       resize_image=ResizeImage(composite_image,composite_info.width,
   4305         composite_info.height,composite_image->filter,exception);
   4306       composite_image=DestroyImage(composite_image);
   4307       if (resize_image == (Image *) NULL)
   4308         {
   4309           XSetCursorState(display,windows,MagickFalse);
   4310           return(MagickFalse);
   4311         }
   4312       composite_image=resize_image;
   4313     }
   4314   if (compose == DisplaceCompositeOp)
   4315     (void) SetImageArtifact(composite_image,"compose:args",
   4316       displacement_geometry);
   4317   if (blend != 0.0)
   4318     {
   4319       CacheView
   4320         *image_view;
   4321 
   4322       int
   4323         y;
   4324 
   4325       Quantum
   4326         opacity;
   4327 
   4328       register int
   4329         x;
   4330 
   4331       register Quantum
   4332         *q;
   4333 
   4334       /*
   4335         Create mattes for blending.
   4336       */
   4337       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
   4338       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
   4339         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
   4340       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
   4341         return(MagickFalse);
   4342       image->alpha_trait=BlendPixelTrait;
   4343       image_view=AcquireAuthenticCacheView(image,exception);
   4344       for (y=0; y < (int) image->rows; y++)
   4345       {
   4346         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
   4347           exception);
   4348         if (q == (Quantum *) NULL)
   4349           break;
   4350         for (x=0; x < (int) image->columns; x++)
   4351         {
   4352           SetPixelAlpha(image,opacity,q);
   4353           q+=GetPixelChannels(image);
   4354         }
   4355         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   4356           break;
   4357       }
   4358       image_view=DestroyCacheView(image_view);
   4359     }
   4360   /*
   4361     Composite image with X Image window.
   4362   */
   4363   (void) CompositeImage(image,composite_image,compose,MagickTrue,
   4364     composite_info.x,composite_info.y,exception);
   4365   composite_image=DestroyImage(composite_image);
   4366   XSetCursorState(display,windows,MagickFalse);
   4367   /*
   4368     Update image configuration.
   4369   */
   4370   XConfigureImageColormap(display,resource_info,windows,image,exception);
   4371   (void) XConfigureImage(display,resource_info,windows,image,exception);
   4372   return(MagickTrue);
   4373 }
   4374 
   4375 /*
   4377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4378 %                                                                             %
   4379 %                                                                             %
   4380 %                                                                             %
   4381 +   X C o n f i g u r e I m a g e                                             %
   4382 %                                                                             %
   4383 %                                                                             %
   4384 %                                                                             %
   4385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4386 %
   4387 %  XConfigureImage() creates a new X image.  It also notifies the window
   4388 %  manager of the new image size and configures the transient widows.
   4389 %
   4390 %  The format of the XConfigureImage method is:
   4391 %
   4392 %      MagickBooleanType XConfigureImage(Display *display,
   4393 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
   4394 %        ExceptionInfo *exception)
   4395 %
   4396 %  A description of each parameter follows:
   4397 %
   4398 %    o display: Specifies a connection to an X server; returned from
   4399 %      XOpenDisplay.
   4400 %
   4401 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   4402 %
   4403 %    o windows: Specifies a pointer to a XWindows structure.
   4404 %
   4405 %    o image: the image.
   4406 %
   4407 %    o exception: return any errors or warnings in this structure.
   4408 %
   4409 %    o exception: return any errors or warnings in this structure.
   4410 %
   4411 */
   4412 static MagickBooleanType XConfigureImage(Display *display,
   4413   XResourceInfo *resource_info,XWindows *windows,Image *image,
   4414   ExceptionInfo *exception)
   4415 {
   4416   char
   4417     geometry[MagickPathExtent];
   4418 
   4419   MagickStatusType
   4420     status;
   4421 
   4422   size_t
   4423     mask,
   4424     height,
   4425     width;
   4426 
   4427   ssize_t
   4428     x,
   4429     y;
   4430 
   4431   XSizeHints
   4432     *size_hints;
   4433 
   4434   XWindowChanges
   4435     window_changes;
   4436 
   4437   /*
   4438     Dismiss if window dimensions are zero.
   4439   */
   4440   width=(unsigned int) windows->image.window_changes.width;
   4441   height=(unsigned int) windows->image.window_changes.height;
   4442   if (image->debug != MagickFalse )
   4443     (void) LogMagickEvent(X11Event,GetMagickModule(),
   4444       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
   4445       windows->image.ximage->height,(double) width,(double) height);
   4446   if ((width*height) == 0)
   4447     return(MagickTrue);
   4448   x=0;
   4449   y=0;
   4450   /*
   4451     Resize image to fit Image window dimensions.
   4452   */
   4453   XSetCursorState(display,windows,MagickTrue);
   4454   (void) XFlush(display);
   4455   if (((int) width != windows->image.ximage->width) ||
   4456       ((int) height != windows->image.ximage->height))
   4457     image->taint=MagickTrue;
   4458   windows->magnify.x=(int)
   4459     width*windows->magnify.x/windows->image.ximage->width;
   4460   windows->magnify.y=(int)
   4461     height*windows->magnify.y/windows->image.ximage->height;
   4462   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
   4463   windows->image.y=(int)
   4464     (height*windows->image.y/windows->image.ximage->height);
   4465   status=XMakeImage(display,resource_info,&windows->image,image,
   4466     (unsigned int) width,(unsigned int) height,exception);
   4467   if (status == MagickFalse)
   4468     XNoticeWidget(display,windows,"Unable to configure X image:",
   4469       windows->image.name);
   4470   /*
   4471     Notify window manager of the new configuration.
   4472   */
   4473   if (resource_info->image_geometry != (char *) NULL)
   4474     (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
   4475       resource_info->image_geometry);
   4476   else
   4477     (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
   4478       XDisplayWidth(display,windows->image.screen),
   4479       XDisplayHeight(display,windows->image.screen));
   4480   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
   4481   window_changes.width=(int) width;
   4482   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
   4483     window_changes.width=XDisplayWidth(display,windows->image.screen);
   4484   window_changes.height=(int) height;
   4485   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
   4486     window_changes.height=XDisplayHeight(display,windows->image.screen);
   4487   mask=(size_t) (CWWidth | CWHeight);
   4488   if (resource_info->backdrop)
   4489     {
   4490       mask|=CWX | CWY;
   4491       window_changes.x=(int)
   4492         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
   4493       window_changes.y=(int)
   4494         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
   4495     }
   4496   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
   4497     (unsigned int) mask,&window_changes);
   4498   (void) XClearWindow(display,windows->image.id);
   4499   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
   4500   /*
   4501     Update Magnify window configuration.
   4502   */
   4503   if (windows->magnify.mapped != MagickFalse )
   4504     XMakeMagnifyImage(display,windows,exception);
   4505   windows->pan.crop_geometry=windows->image.crop_geometry;
   4506   XBestIconSize(display,&windows->pan,image);
   4507   while (((windows->pan.width << 1) < MaxIconSize) &&
   4508          ((windows->pan.height << 1) < MaxIconSize))
   4509   {
   4510     windows->pan.width<<=1;
   4511     windows->pan.height<<=1;
   4512   }
   4513   if (windows->pan.geometry != (char *) NULL)
   4514     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
   4515       &windows->pan.width,&windows->pan.height);
   4516   window_changes.width=(int) windows->pan.width;
   4517   window_changes.height=(int) windows->pan.height;
   4518   size_hints=XAllocSizeHints();
   4519   if (size_hints != (XSizeHints *) NULL)
   4520     {
   4521       /*
   4522         Set new size hints.
   4523       */
   4524       size_hints->flags=PSize | PMinSize | PMaxSize;
   4525       size_hints->width=window_changes.width;
   4526       size_hints->height=window_changes.height;
   4527       size_hints->min_width=size_hints->width;
   4528       size_hints->min_height=size_hints->height;
   4529       size_hints->max_width=size_hints->width;
   4530       size_hints->max_height=size_hints->height;
   4531       (void) XSetNormalHints(display,windows->pan.id,size_hints);
   4532       (void) XFree((void *) size_hints);
   4533     }
   4534   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
   4535     (unsigned int) (CWWidth | CWHeight),&window_changes);
   4536   /*
   4537     Update icon window configuration.
   4538   */
   4539   windows->icon.crop_geometry=windows->image.crop_geometry;
   4540   XBestIconSize(display,&windows->icon,image);
   4541   window_changes.width=(int) windows->icon.width;
   4542   window_changes.height=(int) windows->icon.height;
   4543   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
   4544     (unsigned int) (CWWidth | CWHeight),&window_changes);
   4545   XSetCursorState(display,windows,MagickFalse);
   4546   return(status != 0 ? MagickTrue : MagickFalse);
   4547 }
   4548 
   4549 /*
   4551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4552 %                                                                             %
   4553 %                                                                             %
   4554 %                                                                             %
   4555 +   X C r o p I m a g e                                                       %
   4556 %                                                                             %
   4557 %                                                                             %
   4558 %                                                                             %
   4559 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4560 %
   4561 %  XCropImage() allows the user to select a region of the image and crop, copy,
   4562 %  or cut it.  For copy or cut, the image can subsequently be composited onto
   4563 %  the image with XPasteImage.
   4564 %
   4565 %  The format of the XCropImage method is:
   4566 %
   4567 %      MagickBooleanType XCropImage(Display *display,
   4568 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
   4569 %        const ClipboardMode mode,ExceptionInfo *exception)
   4570 %
   4571 %  A description of each parameter follows:
   4572 %
   4573 %    o display: Specifies a connection to an X server; returned from
   4574 %      XOpenDisplay.
   4575 %
   4576 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   4577 %
   4578 %    o windows: Specifies a pointer to a XWindows structure.
   4579 %
   4580 %    o image: the image; returned from ReadImage.
   4581 %
   4582 %    o mode: This unsigned value specified whether the image should be
   4583 %      cropped, copied, or cut.
   4584 %
   4585 %    o exception: return any errors or warnings in this structure.
   4586 %
   4587 */
   4588 static MagickBooleanType XCropImage(Display *display,
   4589   XResourceInfo *resource_info,XWindows *windows,Image *image,
   4590   const ClipboardMode mode,ExceptionInfo *exception)
   4591 {
   4592   static const char
   4593     *CropModeMenu[] =
   4594     {
   4595       "Help",
   4596       "Dismiss",
   4597       (char *) NULL
   4598     },
   4599     *RectifyModeMenu[] =
   4600     {
   4601       "Crop",
   4602       "Help",
   4603       "Dismiss",
   4604       (char *) NULL
   4605     };
   4606 
   4607   static const ModeType
   4608     CropCommands[] =
   4609     {
   4610       CropHelpCommand,
   4611       CropDismissCommand
   4612     },
   4613     RectifyCommands[] =
   4614     {
   4615       RectifyCopyCommand,
   4616       RectifyHelpCommand,
   4617       RectifyDismissCommand
   4618     };
   4619 
   4620   CacheView
   4621     *image_view;
   4622 
   4623   char
   4624     command[MagickPathExtent],
   4625     text[MagickPathExtent];
   4626 
   4627   Cursor
   4628     cursor;
   4629 
   4630   int
   4631     id,
   4632     x,
   4633     y;
   4634 
   4635   KeySym
   4636     key_symbol;
   4637 
   4638   Image
   4639     *crop_image;
   4640 
   4641   double
   4642     scale_factor;
   4643 
   4644   RectangleInfo
   4645     crop_info,
   4646     highlight_info;
   4647 
   4648   register Quantum
   4649     *q;
   4650 
   4651   unsigned int
   4652     height,
   4653     width;
   4654 
   4655   size_t
   4656     state;
   4657 
   4658   XEvent
   4659     event;
   4660 
   4661   /*
   4662     Map Command widget.
   4663   */
   4664   switch (mode)
   4665   {
   4666     case CopyMode:
   4667     {
   4668       (void) CloneString(&windows->command.name,"Copy");
   4669       break;
   4670     }
   4671     case CropMode:
   4672     {
   4673       (void) CloneString(&windows->command.name,"Crop");
   4674       break;
   4675     }
   4676     case CutMode:
   4677     {
   4678       (void) CloneString(&windows->command.name,"Cut");
   4679       break;
   4680     }
   4681   }
   4682   RectifyModeMenu[0]=windows->command.name;
   4683   windows->command.data=0;
   4684   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
   4685   (void) XMapRaised(display,windows->command.id);
   4686   XClientMessage(display,windows->image.id,windows->im_protocols,
   4687     windows->im_update_widget,CurrentTime);
   4688   /*
   4689     Track pointer until button 1 is pressed.
   4690   */
   4691   XQueryPosition(display,windows->image.id,&x,&y);
   4692   (void) XSelectInput(display,windows->image.id,
   4693     windows->image.attributes.event_mask | PointerMotionMask);
   4694   crop_info.x=(ssize_t) windows->image.x+x;
   4695   crop_info.y=(ssize_t) windows->image.y+y;
   4696   crop_info.width=0;
   4697   crop_info.height=0;
   4698   cursor=XCreateFontCursor(display,XC_fleur);
   4699   state=DefaultState;
   4700   do
   4701   {
   4702     if (windows->info.mapped != MagickFalse )
   4703       {
   4704         /*
   4705           Display pointer position.
   4706         */
   4707         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
   4708           (long) crop_info.x,(long) crop_info.y);
   4709         XInfoWidget(display,windows,text);
   4710       }
   4711     /*
   4712       Wait for next event.
   4713     */
   4714     XScreenEvent(display,windows,&event,exception);
   4715     if (event.xany.window == windows->command.id)
   4716       {
   4717         /*
   4718           Select a command from the Command widget.
   4719         */
   4720         id=XCommandWidget(display,windows,CropModeMenu,&event);
   4721         if (id < 0)
   4722           continue;
   4723         switch (CropCommands[id])
   4724         {
   4725           case CropHelpCommand:
   4726           {
   4727             switch (mode)
   4728             {
   4729               case CopyMode:
   4730               {
   4731                 XTextViewWidget(display,resource_info,windows,MagickFalse,
   4732                   "Help Viewer - Image Copy",ImageCopyHelp);
   4733                 break;
   4734               }
   4735               case CropMode:
   4736               {
   4737                 XTextViewWidget(display,resource_info,windows,MagickFalse,
   4738                   "Help Viewer - Image Crop",ImageCropHelp);
   4739                 break;
   4740               }
   4741               case CutMode:
   4742               {
   4743                 XTextViewWidget(display,resource_info,windows,MagickFalse,
   4744                   "Help Viewer - Image Cut",ImageCutHelp);
   4745                 break;
   4746               }
   4747             }
   4748             break;
   4749           }
   4750           case CropDismissCommand:
   4751           {
   4752             /*
   4753               Prematurely exit.
   4754             */
   4755             state|=EscapeState;
   4756             state|=ExitState;
   4757             break;
   4758           }
   4759           default:
   4760             break;
   4761         }
   4762         continue;
   4763       }
   4764     switch (event.type)
   4765     {
   4766       case ButtonPress:
   4767       {
   4768         if (event.xbutton.button != Button1)
   4769           break;
   4770         if (event.xbutton.window != windows->image.id)
   4771           break;
   4772         /*
   4773           Note first corner of cropping rectangle-- exit loop.
   4774         */
   4775         (void) XCheckDefineCursor(display,windows->image.id,cursor);
   4776         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
   4777         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
   4778         state|=ExitState;
   4779         break;
   4780       }
   4781       case ButtonRelease:
   4782         break;
   4783       case Expose:
   4784         break;
   4785       case KeyPress:
   4786       {
   4787         if (event.xkey.window != windows->image.id)
   4788           break;
   4789         /*
   4790           Respond to a user key press.
   4791         */
   4792         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   4793           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   4794         switch ((int) key_symbol)
   4795         {
   4796           case XK_Escape:
   4797           case XK_F20:
   4798           {
   4799             /*
   4800               Prematurely exit.
   4801             */
   4802             state|=EscapeState;
   4803             state|=ExitState;
   4804             break;
   4805           }
   4806           case XK_F1:
   4807           case XK_Help:
   4808           {
   4809             switch (mode)
   4810             {
   4811               case CopyMode:
   4812               {
   4813                 XTextViewWidget(display,resource_info,windows,MagickFalse,
   4814                   "Help Viewer - Image Copy",ImageCopyHelp);
   4815                 break;
   4816               }
   4817               case CropMode:
   4818               {
   4819                 XTextViewWidget(display,resource_info,windows,MagickFalse,
   4820                   "Help Viewer - Image Crop",ImageCropHelp);
   4821                 break;
   4822               }
   4823               case CutMode:
   4824               {
   4825                 XTextViewWidget(display,resource_info,windows,MagickFalse,
   4826                   "Help Viewer - Image Cut",ImageCutHelp);
   4827                 break;
   4828               }
   4829             }
   4830             break;
   4831           }
   4832           default:
   4833           {
   4834             (void) XBell(display,0);
   4835             break;
   4836           }
   4837         }
   4838         break;
   4839       }
   4840       case MotionNotify:
   4841       {
   4842         if (event.xmotion.window != windows->image.id)
   4843           break;
   4844         /*
   4845           Map and unmap Info widget as text cursor crosses its boundaries.
   4846         */
   4847         x=event.xmotion.x;
   4848         y=event.xmotion.y;
   4849         if (windows->info.mapped != MagickFalse )
   4850           {
   4851             if ((x < (int) (windows->info.x+windows->info.width)) &&
   4852                 (y < (int) (windows->info.y+windows->info.height)))
   4853               (void) XWithdrawWindow(display,windows->info.id,
   4854                 windows->info.screen);
   4855           }
   4856         else
   4857           if ((x > (int) (windows->info.x+windows->info.width)) ||
   4858               (y > (int) (windows->info.y+windows->info.height)))
   4859             (void) XMapWindow(display,windows->info.id);
   4860         crop_info.x=(ssize_t) windows->image.x+x;
   4861         crop_info.y=(ssize_t) windows->image.y+y;
   4862         break;
   4863       }
   4864       default:
   4865         break;
   4866     }
   4867   } while ((state & ExitState) == 0);
   4868   (void) XSelectInput(display,windows->image.id,
   4869     windows->image.attributes.event_mask);
   4870   if ((state & EscapeState) != 0)
   4871     {
   4872       /*
   4873         User want to exit without cropping.
   4874       */
   4875       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   4876       (void) XFreeCursor(display,cursor);
   4877       return(MagickTrue);
   4878     }
   4879   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
   4880   do
   4881   {
   4882     /*
   4883       Size rectangle as pointer moves until the mouse button is released.
   4884     */
   4885     x=(int) crop_info.x;
   4886     y=(int) crop_info.y;
   4887     crop_info.width=0;
   4888     crop_info.height=0;
   4889     state=DefaultState;
   4890     do
   4891     {
   4892       highlight_info=crop_info;
   4893       highlight_info.x=crop_info.x-windows->image.x;
   4894       highlight_info.y=crop_info.y-windows->image.y;
   4895       if ((highlight_info.width > 3) && (highlight_info.height > 3))
   4896         {
   4897           /*
   4898             Display info and draw cropping rectangle.
   4899           */
   4900           if (windows->info.mapped == MagickFalse)
   4901             (void) XMapWindow(display,windows->info.id);
   4902           (void) FormatLocaleString(text,MagickPathExtent,
   4903             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
   4904             crop_info.height,(double) crop_info.x,(double) crop_info.y);
   4905           XInfoWidget(display,windows,text);
   4906           XHighlightRectangle(display,windows->image.id,
   4907             windows->image.highlight_context,&highlight_info);
   4908         }
   4909       else
   4910         if (windows->info.mapped != MagickFalse )
   4911           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   4912       /*
   4913         Wait for next event.
   4914       */
   4915       XScreenEvent(display,windows,&event,exception);
   4916       if ((highlight_info.width > 3) && (highlight_info.height > 3))
   4917         XHighlightRectangle(display,windows->image.id,
   4918           windows->image.highlight_context,&highlight_info);
   4919       switch (event.type)
   4920       {
   4921         case ButtonPress:
   4922         {
   4923           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
   4924           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
   4925           break;
   4926         }
   4927         case ButtonRelease:
   4928         {
   4929           /*
   4930             User has committed to cropping rectangle.
   4931           */
   4932           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
   4933           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
   4934           XSetCursorState(display,windows,MagickFalse);
   4935           state|=ExitState;
   4936           windows->command.data=0;
   4937           (void) XCommandWidget(display,windows,RectifyModeMenu,
   4938             (XEvent *) NULL);
   4939           break;
   4940         }
   4941         case Expose:
   4942           break;
   4943         case MotionNotify:
   4944         {
   4945           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
   4946           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
   4947         }
   4948         default:
   4949           break;
   4950       }
   4951       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
   4952           ((state & ExitState) != 0))
   4953         {
   4954           /*
   4955             Check boundary conditions.
   4956           */
   4957           if (crop_info.x < 0)
   4958             crop_info.x=0;
   4959           else
   4960             if (crop_info.x > (ssize_t) windows->image.ximage->width)
   4961               crop_info.x=(ssize_t) windows->image.ximage->width;
   4962           if ((int) crop_info.x < x)
   4963             crop_info.width=(unsigned int) (x-crop_info.x);
   4964           else
   4965             {
   4966               crop_info.width=(unsigned int) (crop_info.x-x);
   4967               crop_info.x=(ssize_t) x;
   4968             }
   4969           if (crop_info.y < 0)
   4970             crop_info.y=0;
   4971           else
   4972             if (crop_info.y > (ssize_t) windows->image.ximage->height)
   4973               crop_info.y=(ssize_t) windows->image.ximage->height;
   4974           if ((int) crop_info.y < y)
   4975             crop_info.height=(unsigned int) (y-crop_info.y);
   4976           else
   4977             {
   4978               crop_info.height=(unsigned int) (crop_info.y-y);
   4979               crop_info.y=(ssize_t) y;
   4980             }
   4981         }
   4982     } while ((state & ExitState) == 0);
   4983     /*
   4984       Wait for user to grab a corner of the rectangle or press return.
   4985     */
   4986     state=DefaultState;
   4987     (void) XMapWindow(display,windows->info.id);
   4988     do
   4989     {
   4990       if (windows->info.mapped != MagickFalse )
   4991         {
   4992           /*
   4993             Display pointer position.
   4994           */
   4995           (void) FormatLocaleString(text,MagickPathExtent,
   4996             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
   4997             crop_info.height,(double) crop_info.x,(double) crop_info.y);
   4998           XInfoWidget(display,windows,text);
   4999         }
   5000       highlight_info=crop_info;
   5001       highlight_info.x=crop_info.x-windows->image.x;
   5002       highlight_info.y=crop_info.y-windows->image.y;
   5003       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
   5004         {
   5005           state|=EscapeState;
   5006           state|=ExitState;
   5007           break;
   5008         }
   5009       XHighlightRectangle(display,windows->image.id,
   5010         windows->image.highlight_context,&highlight_info);
   5011       XScreenEvent(display,windows,&event,exception);
   5012       if (event.xany.window == windows->command.id)
   5013         {
   5014           /*
   5015             Select a command from the Command widget.
   5016           */
   5017           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
   5018           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
   5019           (void) XSetFunction(display,windows->image.highlight_context,
   5020             GXinvert);
   5021           XHighlightRectangle(display,windows->image.id,
   5022             windows->image.highlight_context,&highlight_info);
   5023           if (id >= 0)
   5024             switch (RectifyCommands[id])
   5025             {
   5026               case RectifyCopyCommand:
   5027               {
   5028                 state|=ExitState;
   5029                 break;
   5030               }
   5031               case RectifyHelpCommand:
   5032               {
   5033                 (void) XSetFunction(display,windows->image.highlight_context,
   5034                   GXcopy);
   5035                 switch (mode)
   5036                 {
   5037                   case CopyMode:
   5038                   {
   5039                     XTextViewWidget(display,resource_info,windows,MagickFalse,
   5040                       "Help Viewer - Image Copy",ImageCopyHelp);
   5041                     break;
   5042                   }
   5043                   case CropMode:
   5044                   {
   5045                     XTextViewWidget(display,resource_info,windows,MagickFalse,
   5046                       "Help Viewer - Image Crop",ImageCropHelp);
   5047                     break;
   5048                   }
   5049                   case CutMode:
   5050                   {
   5051                     XTextViewWidget(display,resource_info,windows,MagickFalse,
   5052                       "Help Viewer - Image Cut",ImageCutHelp);
   5053                     break;
   5054                   }
   5055                 }
   5056                 (void) XSetFunction(display,windows->image.highlight_context,
   5057                   GXinvert);
   5058                 break;
   5059               }
   5060               case RectifyDismissCommand:
   5061               {
   5062                 /*
   5063                   Prematurely exit.
   5064                 */
   5065                 state|=EscapeState;
   5066                 state|=ExitState;
   5067                 break;
   5068               }
   5069               default:
   5070                 break;
   5071             }
   5072           continue;
   5073         }
   5074       XHighlightRectangle(display,windows->image.id,
   5075         windows->image.highlight_context,&highlight_info);
   5076       switch (event.type)
   5077       {
   5078         case ButtonPress:
   5079         {
   5080           if (event.xbutton.button != Button1)
   5081             break;
   5082           if (event.xbutton.window != windows->image.id)
   5083             break;
   5084           x=windows->image.x+event.xbutton.x;
   5085           y=windows->image.y+event.xbutton.y;
   5086           if ((x < (int) (crop_info.x+RoiDelta)) &&
   5087               (x > (int) (crop_info.x-RoiDelta)) &&
   5088               (y < (int) (crop_info.y+RoiDelta)) &&
   5089               (y > (int) (crop_info.y-RoiDelta)))
   5090             {
   5091               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
   5092               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
   5093               state|=UpdateConfigurationState;
   5094               break;
   5095             }
   5096           if ((x < (int) (crop_info.x+RoiDelta)) &&
   5097               (x > (int) (crop_info.x-RoiDelta)) &&
   5098               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
   5099               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
   5100             {
   5101               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
   5102               state|=UpdateConfigurationState;
   5103               break;
   5104             }
   5105           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
   5106               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
   5107               (y < (int) (crop_info.y+RoiDelta)) &&
   5108               (y > (int) (crop_info.y-RoiDelta)))
   5109             {
   5110               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
   5111               state|=UpdateConfigurationState;
   5112               break;
   5113             }
   5114           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
   5115               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
   5116               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
   5117               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
   5118             {
   5119               state|=UpdateConfigurationState;
   5120               break;
   5121             }
   5122         }
   5123         case ButtonRelease:
   5124         {
   5125           if (event.xbutton.window == windows->pan.id)
   5126             if ((highlight_info.x != crop_info.x-windows->image.x) ||
   5127                 (highlight_info.y != crop_info.y-windows->image.y))
   5128               XHighlightRectangle(display,windows->image.id,
   5129                 windows->image.highlight_context,&highlight_info);
   5130           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
   5131             event.xbutton.time);
   5132           break;
   5133         }
   5134         case Expose:
   5135         {
   5136           if (event.xexpose.window == windows->image.id)
   5137             if (event.xexpose.count == 0)
   5138               {
   5139                 event.xexpose.x=(int) highlight_info.x;
   5140                 event.xexpose.y=(int) highlight_info.y;
   5141                 event.xexpose.width=(int) highlight_info.width;
   5142                 event.xexpose.height=(int) highlight_info.height;
   5143                 XRefreshWindow(display,&windows->image,&event);
   5144               }
   5145           if (event.xexpose.window == windows->info.id)
   5146             if (event.xexpose.count == 0)
   5147               XInfoWidget(display,windows,text);
   5148           break;
   5149         }
   5150         case KeyPress:
   5151         {
   5152           if (event.xkey.window != windows->image.id)
   5153             break;
   5154           /*
   5155             Respond to a user key press.
   5156           */
   5157           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   5158             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   5159           switch ((int) key_symbol)
   5160           {
   5161             case XK_Escape:
   5162             case XK_F20:
   5163               state|=EscapeState;
   5164             case XK_Return:
   5165             {
   5166               state|=ExitState;
   5167               break;
   5168             }
   5169             case XK_Home:
   5170             case XK_KP_Home:
   5171             {
   5172               crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
   5173                 2L);
   5174               crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
   5175                 2L);
   5176               break;
   5177             }
   5178             case XK_Left:
   5179             case XK_KP_Left:
   5180             {
   5181               crop_info.x--;
   5182               break;
   5183             }
   5184             case XK_Up:
   5185             case XK_KP_Up:
   5186             case XK_Next:
   5187             {
   5188               crop_info.y--;
   5189               break;
   5190             }
   5191             case XK_Right:
   5192             case XK_KP_Right:
   5193             {
   5194               crop_info.x++;
   5195               break;
   5196             }
   5197             case XK_Prior:
   5198             case XK_Down:
   5199             case XK_KP_Down:
   5200             {
   5201               crop_info.y++;
   5202               break;
   5203             }
   5204             case XK_F1:
   5205             case XK_Help:
   5206             {
   5207               (void) XSetFunction(display,windows->image.highlight_context,
   5208                 GXcopy);
   5209               switch (mode)
   5210               {
   5211                 case CopyMode:
   5212                 {
   5213                   XTextViewWidget(display,resource_info,windows,MagickFalse,
   5214                     "Help Viewer - Image Copy",ImageCopyHelp);
   5215                   break;
   5216                 }
   5217                 case CropMode:
   5218                 {
   5219                   XTextViewWidget(display,resource_info,windows,MagickFalse,
   5220                     "Help Viewer - Image Cropg",ImageCropHelp);
   5221                   break;
   5222                 }
   5223                 case CutMode:
   5224                 {
   5225                   XTextViewWidget(display,resource_info,windows,MagickFalse,
   5226                     "Help Viewer - Image Cutg",ImageCutHelp);
   5227                   break;
   5228                 }
   5229               }
   5230               (void) XSetFunction(display,windows->image.highlight_context,
   5231                 GXinvert);
   5232               break;
   5233             }
   5234             default:
   5235             {
   5236               (void) XBell(display,0);
   5237               break;
   5238             }
   5239           }
   5240           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
   5241             event.xkey.time);
   5242           break;
   5243         }
   5244         case KeyRelease:
   5245           break;
   5246         case MotionNotify:
   5247         {
   5248           if (event.xmotion.window != windows->image.id)
   5249             break;
   5250           /*
   5251             Map and unmap Info widget as text cursor crosses its boundaries.
   5252           */
   5253           x=event.xmotion.x;
   5254           y=event.xmotion.y;
   5255           if (windows->info.mapped != MagickFalse )
   5256             {
   5257               if ((x < (int) (windows->info.x+windows->info.width)) &&
   5258                   (y < (int) (windows->info.y+windows->info.height)))
   5259                 (void) XWithdrawWindow(display,windows->info.id,
   5260                   windows->info.screen);
   5261             }
   5262           else
   5263             if ((x > (int) (windows->info.x+windows->info.width)) ||
   5264                 (y > (int) (windows->info.y+windows->info.height)))
   5265               (void) XMapWindow(display,windows->info.id);
   5266           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
   5267           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
   5268           break;
   5269         }
   5270         case SelectionRequest:
   5271         {
   5272           XSelectionEvent
   5273             notify;
   5274 
   5275           XSelectionRequestEvent
   5276             *request;
   5277 
   5278           /*
   5279             Set primary selection.
   5280           */
   5281           (void) FormatLocaleString(text,MagickPathExtent,
   5282             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
   5283             crop_info.height,(double) crop_info.x,(double) crop_info.y);
   5284           request=(&(event.xselectionrequest));
   5285           (void) XChangeProperty(request->display,request->requestor,
   5286             request->property,request->target,8,PropModeReplace,
   5287             (unsigned char *) text,(int) strlen(text));
   5288           notify.type=SelectionNotify;
   5289           notify.display=request->display;
   5290           notify.requestor=request->requestor;
   5291           notify.selection=request->selection;
   5292           notify.target=request->target;
   5293           notify.time=request->time;
   5294           if (request->property == None)
   5295             notify.property=request->target;
   5296           else
   5297             notify.property=request->property;
   5298           (void) XSendEvent(request->display,request->requestor,False,0,
   5299             (XEvent *) &notify);
   5300         }
   5301         default:
   5302           break;
   5303       }
   5304       if ((state & UpdateConfigurationState) != 0)
   5305         {
   5306           (void) XPutBackEvent(display,&event);
   5307           (void) XCheckDefineCursor(display,windows->image.id,cursor);
   5308           break;
   5309         }
   5310     } while ((state & ExitState) == 0);
   5311   } while ((state & ExitState) == 0);
   5312   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
   5313   XSetCursorState(display,windows,MagickFalse);
   5314   if ((state & EscapeState) != 0)
   5315     return(MagickTrue);
   5316   if (mode == CropMode)
   5317     if (((int) crop_info.width != windows->image.ximage->width) ||
   5318         ((int) crop_info.height != windows->image.ximage->height))
   5319       {
   5320         /*
   5321           Reconfigure Image window as defined by cropping rectangle.
   5322         */
   5323         XSetCropGeometry(display,windows,&crop_info,image);
   5324         windows->image.window_changes.width=(int) crop_info.width;
   5325         windows->image.window_changes.height=(int) crop_info.height;
   5326         (void) XConfigureImage(display,resource_info,windows,image,exception);
   5327         return(MagickTrue);
   5328       }
   5329   /*
   5330     Copy image before applying image transforms.
   5331   */
   5332   XSetCursorState(display,windows,MagickTrue);
   5333   XCheckRefreshWindows(display,windows);
   5334   width=(unsigned int) image->columns;
   5335   height=(unsigned int) image->rows;
   5336   x=0;
   5337   y=0;
   5338   if (windows->image.crop_geometry != (char *) NULL)
   5339     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
   5340   scale_factor=(double) width/windows->image.ximage->width;
   5341   crop_info.x+=x;
   5342   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
   5343   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
   5344   scale_factor=(double) height/windows->image.ximage->height;
   5345   crop_info.y+=y;
   5346   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
   5347   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
   5348   crop_image=CropImage(image,&crop_info,exception);
   5349   XSetCursorState(display,windows,MagickFalse);
   5350   if (crop_image == (Image *) NULL)
   5351     return(MagickFalse);
   5352   if (resource_info->copy_image != (Image *) NULL)
   5353     resource_info->copy_image=DestroyImage(resource_info->copy_image);
   5354   resource_info->copy_image=crop_image;
   5355   if (mode == CopyMode)
   5356     {
   5357       (void) XConfigureImage(display,resource_info,windows,image,exception);
   5358       return(MagickTrue);
   5359     }
   5360   /*
   5361     Cut image.
   5362   */
   5363   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
   5364     return(MagickFalse);
   5365   image->alpha_trait=BlendPixelTrait;
   5366   image_view=AcquireAuthenticCacheView(image,exception);
   5367   for (y=0; y < (int) crop_info.height; y++)
   5368   {
   5369     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
   5370       crop_info.width,1,exception);
   5371     if (q == (Quantum *) NULL)
   5372       break;
   5373     for (x=0; x < (int) crop_info.width; x++)
   5374     {
   5375       SetPixelAlpha(image,TransparentAlpha,q);
   5376       q+=GetPixelChannels(image);
   5377     }
   5378     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   5379       break;
   5380   }
   5381   image_view=DestroyCacheView(image_view);
   5382   /*
   5383     Update image configuration.
   5384   */
   5385   XConfigureImageColormap(display,resource_info,windows,image,exception);
   5386   (void) XConfigureImage(display,resource_info,windows,image,exception);
   5387   return(MagickTrue);
   5388 }
   5389 
   5390 /*
   5392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5393 %                                                                             %
   5394 %                                                                             %
   5395 %                                                                             %
   5396 +   X D r a w I m a g e                                                       %
   5397 %                                                                             %
   5398 %                                                                             %
   5399 %                                                                             %
   5400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5401 %
   5402 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
   5403 %  the image.
   5404 %
   5405 %  The format of the XDrawEditImage method is:
   5406 %
   5407 %      MagickBooleanType XDrawEditImage(Display *display,
   5408 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
   5409 %        ExceptionInfo *exception)
   5410 %
   5411 %  A description of each parameter follows:
   5412 %
   5413 %    o display: Specifies a connection to an X server; returned from
   5414 %      XOpenDisplay.
   5415 %
   5416 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   5417 %
   5418 %    o windows: Specifies a pointer to a XWindows structure.
   5419 %
   5420 %    o image: the image.
   5421 %
   5422 %    o exception: return any errors or warnings in this structure.
   5423 %
   5424 */
   5425 static MagickBooleanType XDrawEditImage(Display *display,
   5426   XResourceInfo *resource_info,XWindows *windows,Image **image,
   5427   ExceptionInfo *exception)
   5428 {
   5429   static const char
   5430     *DrawMenu[] =
   5431     {
   5432       "Element",
   5433       "Color",
   5434       "Stipple",
   5435       "Width",
   5436       "Undo",
   5437       "Help",
   5438       "Dismiss",
   5439       (char *) NULL
   5440     };
   5441 
   5442   static ElementType
   5443     element = PointElement;
   5444 
   5445   static const ModeType
   5446     DrawCommands[] =
   5447     {
   5448       DrawElementCommand,
   5449       DrawColorCommand,
   5450       DrawStippleCommand,
   5451       DrawWidthCommand,
   5452       DrawUndoCommand,
   5453       DrawHelpCommand,
   5454       DrawDismissCommand
   5455     };
   5456 
   5457   static Pixmap
   5458     stipple = (Pixmap) NULL;
   5459 
   5460   static unsigned int
   5461     pen_id = 0,
   5462     line_width = 1;
   5463 
   5464   char
   5465     command[MagickPathExtent],
   5466     text[MagickPathExtent];
   5467 
   5468   Cursor
   5469     cursor;
   5470 
   5471   int
   5472     entry,
   5473     id,
   5474     number_coordinates,
   5475     x,
   5476     y;
   5477 
   5478   double
   5479     degrees;
   5480 
   5481   MagickStatusType
   5482     status;
   5483 
   5484   RectangleInfo
   5485     rectangle_info;
   5486 
   5487   register int
   5488     i;
   5489 
   5490   unsigned int
   5491     distance,
   5492     height,
   5493     max_coordinates,
   5494     width;
   5495 
   5496   size_t
   5497     state;
   5498 
   5499   Window
   5500     root_window;
   5501 
   5502   XDrawInfo
   5503     draw_info;
   5504 
   5505   XEvent
   5506     event;
   5507 
   5508   XPoint
   5509     *coordinate_info;
   5510 
   5511   XSegment
   5512     line_info;
   5513 
   5514   /*
   5515     Allocate polygon info.
   5516   */
   5517   max_coordinates=2048;
   5518   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
   5519     sizeof(*coordinate_info));
   5520   if (coordinate_info == (XPoint *) NULL)
   5521     {
   5522       (void) ThrowMagickException(exception,GetMagickModule(),
   5523         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
   5524       return(MagickFalse);
   5525     }
   5526   /*
   5527     Map Command widget.
   5528   */
   5529   (void) CloneString(&windows->command.name,"Draw");
   5530   windows->command.data=4;
   5531   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
   5532   (void) XMapRaised(display,windows->command.id);
   5533   XClientMessage(display,windows->image.id,windows->im_protocols,
   5534     windows->im_update_widget,CurrentTime);
   5535   /*
   5536     Wait for first button press.
   5537   */
   5538   root_window=XRootWindow(display,XDefaultScreen(display));
   5539   draw_info.stencil=OpaqueStencil;
   5540   status=MagickTrue;
   5541   cursor=XCreateFontCursor(display,XC_tcross);
   5542   for ( ; ; )
   5543   {
   5544     XQueryPosition(display,windows->image.id,&x,&y);
   5545     (void) XSelectInput(display,windows->image.id,
   5546       windows->image.attributes.event_mask | PointerMotionMask);
   5547     (void) XCheckDefineCursor(display,windows->image.id,cursor);
   5548     state=DefaultState;
   5549     do
   5550     {
   5551       if (windows->info.mapped != MagickFalse )
   5552         {
   5553           /*
   5554             Display pointer position.
   5555           */
   5556           (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
   5557             x+windows->image.x,y+windows->image.y);
   5558           XInfoWidget(display,windows,text);
   5559         }
   5560       /*
   5561         Wait for next event.
   5562       */
   5563       XScreenEvent(display,windows,&event,exception);
   5564       if (event.xany.window == windows->command.id)
   5565         {
   5566           /*
   5567             Select a command from the Command widget.
   5568           */
   5569           id=XCommandWidget(display,windows,DrawMenu,&event);
   5570           if (id < 0)
   5571             continue;
   5572           switch (DrawCommands[id])
   5573           {
   5574             case DrawElementCommand:
   5575             {
   5576               static const char
   5577                 *Elements[] =
   5578                 {
   5579                   "point",
   5580                   "line",
   5581                   "rectangle",
   5582                   "fill rectangle",
   5583                   "circle",
   5584                   "fill circle",
   5585                   "ellipse",
   5586                   "fill ellipse",
   5587                   "polygon",
   5588                   "fill polygon",
   5589                   (char *) NULL,
   5590                 };
   5591 
   5592               /*
   5593                 Select a command from the pop-up menu.
   5594               */
   5595               element=(ElementType) (XMenuWidget(display,windows,
   5596                 DrawMenu[id],Elements,command)+1);
   5597               break;
   5598             }
   5599             case DrawColorCommand:
   5600             {
   5601               const char
   5602                 *ColorMenu[MaxNumberPens+1];
   5603 
   5604               int
   5605                 pen_number;
   5606 
   5607               MagickBooleanType
   5608                 transparent;
   5609 
   5610               XColor
   5611                 color;
   5612 
   5613               /*
   5614                 Initialize menu selections.
   5615               */
   5616               for (i=0; i < (int) (MaxNumberPens-2); i++)
   5617                 ColorMenu[i]=resource_info->pen_colors[i];
   5618               ColorMenu[MaxNumberPens-2]="transparent";
   5619               ColorMenu[MaxNumberPens-1]="Browser...";
   5620               ColorMenu[MaxNumberPens]=(char *) NULL;
   5621               /*
   5622                 Select a pen color from the pop-up menu.
   5623               */
   5624               pen_number=XMenuWidget(display,windows,DrawMenu[id],
   5625                 (const char **) ColorMenu,command);
   5626               if (pen_number < 0)
   5627                 break;
   5628               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
   5629                 MagickFalse;
   5630               if (transparent != MagickFalse )
   5631                 {
   5632                   draw_info.stencil=TransparentStencil;
   5633                   break;
   5634                 }
   5635               if (pen_number == (MaxNumberPens-1))
   5636                 {
   5637                   static char
   5638                     color_name[MagickPathExtent] = "gray";
   5639 
   5640                   /*
   5641                     Select a pen color from a dialog.
   5642                   */
   5643                   resource_info->pen_colors[pen_number]=color_name;
   5644                   XColorBrowserWidget(display,windows,"Select",color_name);
   5645                   if (*color_name == '\0')
   5646                     break;
   5647                 }
   5648               /*
   5649                 Set pen color.
   5650               */
   5651               (void) XParseColor(display,windows->map_info->colormap,
   5652                 resource_info->pen_colors[pen_number],&color);
   5653               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
   5654                 (unsigned int) MaxColors,&color);
   5655               windows->pixel_info->pen_colors[pen_number]=color;
   5656               pen_id=(unsigned int) pen_number;
   5657               draw_info.stencil=OpaqueStencil;
   5658               break;
   5659             }
   5660             case DrawStippleCommand:
   5661             {
   5662               Image
   5663                 *stipple_image;
   5664 
   5665               ImageInfo
   5666                 *image_info;
   5667 
   5668               int
   5669                 status;
   5670 
   5671               static char
   5672                 filename[MagickPathExtent] = "\0";
   5673 
   5674               static const char
   5675                 *StipplesMenu[] =
   5676                 {
   5677                   "Brick",
   5678                   "Diagonal",
   5679                   "Scales",
   5680                   "Vertical",
   5681                   "Wavy",
   5682                   "Translucent",
   5683                   "Opaque",
   5684                   (char *) NULL,
   5685                   (char *) NULL,
   5686                 };
   5687 
   5688               /*
   5689                 Select a command from the pop-up menu.
   5690               */
   5691               StipplesMenu[7]="Open...";
   5692               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
   5693                 command);
   5694               if (entry < 0)
   5695                 break;
   5696               if (stipple != (Pixmap) NULL)
   5697                 (void) XFreePixmap(display,stipple);
   5698               stipple=(Pixmap) NULL;
   5699               if (entry != 7)
   5700                 {
   5701                   switch (entry)
   5702                   {
   5703                     case 0:
   5704                     {
   5705                       stipple=XCreateBitmapFromData(display,root_window,
   5706                         (char *) BricksBitmap,BricksWidth,BricksHeight);
   5707                       break;
   5708                     }
   5709                     case 1:
   5710                     {
   5711                       stipple=XCreateBitmapFromData(display,root_window,
   5712                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
   5713                       break;
   5714                     }
   5715                     case 2:
   5716                     {
   5717                       stipple=XCreateBitmapFromData(display,root_window,
   5718                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
   5719                       break;
   5720                     }
   5721                     case 3:
   5722                     {
   5723                       stipple=XCreateBitmapFromData(display,root_window,
   5724                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
   5725                       break;
   5726                     }
   5727                     case 4:
   5728                     {
   5729                       stipple=XCreateBitmapFromData(display,root_window,
   5730                         (char *) WavyBitmap,WavyWidth,WavyHeight);
   5731                       break;
   5732                     }
   5733                     case 5:
   5734                     {
   5735                       stipple=XCreateBitmapFromData(display,root_window,
   5736                         (char *) HighlightBitmap,HighlightWidth,
   5737                         HighlightHeight);
   5738                       break;
   5739                     }
   5740                     case 6:
   5741                     default:
   5742                     {
   5743                       stipple=XCreateBitmapFromData(display,root_window,
   5744                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
   5745                       break;
   5746                     }
   5747                   }
   5748                   break;
   5749                 }
   5750               XFileBrowserWidget(display,windows,"Stipple",filename);
   5751               if (*filename == '\0')
   5752                 break;
   5753               /*
   5754                 Read image.
   5755               */
   5756               XSetCursorState(display,windows,MagickTrue);
   5757               XCheckRefreshWindows(display,windows);
   5758               image_info=AcquireImageInfo();
   5759               (void) CopyMagickString(image_info->filename,filename,
   5760                 MagickPathExtent);
   5761               stipple_image=ReadImage(image_info,exception);
   5762               CatchException(exception);
   5763               XSetCursorState(display,windows,MagickFalse);
   5764               if (stipple_image == (Image *) NULL)
   5765                 break;
   5766               (void) AcquireUniqueFileResource(filename);
   5767               (void) FormatLocaleString(stipple_image->filename,
   5768                 MagickPathExtent,"xbm:%s",filename);
   5769               (void) WriteImage(image_info,stipple_image,exception);
   5770               stipple_image=DestroyImage(stipple_image);
   5771               image_info=DestroyImageInfo(image_info);
   5772               status=XReadBitmapFile(display,root_window,filename,&width,
   5773                 &height,&stipple,&x,&y);
   5774               (void) RelinquishUniqueFileResource(filename);
   5775               if ((status != BitmapSuccess) != 0)
   5776                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
   5777                   filename);
   5778               break;
   5779             }
   5780             case DrawWidthCommand:
   5781             {
   5782               static char
   5783                 width[MagickPathExtent] = "0";
   5784 
   5785               static const char
   5786                 *WidthsMenu[] =
   5787                 {
   5788                   "1",
   5789                   "2",
   5790                   "4",
   5791                   "8",
   5792                   "16",
   5793                   "Dialog...",
   5794                   (char *) NULL,
   5795                 };
   5796 
   5797               /*
   5798                 Select a command from the pop-up menu.
   5799               */
   5800               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
   5801                 command);
   5802               if (entry < 0)
   5803                 break;
   5804               if (entry != 5)
   5805                 {
   5806                   line_width=(unsigned int) StringToUnsignedLong(
   5807                     WidthsMenu[entry]);
   5808                   break;
   5809                 }
   5810               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
   5811                 width);
   5812               if (*width == '\0')
   5813                 break;
   5814               line_width=(unsigned int) StringToUnsignedLong(width);
   5815               break;
   5816             }
   5817             case DrawUndoCommand:
   5818             {
   5819               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
   5820                 image,exception);
   5821               break;
   5822             }
   5823             case DrawHelpCommand:
   5824             {
   5825               XTextViewWidget(display,resource_info,windows,MagickFalse,
   5826                 "Help Viewer - Image Rotation",ImageDrawHelp);
   5827               (void) XCheckDefineCursor(display,windows->image.id,cursor);
   5828               break;
   5829             }
   5830             case DrawDismissCommand:
   5831             {
   5832               /*
   5833                 Prematurely exit.
   5834               */
   5835               state|=EscapeState;
   5836               state|=ExitState;
   5837               break;
   5838             }
   5839             default:
   5840               break;
   5841           }
   5842           (void) XCheckDefineCursor(display,windows->image.id,cursor);
   5843           continue;
   5844         }
   5845       switch (event.type)
   5846       {
   5847         case ButtonPress:
   5848         {
   5849           if (event.xbutton.button != Button1)
   5850             break;
   5851           if (event.xbutton.window != windows->image.id)
   5852             break;
   5853           /*
   5854             exit loop.
   5855           */
   5856           x=event.xbutton.x;
   5857           y=event.xbutton.y;
   5858           state|=ExitState;
   5859           break;
   5860         }
   5861         case ButtonRelease:
   5862           break;
   5863         case Expose:
   5864           break;
   5865         case KeyPress:
   5866         {
   5867           KeySym
   5868             key_symbol;
   5869 
   5870           if (event.xkey.window != windows->image.id)
   5871             break;
   5872           /*
   5873             Respond to a user key press.
   5874           */
   5875           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   5876             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   5877           switch ((int) key_symbol)
   5878           {
   5879             case XK_Escape:
   5880             case XK_F20:
   5881             {
   5882               /*
   5883                 Prematurely exit.
   5884               */
   5885               state|=EscapeState;
   5886               state|=ExitState;
   5887               break;
   5888             }
   5889             case XK_F1:
   5890             case XK_Help:
   5891             {
   5892               XTextViewWidget(display,resource_info,windows,MagickFalse,
   5893                 "Help Viewer - Image Rotation",ImageDrawHelp);
   5894               break;
   5895             }
   5896             default:
   5897             {
   5898               (void) XBell(display,0);
   5899               break;
   5900             }
   5901           }
   5902           break;
   5903         }
   5904         case MotionNotify:
   5905         {
   5906           /*
   5907             Map and unmap Info widget as text cursor crosses its boundaries.
   5908           */
   5909           x=event.xmotion.x;
   5910           y=event.xmotion.y;
   5911           if (windows->info.mapped != MagickFalse )
   5912             {
   5913               if ((x < (int) (windows->info.x+windows->info.width)) &&
   5914                   (y < (int) (windows->info.y+windows->info.height)))
   5915                 (void) XWithdrawWindow(display,windows->info.id,
   5916                   windows->info.screen);
   5917             }
   5918           else
   5919             if ((x > (int) (windows->info.x+windows->info.width)) ||
   5920                 (y > (int) (windows->info.y+windows->info.height)))
   5921               (void) XMapWindow(display,windows->info.id);
   5922           break;
   5923         }
   5924       }
   5925     } while ((state & ExitState) == 0);
   5926     (void) XSelectInput(display,windows->image.id,
   5927       windows->image.attributes.event_mask);
   5928     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   5929     if ((state & EscapeState) != 0)
   5930       break;
   5931     /*
   5932       Draw element as pointer moves until the button is released.
   5933     */
   5934     distance=0;
   5935     degrees=0.0;
   5936     line_info.x1=x;
   5937     line_info.y1=y;
   5938     line_info.x2=x;
   5939     line_info.y2=y;
   5940     rectangle_info.x=(ssize_t) x;
   5941     rectangle_info.y=(ssize_t) y;
   5942     rectangle_info.width=0;
   5943     rectangle_info.height=0;
   5944     number_coordinates=1;
   5945     coordinate_info->x=x;
   5946     coordinate_info->y=y;
   5947     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
   5948     state=DefaultState;
   5949     do
   5950     {
   5951       switch (element)
   5952       {
   5953         case PointElement:
   5954         default:
   5955         {
   5956           if (number_coordinates > 1)
   5957             {
   5958               (void) XDrawLines(display,windows->image.id,
   5959                 windows->image.highlight_context,coordinate_info,
   5960                 number_coordinates,CoordModeOrigin);
   5961               (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
   5962                 coordinate_info[number_coordinates-1].x,
   5963                 coordinate_info[number_coordinates-1].y);
   5964               XInfoWidget(display,windows,text);
   5965             }
   5966           break;
   5967         }
   5968         case LineElement:
   5969         {
   5970           if (distance > 9)
   5971             {
   5972               /*
   5973                 Display angle of the line.
   5974               */
   5975               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
   5976                 line_info.y1),(double) (line_info.x2-line_info.x1)));
   5977               (void) FormatLocaleString(text,MagickPathExtent," %g",
   5978                 (double) degrees);
   5979               XInfoWidget(display,windows,text);
   5980               XHighlightLine(display,windows->image.id,
   5981                 windows->image.highlight_context,&line_info);
   5982             }
   5983           else
   5984             if (windows->info.mapped != MagickFalse )
   5985               (void) XWithdrawWindow(display,windows->info.id,
   5986                 windows->info.screen);
   5987           break;
   5988         }
   5989         case RectangleElement:
   5990         case FillRectangleElement:
   5991         {
   5992           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
   5993             {
   5994               /*
   5995                 Display info and draw drawing rectangle.
   5996               */
   5997               (void) FormatLocaleString(text,MagickPathExtent,
   5998                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
   5999                 (double) rectangle_info.height,(double) rectangle_info.x,
   6000                 (double) rectangle_info.y);
   6001               XInfoWidget(display,windows,text);
   6002               XHighlightRectangle(display,windows->image.id,
   6003                 windows->image.highlight_context,&rectangle_info);
   6004             }
   6005           else
   6006             if (windows->info.mapped != MagickFalse )
   6007               (void) XWithdrawWindow(display,windows->info.id,
   6008                 windows->info.screen);
   6009           break;
   6010         }
   6011         case CircleElement:
   6012         case FillCircleElement:
   6013         case EllipseElement:
   6014         case FillEllipseElement:
   6015         {
   6016           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
   6017             {
   6018               /*
   6019                 Display info and draw drawing rectangle.
   6020               */
   6021               (void) FormatLocaleString(text,MagickPathExtent,
   6022                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
   6023                 (double) rectangle_info.height,(double) rectangle_info.x,
   6024                 (double) rectangle_info.y);
   6025               XInfoWidget(display,windows,text);
   6026               XHighlightEllipse(display,windows->image.id,
   6027                 windows->image.highlight_context,&rectangle_info);
   6028             }
   6029           else
   6030             if (windows->info.mapped != MagickFalse )
   6031               (void) XWithdrawWindow(display,windows->info.id,
   6032                 windows->info.screen);
   6033           break;
   6034         }
   6035         case PolygonElement:
   6036         case FillPolygonElement:
   6037         {
   6038           if (number_coordinates > 1)
   6039             (void) XDrawLines(display,windows->image.id,
   6040               windows->image.highlight_context,coordinate_info,
   6041               number_coordinates,CoordModeOrigin);
   6042           if (distance > 9)
   6043             {
   6044               /*
   6045                 Display angle of the line.
   6046               */
   6047               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
   6048                 line_info.y1),(double) (line_info.x2-line_info.x1)));
   6049               (void) FormatLocaleString(text,MagickPathExtent," %g",
   6050                 (double) degrees);
   6051               XInfoWidget(display,windows,text);
   6052               XHighlightLine(display,windows->image.id,
   6053                 windows->image.highlight_context,&line_info);
   6054             }
   6055           else
   6056             if (windows->info.mapped != MagickFalse )
   6057               (void) XWithdrawWindow(display,windows->info.id,
   6058                 windows->info.screen);
   6059           break;
   6060         }
   6061       }
   6062       /*
   6063         Wait for next event.
   6064       */
   6065       XScreenEvent(display,windows,&event,exception);
   6066       switch (element)
   6067       {
   6068         case PointElement:
   6069         default:
   6070         {
   6071           if (number_coordinates > 1)
   6072             (void) XDrawLines(display,windows->image.id,
   6073               windows->image.highlight_context,coordinate_info,
   6074               number_coordinates,CoordModeOrigin);
   6075           break;
   6076         }
   6077         case LineElement:
   6078         {
   6079           if (distance > 9)
   6080             XHighlightLine(display,windows->image.id,
   6081               windows->image.highlight_context,&line_info);
   6082           break;
   6083         }
   6084         case RectangleElement:
   6085         case FillRectangleElement:
   6086         {
   6087           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
   6088             XHighlightRectangle(display,windows->image.id,
   6089               windows->image.highlight_context,&rectangle_info);
   6090           break;
   6091         }
   6092         case CircleElement:
   6093         case FillCircleElement:
   6094         case EllipseElement:
   6095         case FillEllipseElement:
   6096         {
   6097           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
   6098             XHighlightEllipse(display,windows->image.id,
   6099               windows->image.highlight_context,&rectangle_info);
   6100           break;
   6101         }
   6102         case PolygonElement:
   6103         case FillPolygonElement:
   6104         {
   6105           if (number_coordinates > 1)
   6106             (void) XDrawLines(display,windows->image.id,
   6107               windows->image.highlight_context,coordinate_info,
   6108               number_coordinates,CoordModeOrigin);
   6109           if (distance > 9)
   6110             XHighlightLine(display,windows->image.id,
   6111               windows->image.highlight_context,&line_info);
   6112           break;
   6113         }
   6114       }
   6115       switch (event.type)
   6116       {
   6117         case ButtonPress:
   6118           break;
   6119         case ButtonRelease:
   6120         {
   6121           /*
   6122             User has committed to element.
   6123           */
   6124           line_info.x2=event.xbutton.x;
   6125           line_info.y2=event.xbutton.y;
   6126           rectangle_info.x=(ssize_t) event.xbutton.x;
   6127           rectangle_info.y=(ssize_t) event.xbutton.y;
   6128           coordinate_info[number_coordinates].x=event.xbutton.x;
   6129           coordinate_info[number_coordinates].y=event.xbutton.y;
   6130           if (((element != PolygonElement) &&
   6131                (element != FillPolygonElement)) || (distance <= 9))
   6132             {
   6133               state|=ExitState;
   6134               break;
   6135             }
   6136           number_coordinates++;
   6137           if (number_coordinates < (int) max_coordinates)
   6138             {
   6139               line_info.x1=event.xbutton.x;
   6140               line_info.y1=event.xbutton.y;
   6141               break;
   6142             }
   6143           max_coordinates<<=1;
   6144           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
   6145             max_coordinates,sizeof(*coordinate_info));
   6146           if (coordinate_info == (XPoint *) NULL)
   6147             (void) ThrowMagickException(exception,GetMagickModule(),
   6148               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
   6149           break;
   6150         }
   6151         case Expose:
   6152           break;
   6153         case MotionNotify:
   6154         {
   6155           if (event.xmotion.window != windows->image.id)
   6156             break;
   6157           if (element != PointElement)
   6158             {
   6159               line_info.x2=event.xmotion.x;
   6160               line_info.y2=event.xmotion.y;
   6161               rectangle_info.x=(ssize_t) event.xmotion.x;
   6162               rectangle_info.y=(ssize_t) event.xmotion.y;
   6163               break;
   6164             }
   6165           coordinate_info[number_coordinates].x=event.xbutton.x;
   6166           coordinate_info[number_coordinates].y=event.xbutton.y;
   6167           number_coordinates++;
   6168           if (number_coordinates < (int) max_coordinates)
   6169             break;
   6170           max_coordinates<<=1;
   6171           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
   6172             max_coordinates,sizeof(*coordinate_info));
   6173           if (coordinate_info == (XPoint *) NULL)
   6174             (void) ThrowMagickException(exception,GetMagickModule(),
   6175               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
   6176           break;
   6177         }
   6178         default:
   6179           break;
   6180       }
   6181       /*
   6182         Check boundary conditions.
   6183       */
   6184       if (line_info.x2 < 0)
   6185         line_info.x2=0;
   6186       else
   6187         if (line_info.x2 > (int) windows->image.width)
   6188           line_info.x2=(short) windows->image.width;
   6189       if (line_info.y2 < 0)
   6190         line_info.y2=0;
   6191       else
   6192         if (line_info.y2 > (int) windows->image.height)
   6193           line_info.y2=(short) windows->image.height;
   6194       distance=(unsigned int)
   6195         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
   6196          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
   6197       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
   6198           ((state & ExitState) != 0))
   6199         {
   6200           if (rectangle_info.x < 0)
   6201             rectangle_info.x=0;
   6202           else
   6203             if (rectangle_info.x > (ssize_t) windows->image.width)
   6204               rectangle_info.x=(ssize_t) windows->image.width;
   6205           if ((int) rectangle_info.x < x)
   6206             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
   6207           else
   6208             {
   6209               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
   6210               rectangle_info.x=(ssize_t) x;
   6211             }
   6212           if (rectangle_info.y < 0)
   6213             rectangle_info.y=0;
   6214           else
   6215             if (rectangle_info.y > (ssize_t) windows->image.height)
   6216               rectangle_info.y=(ssize_t) windows->image.height;
   6217           if ((int) rectangle_info.y < y)
   6218             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
   6219           else
   6220             {
   6221               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
   6222               rectangle_info.y=(ssize_t) y;
   6223             }
   6224         }
   6225     } while ((state & ExitState) == 0);
   6226     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
   6227     if ((element == PointElement) || (element == PolygonElement) ||
   6228         (element == FillPolygonElement))
   6229       {
   6230         /*
   6231           Determine polygon bounding box.
   6232         */
   6233         rectangle_info.x=(ssize_t) coordinate_info->x;
   6234         rectangle_info.y=(ssize_t) coordinate_info->y;
   6235         x=coordinate_info->x;
   6236         y=coordinate_info->y;
   6237         for (i=1; i < number_coordinates; i++)
   6238         {
   6239           if (coordinate_info[i].x > x)
   6240             x=coordinate_info[i].x;
   6241           if (coordinate_info[i].y > y)
   6242             y=coordinate_info[i].y;
   6243           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
   6244             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
   6245           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
   6246             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
   6247         }
   6248         rectangle_info.width=(size_t) (x-rectangle_info.x);
   6249         rectangle_info.height=(size_t) (y-rectangle_info.y);
   6250         for (i=0; i < number_coordinates; i++)
   6251         {
   6252           coordinate_info[i].x-=rectangle_info.x;
   6253           coordinate_info[i].y-=rectangle_info.y;
   6254         }
   6255       }
   6256     else
   6257       if (distance <= 9)
   6258         continue;
   6259       else
   6260         if ((element == RectangleElement) ||
   6261             (element == CircleElement) || (element == EllipseElement))
   6262           {
   6263             rectangle_info.width--;
   6264             rectangle_info.height--;
   6265           }
   6266     /*
   6267       Drawing is relative to image configuration.
   6268     */
   6269     draw_info.x=(int) rectangle_info.x;
   6270     draw_info.y=(int) rectangle_info.y;
   6271     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
   6272       image,exception);
   6273     width=(unsigned int) (*image)->columns;
   6274     height=(unsigned int) (*image)->rows;
   6275     x=0;
   6276     y=0;
   6277     if (windows->image.crop_geometry != (char *) NULL)
   6278       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
   6279     draw_info.x+=windows->image.x-(line_width/2);
   6280     if (draw_info.x < 0)
   6281       draw_info.x=0;
   6282     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
   6283     draw_info.y+=windows->image.y-(line_width/2);
   6284     if (draw_info.y < 0)
   6285       draw_info.y=0;
   6286     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
   6287     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
   6288     if (draw_info.width > (unsigned int) (*image)->columns)
   6289       draw_info.width=(unsigned int) (*image)->columns;
   6290     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
   6291     if (draw_info.height > (unsigned int) (*image)->rows)
   6292       draw_info.height=(unsigned int) (*image)->rows;
   6293     (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
   6294       width*draw_info.width/windows->image.ximage->width,
   6295       height*draw_info.height/windows->image.ximage->height,
   6296       draw_info.x+x,draw_info.y+y);
   6297     /*
   6298       Initialize drawing attributes.
   6299     */
   6300     draw_info.degrees=0.0;
   6301     draw_info.element=element;
   6302     draw_info.stipple=stipple;
   6303     draw_info.line_width=line_width;
   6304     draw_info.line_info=line_info;
   6305     if (line_info.x1 > (int) (line_width/2))
   6306       draw_info.line_info.x1=(short) line_width/2;
   6307     if (line_info.y1 > (int) (line_width/2))
   6308       draw_info.line_info.y1=(short) line_width/2;
   6309     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
   6310     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
   6311     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
   6312       {
   6313         draw_info.line_info.x2=(-draw_info.line_info.x2);
   6314         draw_info.line_info.y2=(-draw_info.line_info.y2);
   6315       }
   6316     if (draw_info.line_info.x2 < 0)
   6317       {
   6318         draw_info.line_info.x2=(-draw_info.line_info.x2);
   6319         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
   6320       }
   6321     if (draw_info.line_info.y2 < 0)
   6322       {
   6323         draw_info.line_info.y2=(-draw_info.line_info.y2);
   6324         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
   6325       }
   6326     draw_info.rectangle_info=rectangle_info;
   6327     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
   6328       draw_info.rectangle_info.x=(ssize_t) line_width/2;
   6329     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
   6330       draw_info.rectangle_info.y=(ssize_t) line_width/2;
   6331     draw_info.number_coordinates=(unsigned int) number_coordinates;
   6332     draw_info.coordinate_info=coordinate_info;
   6333     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
   6334     /*
   6335       Draw element on image.
   6336     */
   6337     XSetCursorState(display,windows,MagickTrue);
   6338     XCheckRefreshWindows(display,windows);
   6339     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
   6340     XSetCursorState(display,windows,MagickFalse);
   6341     /*
   6342       Update image colormap and return to image drawing.
   6343     */
   6344     XConfigureImageColormap(display,resource_info,windows,*image,exception);
   6345     (void) XConfigureImage(display,resource_info,windows,*image,exception);
   6346   }
   6347   XSetCursorState(display,windows,MagickFalse);
   6348   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
   6349   return(status != 0 ? MagickTrue : MagickFalse);
   6350 }
   6351 
   6352 /*
   6354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   6355 %                                                                             %
   6356 %                                                                             %
   6357 %                                                                             %
   6358 +   X D r a w P a n R e c t a n g l e                                         %
   6359 %                                                                             %
   6360 %                                                                             %
   6361 %                                                                             %
   6362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   6363 %
   6364 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
   6365 %  displays a zoom image and the rectangle shows which portion of the image is
   6366 %  displayed in the Image window.
   6367 %
   6368 %  The format of the XDrawPanRectangle method is:
   6369 %
   6370 %      XDrawPanRectangle(Display *display,XWindows *windows)
   6371 %
   6372 %  A description of each parameter follows:
   6373 %
   6374 %    o display: Specifies a connection to an X server;  returned from
   6375 %      XOpenDisplay.
   6376 %
   6377 %    o windows: Specifies a pointer to a XWindows structure.
   6378 %
   6379 */
   6380 static void XDrawPanRectangle(Display *display,XWindows *windows)
   6381 {
   6382   double
   6383     scale_factor;
   6384 
   6385   RectangleInfo
   6386     highlight_info;
   6387 
   6388   /*
   6389     Determine dimensions of the panning rectangle.
   6390   */
   6391   scale_factor=(double) windows->pan.width/windows->image.ximage->width;
   6392   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
   6393   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
   6394   scale_factor=(double)
   6395     windows->pan.height/windows->image.ximage->height;
   6396   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
   6397   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
   6398   /*
   6399     Display the panning rectangle.
   6400   */
   6401   (void) XClearWindow(display,windows->pan.id);
   6402   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
   6403     &highlight_info);
   6404 }
   6405 
   6406 /*
   6408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   6409 %                                                                             %
   6410 %                                                                             %
   6411 %                                                                             %
   6412 +   X I m a g e C a c h e                                                     %
   6413 %                                                                             %
   6414 %                                                                             %
   6415 %                                                                             %
   6416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   6417 %
   6418 %  XImageCache() handles the creation, manipulation, and destruction of the
   6419 %  image cache (undo and redo buffers).
   6420 %
   6421 %  The format of the XImageCache method is:
   6422 %
   6423 %      void XImageCache(Display *display,XResourceInfo *resource_info,
   6424 %        XWindows *windows,const CommandType command,Image **image,
   6425 %        ExceptionInfo *exception)
   6426 %
   6427 %  A description of each parameter follows:
   6428 %
   6429 %    o display: Specifies a connection to an X server; returned from
   6430 %      XOpenDisplay.
   6431 %
   6432 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   6433 %
   6434 %    o windows: Specifies a pointer to a XWindows structure.
   6435 %
   6436 %    o command: Specifies a command to perform.
   6437 %
   6438 %    o image: the image;  XImageCache may transform the image and return a new
   6439 %      image pointer.
   6440 %
   6441 %    o exception: return any errors or warnings in this structure.
   6442 %
   6443 */
   6444 static void XImageCache(Display *display,XResourceInfo *resource_info,
   6445   XWindows *windows,const CommandType command,Image **image,
   6446   ExceptionInfo *exception)
   6447 {
   6448   Image
   6449     *cache_image;
   6450 
   6451   static Image
   6452     *redo_image = (Image *) NULL,
   6453     *undo_image = (Image *) NULL;
   6454 
   6455   switch (command)
   6456   {
   6457     case FreeBuffersCommand:
   6458     {
   6459       /*
   6460         Free memory from the undo and redo cache.
   6461       */
   6462       while (undo_image != (Image *) NULL)
   6463       {
   6464         cache_image=undo_image;
   6465         undo_image=GetPreviousImageInList(undo_image);
   6466         cache_image->list=DestroyImage(cache_image->list);
   6467         cache_image=DestroyImage(cache_image);
   6468       }
   6469       undo_image=NewImageList();
   6470       if (redo_image != (Image *) NULL)
   6471         redo_image=DestroyImage(redo_image);
   6472       redo_image=NewImageList();
   6473       return;
   6474     }
   6475     case UndoCommand:
   6476     {
   6477       char
   6478         image_geometry[MagickPathExtent];
   6479 
   6480       /*
   6481         Undo the last image transformation.
   6482       */
   6483       if (undo_image == (Image *) NULL)
   6484         {
   6485           (void) XBell(display,0);
   6486           return;
   6487         }
   6488       cache_image=undo_image;
   6489       undo_image=GetPreviousImageInList(undo_image);
   6490       windows->image.window_changes.width=(int) cache_image->columns;
   6491       windows->image.window_changes.height=(int) cache_image->rows;
   6492       (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
   6493         windows->image.ximage->width,windows->image.ximage->height);
   6494       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
   6495         exception);
   6496       if (windows->image.crop_geometry != (char *) NULL)
   6497         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
   6498           windows->image.crop_geometry);
   6499       windows->image.crop_geometry=cache_image->geometry;
   6500       if (redo_image != (Image *) NULL)
   6501         redo_image=DestroyImage(redo_image);
   6502       redo_image=(*image);
   6503       *image=cache_image->list;
   6504       cache_image=DestroyImage(cache_image);
   6505       if (windows->image.orphan != MagickFalse )
   6506         return;
   6507       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   6508       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   6509       return;
   6510     }
   6511     case CutCommand:
   6512     case PasteCommand:
   6513     case ApplyCommand:
   6514     case HalfSizeCommand:
   6515     case OriginalSizeCommand:
   6516     case DoubleSizeCommand:
   6517     case ResizeCommand:
   6518     case TrimCommand:
   6519     case CropCommand:
   6520     case ChopCommand:
   6521     case FlipCommand:
   6522     case FlopCommand:
   6523     case RotateRightCommand:
   6524     case RotateLeftCommand:
   6525     case RotateCommand:
   6526     case ShearCommand:
   6527     case RollCommand:
   6528     case NegateCommand:
   6529     case ContrastStretchCommand:
   6530     case SigmoidalContrastCommand:
   6531     case NormalizeCommand:
   6532     case EqualizeCommand:
   6533     case HueCommand:
   6534     case SaturationCommand:
   6535     case BrightnessCommand:
   6536     case GammaCommand:
   6537     case SpiffCommand:
   6538     case DullCommand:
   6539     case GrayscaleCommand:
   6540     case MapCommand:
   6541     case QuantizeCommand:
   6542     case DespeckleCommand:
   6543     case EmbossCommand:
   6544     case ReduceNoiseCommand:
   6545     case AddNoiseCommand:
   6546     case SharpenCommand:
   6547     case BlurCommand:
   6548     case ThresholdCommand:
   6549     case EdgeDetectCommand:
   6550     case SpreadCommand:
   6551     case ShadeCommand:
   6552     case RaiseCommand:
   6553     case SegmentCommand:
   6554     case SolarizeCommand:
   6555     case SepiaToneCommand:
   6556     case SwirlCommand:
   6557     case ImplodeCommand:
   6558     case VignetteCommand:
   6559     case WaveCommand:
   6560     case OilPaintCommand:
   6561     case CharcoalDrawCommand:
   6562     case AnnotateCommand:
   6563     case AddBorderCommand:
   6564     case AddFrameCommand:
   6565     case CompositeCommand:
   6566     case CommentCommand:
   6567     case LaunchCommand:
   6568     case RegionofInterestCommand:
   6569     case SaveToUndoBufferCommand:
   6570     case RedoCommand:
   6571     {
   6572       Image
   6573         *previous_image;
   6574 
   6575       ssize_t
   6576         bytes;
   6577 
   6578       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
   6579       if (undo_image != (Image *) NULL)
   6580         {
   6581           /*
   6582             Ensure the undo cache has enough memory available.
   6583           */
   6584           previous_image=undo_image;
   6585           while (previous_image != (Image *) NULL)
   6586           {
   6587             bytes+=previous_image->list->columns*previous_image->list->rows*
   6588               sizeof(PixelInfo);
   6589             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
   6590               {
   6591                 previous_image=GetPreviousImageInList(previous_image);
   6592                 continue;
   6593               }
   6594             bytes-=previous_image->list->columns*previous_image->list->rows*
   6595               sizeof(PixelInfo);
   6596             if (previous_image == undo_image)
   6597               undo_image=NewImageList();
   6598             else
   6599               previous_image->next->previous=NewImageList();
   6600             break;
   6601           }
   6602           while (previous_image != (Image *) NULL)
   6603           {
   6604             /*
   6605               Delete any excess memory from undo cache.
   6606             */
   6607             cache_image=previous_image;
   6608             previous_image=GetPreviousImageInList(previous_image);
   6609             cache_image->list=DestroyImage(cache_image->list);
   6610             cache_image=DestroyImage(cache_image);
   6611           }
   6612         }
   6613       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
   6614         break;
   6615       /*
   6616         Save image before transformations are applied.
   6617       */
   6618       cache_image=AcquireImage((ImageInfo *) NULL,exception);
   6619       if (cache_image == (Image *) NULL)
   6620         break;
   6621       XSetCursorState(display,windows,MagickTrue);
   6622       XCheckRefreshWindows(display,windows);
   6623       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
   6624       XSetCursorState(display,windows,MagickFalse);
   6625       if (cache_image->list == (Image *) NULL)
   6626         {
   6627           cache_image=DestroyImage(cache_image);
   6628           break;
   6629         }
   6630       cache_image->columns=(size_t) windows->image.ximage->width;
   6631       cache_image->rows=(size_t) windows->image.ximage->height;
   6632       cache_image->geometry=windows->image.crop_geometry;
   6633       if (windows->image.crop_geometry != (char *) NULL)
   6634         {
   6635           cache_image->geometry=AcquireString((char *) NULL);
   6636           (void) CopyMagickString(cache_image->geometry,
   6637             windows->image.crop_geometry,MagickPathExtent);
   6638         }
   6639       if (undo_image == (Image *) NULL)
   6640         {
   6641           undo_image=cache_image;
   6642           break;
   6643         }
   6644       undo_image->next=cache_image;
   6645       undo_image->next->previous=undo_image;
   6646       undo_image=undo_image->next;
   6647       break;
   6648     }
   6649     default:
   6650       break;
   6651   }
   6652   if (command == RedoCommand)
   6653     {
   6654       /*
   6655         Redo the last image transformation.
   6656       */
   6657       if (redo_image == (Image *) NULL)
   6658         {
   6659           (void) XBell(display,0);
   6660           return;
   6661         }
   6662       windows->image.window_changes.width=(int) redo_image->columns;
   6663       windows->image.window_changes.height=(int) redo_image->rows;
   6664       if (windows->image.crop_geometry != (char *) NULL)
   6665         windows->image.crop_geometry=(char *)
   6666           RelinquishMagickMemory(windows->image.crop_geometry);
   6667       windows->image.crop_geometry=redo_image->geometry;
   6668       *image=DestroyImage(*image);
   6669       *image=redo_image;
   6670       redo_image=NewImageList();
   6671       if (windows->image.orphan != MagickFalse )
   6672         return;
   6673       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   6674       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   6675       return;
   6676     }
   6677   if (command != InfoCommand)
   6678     return;
   6679   /*
   6680     Display image info.
   6681   */
   6682   XSetCursorState(display,windows,MagickTrue);
   6683   XCheckRefreshWindows(display,windows);
   6684   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
   6685   XSetCursorState(display,windows,MagickFalse);
   6686 }
   6687 
   6688 /*
   6690 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   6691 %                                                                             %
   6692 %                                                                             %
   6693 %                                                                             %
   6694 +   X I m a g e W i n d o w C o m m a n d                                     %
   6695 %                                                                             %
   6696 %                                                                             %
   6697 %                                                                             %
   6698 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   6699 %
   6700 %  XImageWindowCommand() makes a transform to the image or Image window as
   6701 %  specified by a user menu button or keyboard command.
   6702 %
   6703 %  The format of the XImageWindowCommand method is:
   6704 %
   6705 %      CommandType XImageWindowCommand(Display *display,
   6706 %        XResourceInfo *resource_info,XWindows *windows,
   6707 %        const MagickStatusType state,KeySym key_symbol,Image **image,
   6708 %        ExceptionInfo *exception)
   6709 %
   6710 %  A description of each parameter follows:
   6711 %
   6712 %    o nexus:  Method XImageWindowCommand returns an image when the
   6713 %      user chooses 'Open Image' from the command menu.  Otherwise a null
   6714 %      image is returned.
   6715 %
   6716 %    o display: Specifies a connection to an X server; returned from
   6717 %      XOpenDisplay.
   6718 %
   6719 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   6720 %
   6721 %    o windows: Specifies a pointer to a XWindows structure.
   6722 %
   6723 %    o state: key mask.
   6724 %
   6725 %    o key_symbol: Specifies a command to perform.
   6726 %
   6727 %    o image: the image;  XImageWIndowCommand may transform the image and
   6728 %      return a new image pointer.
   6729 %
   6730 %    o exception: return any errors or warnings in this structure.
   6731 %
   6732 */
   6733 static CommandType XImageWindowCommand(Display *display,
   6734   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
   6735   KeySym key_symbol,Image **image,ExceptionInfo *exception)
   6736 {
   6737   static char
   6738     delta[MagickPathExtent] = "";
   6739 
   6740   static const char
   6741     Digits[] = "01234567890";
   6742 
   6743   static KeySym
   6744     last_symbol = XK_0;
   6745 
   6746   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
   6747     {
   6748       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
   6749         {
   6750           *delta='\0';
   6751           resource_info->quantum=1;
   6752         }
   6753       last_symbol=key_symbol;
   6754       delta[strlen(delta)+1]='\0';
   6755       delta[strlen(delta)]=Digits[key_symbol-XK_0];
   6756       resource_info->quantum=StringToLong(delta);
   6757       return(NullCommand);
   6758     }
   6759   last_symbol=key_symbol;
   6760   if (resource_info->immutable)
   6761     {
   6762       /*
   6763         Virtual image window has a restricted command set.
   6764       */
   6765       switch (key_symbol)
   6766       {
   6767         case XK_question:
   6768           return(InfoCommand);
   6769         case XK_p:
   6770         case XK_Print:
   6771           return(PrintCommand);
   6772         case XK_space:
   6773           return(NextCommand);
   6774         case XK_q:
   6775         case XK_Escape:
   6776           return(QuitCommand);
   6777         default:
   6778           break;
   6779       }
   6780       return(NullCommand);
   6781     }
   6782   switch ((int) key_symbol)
   6783   {
   6784     case XK_o:
   6785     {
   6786       if ((state & ControlMask) == 0)
   6787         break;
   6788       return(OpenCommand);
   6789     }
   6790     case XK_space:
   6791       return(NextCommand);
   6792     case XK_BackSpace:
   6793       return(FormerCommand);
   6794     case XK_s:
   6795     {
   6796       if ((state & Mod1Mask) != 0)
   6797         return(SwirlCommand);
   6798       if ((state & ControlMask) == 0)
   6799         return(ShearCommand);
   6800       return(SaveCommand);
   6801     }
   6802     case XK_p:
   6803     case XK_Print:
   6804     {
   6805       if ((state & Mod1Mask) != 0)
   6806         return(OilPaintCommand);
   6807       if ((state & Mod4Mask) != 0)
   6808         return(ColorCommand);
   6809       if ((state & ControlMask) == 0)
   6810         return(NullCommand);
   6811       return(PrintCommand);
   6812     }
   6813     case XK_d:
   6814     {
   6815       if ((state & Mod4Mask) != 0)
   6816         return(DrawCommand);
   6817       if ((state & ControlMask) == 0)
   6818         return(NullCommand);
   6819       return(DeleteCommand);
   6820     }
   6821     case XK_Select:
   6822     {
   6823       if ((state & ControlMask) == 0)
   6824         return(NullCommand);
   6825       return(SelectCommand);
   6826     }
   6827     case XK_n:
   6828     {
   6829       if ((state & ControlMask) == 0)
   6830         return(NullCommand);
   6831       return(NewCommand);
   6832     }
   6833     case XK_q:
   6834     case XK_Escape:
   6835       return(QuitCommand);
   6836     case XK_z:
   6837     case XK_Undo:
   6838     {
   6839       if ((state & ControlMask) == 0)
   6840         return(NullCommand);
   6841       return(UndoCommand);
   6842     }
   6843     case XK_r:
   6844     case XK_Redo:
   6845     {
   6846       if ((state & ControlMask) == 0)
   6847         return(RollCommand);
   6848       return(RedoCommand);
   6849     }
   6850     case XK_x:
   6851     {
   6852       if ((state & ControlMask) == 0)
   6853         return(NullCommand);
   6854       return(CutCommand);
   6855     }
   6856     case XK_c:
   6857     {
   6858       if ((state & Mod1Mask) != 0)
   6859         return(CharcoalDrawCommand);
   6860       if ((state & ControlMask) == 0)
   6861         return(CropCommand);
   6862       return(CopyCommand);
   6863     }
   6864     case XK_v:
   6865     case XK_Insert:
   6866     {
   6867       if ((state & Mod4Mask) != 0)
   6868         return(CompositeCommand);
   6869       if ((state & ControlMask) == 0)
   6870         return(FlipCommand);
   6871       return(PasteCommand);
   6872     }
   6873     case XK_less:
   6874       return(HalfSizeCommand);
   6875     case XK_minus:
   6876       return(OriginalSizeCommand);
   6877     case XK_greater:
   6878       return(DoubleSizeCommand);
   6879     case XK_percent:
   6880       return(ResizeCommand);
   6881     case XK_at:
   6882       return(RefreshCommand);
   6883     case XK_bracketleft:
   6884       return(ChopCommand);
   6885     case XK_h:
   6886       return(FlopCommand);
   6887     case XK_slash:
   6888       return(RotateRightCommand);
   6889     case XK_backslash:
   6890       return(RotateLeftCommand);
   6891     case XK_asterisk:
   6892       return(RotateCommand);
   6893     case XK_t:
   6894       return(TrimCommand);
   6895     case XK_H:
   6896       return(HueCommand);
   6897     case XK_S:
   6898       return(SaturationCommand);
   6899     case XK_L:
   6900       return(BrightnessCommand);
   6901     case XK_G:
   6902       return(GammaCommand);
   6903     case XK_C:
   6904       return(SpiffCommand);
   6905     case XK_Z:
   6906       return(DullCommand);
   6907     case XK_N:
   6908       return(NormalizeCommand);
   6909     case XK_equal:
   6910       return(EqualizeCommand);
   6911     case XK_asciitilde:
   6912       return(NegateCommand);
   6913     case XK_period:
   6914       return(GrayscaleCommand);
   6915     case XK_numbersign:
   6916       return(QuantizeCommand);
   6917     case XK_F2:
   6918       return(DespeckleCommand);
   6919     case XK_F3:
   6920       return(EmbossCommand);
   6921     case XK_F4:
   6922       return(ReduceNoiseCommand);
   6923     case XK_F5:
   6924       return(AddNoiseCommand);
   6925     case XK_F6:
   6926       return(SharpenCommand);
   6927     case XK_F7:
   6928       return(BlurCommand);
   6929     case XK_F8:
   6930       return(ThresholdCommand);
   6931     case XK_F9:
   6932       return(EdgeDetectCommand);
   6933     case XK_F10:
   6934       return(SpreadCommand);
   6935     case XK_F11:
   6936       return(ShadeCommand);
   6937     case XK_F12:
   6938       return(RaiseCommand);
   6939     case XK_F13:
   6940       return(SegmentCommand);
   6941     case XK_i:
   6942     {
   6943       if ((state & Mod1Mask) == 0)
   6944         return(NullCommand);
   6945       return(ImplodeCommand);
   6946     }
   6947     case XK_w:
   6948     {
   6949       if ((state & Mod1Mask) == 0)
   6950         return(NullCommand);
   6951       return(WaveCommand);
   6952     }
   6953     case XK_m:
   6954     {
   6955       if ((state & Mod4Mask) == 0)
   6956         return(NullCommand);
   6957       return(MatteCommand);
   6958     }
   6959     case XK_b:
   6960     {
   6961       if ((state & Mod4Mask) == 0)
   6962         return(NullCommand);
   6963       return(AddBorderCommand);
   6964     }
   6965     case XK_f:
   6966     {
   6967       if ((state & Mod4Mask) == 0)
   6968         return(NullCommand);
   6969       return(AddFrameCommand);
   6970     }
   6971     case XK_exclam:
   6972     {
   6973       if ((state & Mod4Mask) == 0)
   6974         return(NullCommand);
   6975       return(CommentCommand);
   6976     }
   6977     case XK_a:
   6978     {
   6979       if ((state & Mod1Mask) != 0)
   6980         return(ApplyCommand);
   6981       if ((state & Mod4Mask) != 0)
   6982         return(AnnotateCommand);
   6983       if ((state & ControlMask) == 0)
   6984         return(NullCommand);
   6985       return(RegionofInterestCommand);
   6986     }
   6987     case XK_question:
   6988       return(InfoCommand);
   6989     case XK_plus:
   6990       return(ZoomCommand);
   6991     case XK_P:
   6992     {
   6993       if ((state & ShiftMask) == 0)
   6994         return(NullCommand);
   6995       return(ShowPreviewCommand);
   6996     }
   6997     case XK_Execute:
   6998       return(LaunchCommand);
   6999     case XK_F1:
   7000       return(HelpCommand);
   7001     case XK_Find:
   7002       return(BrowseDocumentationCommand);
   7003     case XK_Menu:
   7004     {
   7005       (void) XMapRaised(display,windows->command.id);
   7006       return(NullCommand);
   7007     }
   7008     case XK_Next:
   7009     case XK_Prior:
   7010     case XK_Home:
   7011     case XK_KP_Home:
   7012     {
   7013       XTranslateImage(display,windows,*image,key_symbol);
   7014       return(NullCommand);
   7015     }
   7016     case XK_Up:
   7017     case XK_KP_Up:
   7018     case XK_Down:
   7019     case XK_KP_Down:
   7020     case XK_Left:
   7021     case XK_KP_Left:
   7022     case XK_Right:
   7023     case XK_KP_Right:
   7024     {
   7025       if ((state & Mod1Mask) != 0)
   7026         {
   7027           RectangleInfo
   7028             crop_info;
   7029 
   7030           /*
   7031             Trim one pixel from edge of image.
   7032           */
   7033           crop_info.x=0;
   7034           crop_info.y=0;
   7035           crop_info.width=(size_t) windows->image.ximage->width;
   7036           crop_info.height=(size_t) windows->image.ximage->height;
   7037           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
   7038             {
   7039               if (resource_info->quantum >= (int) crop_info.height)
   7040                 resource_info->quantum=(int) crop_info.height-1;
   7041               crop_info.height-=resource_info->quantum;
   7042             }
   7043           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
   7044             {
   7045               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
   7046                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
   7047               crop_info.y+=resource_info->quantum;
   7048               crop_info.height-=resource_info->quantum;
   7049             }
   7050           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
   7051             {
   7052               if (resource_info->quantum >= (int) crop_info.width)
   7053                 resource_info->quantum=(int) crop_info.width-1;
   7054               crop_info.width-=resource_info->quantum;
   7055             }
   7056           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
   7057             {
   7058               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
   7059                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
   7060               crop_info.x+=resource_info->quantum;
   7061               crop_info.width-=resource_info->quantum;
   7062             }
   7063           if ((int) (windows->image.x+windows->image.width) >
   7064               (int) crop_info.width)
   7065             windows->image.x=(int) (crop_info.width-windows->image.width);
   7066           if ((int) (windows->image.y+windows->image.height) >
   7067               (int) crop_info.height)
   7068             windows->image.y=(int) (crop_info.height-windows->image.height);
   7069           XSetCropGeometry(display,windows,&crop_info,*image);
   7070           windows->image.window_changes.width=(int) crop_info.width;
   7071           windows->image.window_changes.height=(int) crop_info.height;
   7072           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
   7073           (void) XConfigureImage(display,resource_info,windows,*image,
   7074             exception);
   7075           return(NullCommand);
   7076         }
   7077       XTranslateImage(display,windows,*image,key_symbol);
   7078       return(NullCommand);
   7079     }
   7080     default:
   7081       return(NullCommand);
   7082   }
   7083   return(NullCommand);
   7084 }
   7085 
   7086 /*
   7088 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   7089 %                                                                             %
   7090 %                                                                             %
   7091 %                                                                             %
   7092 +   X M a g i c k C o m m a n d                                               %
   7093 %                                                                             %
   7094 %                                                                             %
   7095 %                                                                             %
   7096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   7097 %
   7098 %  XMagickCommand() makes a transform to the image or Image window as
   7099 %  specified by a user menu button or keyboard command.
   7100 %
   7101 %  The format of the XMagickCommand method is:
   7102 %
   7103 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
   7104 %        XWindows *windows,const CommandType command,Image **image,
   7105 %        ExceptionInfo *exception)
   7106 %
   7107 %  A description of each parameter follows:
   7108 %
   7109 %    o display: Specifies a connection to an X server; returned from
   7110 %      XOpenDisplay.
   7111 %
   7112 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   7113 %
   7114 %    o windows: Specifies a pointer to a XWindows structure.
   7115 %
   7116 %    o command: Specifies a command to perform.
   7117 %
   7118 %    o image: the image;  XMagickCommand may transform the image and return a
   7119 %      new image pointer.
   7120 %
   7121 %    o exception: return any errors or warnings in this structure.
   7122 %
   7123 */
   7124 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
   7125   XWindows *windows,const CommandType command,Image **image,
   7126   ExceptionInfo *exception)
   7127 {
   7128   char
   7129     filename[MagickPathExtent],
   7130     geometry[MagickPathExtent],
   7131     modulate_factors[MagickPathExtent];
   7132 
   7133   GeometryInfo
   7134     geometry_info;
   7135 
   7136   Image
   7137     *nexus;
   7138 
   7139   ImageInfo
   7140     *image_info;
   7141 
   7142   int
   7143     x,
   7144     y;
   7145 
   7146   MagickStatusType
   7147     flags,
   7148     status;
   7149 
   7150   QuantizeInfo
   7151     quantize_info;
   7152 
   7153   RectangleInfo
   7154     page_geometry;
   7155 
   7156   register int
   7157     i;
   7158 
   7159   static char
   7160     color[MagickPathExtent] = "gray";
   7161 
   7162   unsigned int
   7163     height,
   7164     width;
   7165 
   7166   /*
   7167     Process user command.
   7168   */
   7169   XCheckRefreshWindows(display,windows);
   7170   XImageCache(display,resource_info,windows,command,image,exception);
   7171   nexus=NewImageList();
   7172   windows->image.window_changes.width=windows->image.ximage->width;
   7173   windows->image.window_changes.height=windows->image.ximage->height;
   7174   image_info=CloneImageInfo(resource_info->image_info);
   7175   SetGeometryInfo(&geometry_info);
   7176   GetQuantizeInfo(&quantize_info);
   7177   switch (command)
   7178   {
   7179     case OpenCommand:
   7180     {
   7181       /*
   7182         Load image.
   7183       */
   7184       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
   7185       break;
   7186     }
   7187     case NextCommand:
   7188     {
   7189       /*
   7190         Display next image.
   7191       */
   7192       for (i=0; i < resource_info->quantum; i++)
   7193         XClientMessage(display,windows->image.id,windows->im_protocols,
   7194           windows->im_next_image,CurrentTime);
   7195       break;
   7196     }
   7197     case FormerCommand:
   7198     {
   7199       /*
   7200         Display former image.
   7201       */
   7202       for (i=0; i < resource_info->quantum; i++)
   7203         XClientMessage(display,windows->image.id,windows->im_protocols,
   7204           windows->im_former_image,CurrentTime);
   7205       break;
   7206     }
   7207     case SelectCommand:
   7208     {
   7209       int
   7210         status;
   7211 
   7212       /*
   7213         Select image.
   7214       */
   7215       if (*resource_info->home_directory == '\0')
   7216         (void) CopyMagickString(resource_info->home_directory,".",
   7217           MagickPathExtent);
   7218       status=chdir(resource_info->home_directory);
   7219       if (status == -1)
   7220         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
   7221           "UnableToOpenFile","%s",resource_info->home_directory);
   7222       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
   7223       break;
   7224     }
   7225     case SaveCommand:
   7226     {
   7227       /*
   7228         Save image.
   7229       */
   7230       status=XSaveImage(display,resource_info,windows,*image,exception);
   7231       if (status == MagickFalse)
   7232         {
   7233           char
   7234             message[MagickPathExtent];
   7235 
   7236           (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
   7237             exception->reason != (char *) NULL ? exception->reason : "",
   7238             exception->description != (char *) NULL ? exception->description :
   7239             "");
   7240           XNoticeWidget(display,windows,"Unable to save file:",message);
   7241           break;
   7242         }
   7243       break;
   7244     }
   7245     case PrintCommand:
   7246     {
   7247       /*
   7248         Print image.
   7249       */
   7250       status=XPrintImage(display,resource_info,windows,*image,exception);
   7251       if (status == MagickFalse)
   7252         {
   7253           char
   7254             message[MagickPathExtent];
   7255 
   7256           (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
   7257             exception->reason != (char *) NULL ? exception->reason : "",
   7258             exception->description != (char *) NULL ? exception->description :
   7259             "");
   7260           XNoticeWidget(display,windows,"Unable to print file:",message);
   7261           break;
   7262         }
   7263       break;
   7264     }
   7265     case DeleteCommand:
   7266     {
   7267       static char
   7268         filename[MagickPathExtent] = "\0";
   7269 
   7270       /*
   7271         Delete image file.
   7272       */
   7273       XFileBrowserWidget(display,windows,"Delete",filename);
   7274       if (*filename == '\0')
   7275         break;
   7276       status=ShredFile(filename);
   7277       if (status != MagickFalse )
   7278         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
   7279       break;
   7280     }
   7281     case NewCommand:
   7282     {
   7283       int
   7284         status;
   7285 
   7286       static char
   7287         color[MagickPathExtent] = "gray",
   7288         geometry[MagickPathExtent] = "640x480";
   7289 
   7290       static const char
   7291         *format = "gradient";
   7292 
   7293       /*
   7294         Query user for canvas geometry.
   7295       */
   7296       status=XDialogWidget(display,windows,"New","Enter image geometry:",
   7297         geometry);
   7298       if (*geometry == '\0')
   7299         break;
   7300       if (status == 0)
   7301         format="xc";
   7302       XColorBrowserWidget(display,windows,"Select",color);
   7303       if (*color == '\0')
   7304         break;
   7305       /*
   7306         Create canvas.
   7307       */
   7308       (void) FormatLocaleString(image_info->filename,MagickPathExtent,
   7309         "%s:%s",format,color);
   7310       (void) CloneString(&image_info->size,geometry);
   7311       nexus=ReadImage(image_info,exception);
   7312       CatchException(exception);
   7313       XClientMessage(display,windows->image.id,windows->im_protocols,
   7314         windows->im_next_image,CurrentTime);
   7315       break;
   7316     }
   7317     case VisualDirectoryCommand:
   7318     {
   7319       /*
   7320         Visual Image directory.
   7321       */
   7322       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
   7323       break;
   7324     }
   7325     case QuitCommand:
   7326     {
   7327       /*
   7328         exit program.
   7329       */
   7330       if (resource_info->confirm_exit == MagickFalse)
   7331         XClientMessage(display,windows->image.id,windows->im_protocols,
   7332           windows->im_exit,CurrentTime);
   7333       else
   7334         {
   7335           int
   7336             status;
   7337 
   7338           /*
   7339             Confirm program exit.
   7340           */
   7341           status=XConfirmWidget(display,windows,"Do you really want to exit",
   7342             resource_info->client_name);
   7343           if (status > 0)
   7344             XClientMessage(display,windows->image.id,windows->im_protocols,
   7345               windows->im_exit,CurrentTime);
   7346         }
   7347       break;
   7348     }
   7349     case CutCommand:
   7350     {
   7351       /*
   7352         Cut image.
   7353       */
   7354       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
   7355       break;
   7356     }
   7357     case CopyCommand:
   7358     {
   7359       /*
   7360         Copy image.
   7361       */
   7362       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
   7363         exception);
   7364       break;
   7365     }
   7366     case PasteCommand:
   7367     {
   7368       /*
   7369         Paste image.
   7370       */
   7371       status=XPasteImage(display,resource_info,windows,*image,exception);
   7372       if (status == MagickFalse)
   7373         {
   7374           XNoticeWidget(display,windows,"Unable to paste X image",
   7375             (*image)->filename);
   7376           break;
   7377         }
   7378       break;
   7379     }
   7380     case HalfSizeCommand:
   7381     {
   7382       /*
   7383         Half image size.
   7384       */
   7385       windows->image.window_changes.width=windows->image.ximage->width/2;
   7386       windows->image.window_changes.height=windows->image.ximage->height/2;
   7387       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7388       break;
   7389     }
   7390     case OriginalSizeCommand:
   7391     {
   7392       /*
   7393         Original image size.
   7394       */
   7395       windows->image.window_changes.width=(int) (*image)->columns;
   7396       windows->image.window_changes.height=(int) (*image)->rows;
   7397       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7398       break;
   7399     }
   7400     case DoubleSizeCommand:
   7401     {
   7402       /*
   7403         Double the image size.
   7404       */
   7405       windows->image.window_changes.width=windows->image.ximage->width << 1;
   7406       windows->image.window_changes.height=windows->image.ximage->height << 1;
   7407       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7408       break;
   7409     }
   7410     case ResizeCommand:
   7411     {
   7412       int
   7413         status;
   7414 
   7415       size_t
   7416         height,
   7417         width;
   7418 
   7419       ssize_t
   7420         x,
   7421         y;
   7422 
   7423       /*
   7424         Resize image.
   7425       */
   7426       width=(size_t) windows->image.ximage->width;
   7427       height=(size_t) windows->image.ximage->height;
   7428       x=0;
   7429       y=0;
   7430       (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
   7431         (double) width,(double) height);
   7432       status=XDialogWidget(display,windows,"Resize",
   7433         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
   7434       if (*geometry == '\0')
   7435         break;
   7436       if (status == 0)
   7437         (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
   7438       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
   7439       windows->image.window_changes.width=(int) width;
   7440       windows->image.window_changes.height=(int) height;
   7441       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7442       break;
   7443     }
   7444     case ApplyCommand:
   7445     {
   7446       char
   7447         image_geometry[MagickPathExtent];
   7448 
   7449       if ((windows->image.crop_geometry == (char *) NULL) &&
   7450           ((int) (*image)->columns == windows->image.ximage->width) &&
   7451           ((int) (*image)->rows == windows->image.ximage->height))
   7452         break;
   7453       /*
   7454         Apply size transforms to image.
   7455       */
   7456       XSetCursorState(display,windows,MagickTrue);
   7457       XCheckRefreshWindows(display,windows);
   7458       /*
   7459         Crop and/or scale displayed image.
   7460       */
   7461       (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
   7462         windows->image.ximage->width,windows->image.ximage->height);
   7463       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
   7464         exception);
   7465       if (windows->image.crop_geometry != (char *) NULL)
   7466         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
   7467           windows->image.crop_geometry);
   7468       windows->image.x=0;
   7469       windows->image.y=0;
   7470       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7471       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7472       break;
   7473     }
   7474     case RefreshCommand:
   7475     {
   7476       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7477       break;
   7478     }
   7479     case RestoreCommand:
   7480     {
   7481       /*
   7482         Restore Image window to its original size.
   7483       */
   7484       if ((windows->image.width == (unsigned int) (*image)->columns) &&
   7485           (windows->image.height == (unsigned int) (*image)->rows) &&
   7486           (windows->image.crop_geometry == (char *) NULL))
   7487         {
   7488           (void) XBell(display,0);
   7489           break;
   7490         }
   7491       windows->image.window_changes.width=(int) (*image)->columns;
   7492       windows->image.window_changes.height=(int) (*image)->rows;
   7493       if (windows->image.crop_geometry != (char *) NULL)
   7494         {
   7495           windows->image.crop_geometry=(char *)
   7496             RelinquishMagickMemory(windows->image.crop_geometry);
   7497           windows->image.crop_geometry=(char *) NULL;
   7498           windows->image.x=0;
   7499           windows->image.y=0;
   7500         }
   7501       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7502       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7503       break;
   7504     }
   7505     case CropCommand:
   7506     {
   7507       /*
   7508         Crop image.
   7509       */
   7510       (void) XCropImage(display,resource_info,windows,*image,CropMode,
   7511         exception);
   7512       break;
   7513     }
   7514     case ChopCommand:
   7515     {
   7516       /*
   7517         Chop image.
   7518       */
   7519       status=XChopImage(display,resource_info,windows,image,exception);
   7520       if (status == MagickFalse)
   7521         {
   7522           XNoticeWidget(display,windows,"Unable to cut X image",
   7523             (*image)->filename);
   7524           break;
   7525         }
   7526       break;
   7527     }
   7528     case FlopCommand:
   7529     {
   7530       Image
   7531         *flop_image;
   7532 
   7533       /*
   7534         Flop image scanlines.
   7535       */
   7536       XSetCursorState(display,windows,MagickTrue);
   7537       XCheckRefreshWindows(display,windows);
   7538       flop_image=FlopImage(*image,exception);
   7539       if (flop_image != (Image *) NULL)
   7540         {
   7541           *image=DestroyImage(*image);
   7542           *image=flop_image;
   7543         }
   7544       CatchException(exception);
   7545       XSetCursorState(display,windows,MagickFalse);
   7546       if (windows->image.crop_geometry != (char *) NULL)
   7547         {
   7548           /*
   7549             Flop crop geometry.
   7550           */
   7551           width=(unsigned int) (*image)->columns;
   7552           height=(unsigned int) (*image)->rows;
   7553           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
   7554             &width,&height);
   7555           (void) FormatLocaleString(windows->image.crop_geometry,
   7556             MagickPathExtent,"%ux%u%+d%+d",width,height,(int) (*image)->columns-
   7557             (int) width-x,y);
   7558         }
   7559       if (windows->image.orphan != MagickFalse )
   7560         break;
   7561       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7562       break;
   7563     }
   7564     case FlipCommand:
   7565     {
   7566       Image
   7567         *flip_image;
   7568 
   7569       /*
   7570         Flip image scanlines.
   7571       */
   7572       XSetCursorState(display,windows,MagickTrue);
   7573       XCheckRefreshWindows(display,windows);
   7574       flip_image=FlipImage(*image,exception);
   7575       if (flip_image != (Image *) NULL)
   7576         {
   7577           *image=DestroyImage(*image);
   7578           *image=flip_image;
   7579         }
   7580       CatchException(exception);
   7581       XSetCursorState(display,windows,MagickFalse);
   7582       if (windows->image.crop_geometry != (char *) NULL)
   7583         {
   7584           /*
   7585             Flip crop geometry.
   7586           */
   7587           width=(unsigned int) (*image)->columns;
   7588           height=(unsigned int) (*image)->rows;
   7589           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
   7590             &width,&height);
   7591           (void) FormatLocaleString(windows->image.crop_geometry,
   7592             MagickPathExtent,"%ux%u%+d%+d",width,height,x,(int) (*image)->rows-
   7593             (int) height-y);
   7594         }
   7595       if (windows->image.orphan != MagickFalse )
   7596         break;
   7597       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7598       break;
   7599     }
   7600     case RotateRightCommand:
   7601     {
   7602       /*
   7603         Rotate image 90 degrees clockwise.
   7604       */
   7605       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
   7606       if (status == MagickFalse)
   7607         {
   7608           XNoticeWidget(display,windows,"Unable to rotate X image",
   7609             (*image)->filename);
   7610           break;
   7611         }
   7612       break;
   7613     }
   7614     case RotateLeftCommand:
   7615     {
   7616       /*
   7617         Rotate image 90 degrees counter-clockwise.
   7618       */
   7619       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
   7620       if (status == MagickFalse)
   7621         {
   7622           XNoticeWidget(display,windows,"Unable to rotate X image",
   7623             (*image)->filename);
   7624           break;
   7625         }
   7626       break;
   7627     }
   7628     case RotateCommand:
   7629     {
   7630       /*
   7631         Rotate image.
   7632       */
   7633       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
   7634       if (status == MagickFalse)
   7635         {
   7636           XNoticeWidget(display,windows,"Unable to rotate X image",
   7637             (*image)->filename);
   7638           break;
   7639         }
   7640       break;
   7641     }
   7642     case ShearCommand:
   7643     {
   7644       Image
   7645         *shear_image;
   7646 
   7647       static char
   7648         geometry[MagickPathExtent] = "45.0x45.0";
   7649 
   7650       /*
   7651         Query user for shear color and geometry.
   7652       */
   7653       XColorBrowserWidget(display,windows,"Select",color);
   7654       if (*color == '\0')
   7655         break;
   7656       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
   7657         geometry);
   7658       if (*geometry == '\0')
   7659         break;
   7660       /*
   7661         Shear image.
   7662       */
   7663       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
   7664         exception);
   7665       XSetCursorState(display,windows,MagickTrue);
   7666       XCheckRefreshWindows(display,windows);
   7667       (void) QueryColorCompliance(color,AllCompliance,
   7668         &(*image)->background_color,exception);
   7669       flags=ParseGeometry(geometry,&geometry_info);
   7670       if ((flags & SigmaValue) == 0)
   7671         geometry_info.sigma=geometry_info.rho;
   7672       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
   7673         exception);
   7674       if (shear_image != (Image *) NULL)
   7675         {
   7676           *image=DestroyImage(*image);
   7677           *image=shear_image;
   7678         }
   7679       CatchException(exception);
   7680       XSetCursorState(display,windows,MagickFalse);
   7681       if (windows->image.orphan != MagickFalse )
   7682         break;
   7683       windows->image.window_changes.width=(int) (*image)->columns;
   7684       windows->image.window_changes.height=(int) (*image)->rows;
   7685       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7686       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7687       break;
   7688     }
   7689     case RollCommand:
   7690     {
   7691       Image
   7692         *roll_image;
   7693 
   7694       static char
   7695         geometry[MagickPathExtent] = "+2+2";
   7696 
   7697       /*
   7698         Query user for the roll geometry.
   7699       */
   7700       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
   7701         geometry);
   7702       if (*geometry == '\0')
   7703         break;
   7704       /*
   7705         Roll image.
   7706       */
   7707       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
   7708         exception);
   7709       XSetCursorState(display,windows,MagickTrue);
   7710       XCheckRefreshWindows(display,windows);
   7711       (void) ParsePageGeometry(*image,geometry,&page_geometry,
   7712         exception);
   7713       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
   7714         exception);
   7715       if (roll_image != (Image *) NULL)
   7716         {
   7717           *image=DestroyImage(*image);
   7718           *image=roll_image;
   7719         }
   7720       CatchException(exception);
   7721       XSetCursorState(display,windows,MagickFalse);
   7722       if (windows->image.orphan != MagickFalse )
   7723         break;
   7724       windows->image.window_changes.width=(int) (*image)->columns;
   7725       windows->image.window_changes.height=(int) (*image)->rows;
   7726       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7727       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7728       break;
   7729     }
   7730     case TrimCommand:
   7731     {
   7732       static char
   7733         fuzz[MagickPathExtent];
   7734 
   7735       /*
   7736         Query user for the fuzz factor.
   7737       */
   7738       (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
   7739         (*image)->fuzz/(QuantumRange+1.0));
   7740       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
   7741       if (*fuzz == '\0')
   7742         break;
   7743       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
   7744       /*
   7745         Trim image.
   7746       */
   7747       status=XTrimImage(display,resource_info,windows,*image,exception);
   7748       if (status == MagickFalse)
   7749         {
   7750           XNoticeWidget(display,windows,"Unable to trim X image",
   7751             (*image)->filename);
   7752           break;
   7753         }
   7754       break;
   7755     }
   7756     case HueCommand:
   7757     {
   7758       static char
   7759         hue_percent[MagickPathExtent] = "110";
   7760 
   7761       /*
   7762         Query user for percent hue change.
   7763       */
   7764       (void) XDialogWidget(display,windows,"Apply",
   7765         "Enter percent change in image hue (0-200):",hue_percent);
   7766       if (*hue_percent == '\0')
   7767         break;
   7768       /*
   7769         Vary the image hue.
   7770       */
   7771       XSetCursorState(display,windows,MagickTrue);
   7772       XCheckRefreshWindows(display,windows);
   7773       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
   7774       (void) ConcatenateMagickString(modulate_factors,hue_percent,
   7775         MagickPathExtent);
   7776       (void) ModulateImage(*image,modulate_factors,exception);
   7777       XSetCursorState(display,windows,MagickFalse);
   7778       if (windows->image.orphan != MagickFalse )
   7779         break;
   7780       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7781       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7782       break;
   7783     }
   7784     case SaturationCommand:
   7785     {
   7786       static char
   7787         saturation_percent[MagickPathExtent] = "110";
   7788 
   7789       /*
   7790         Query user for percent saturation change.
   7791       */
   7792       (void) XDialogWidget(display,windows,"Apply",
   7793         "Enter percent change in color saturation (0-200):",saturation_percent);
   7794       if (*saturation_percent == '\0')
   7795         break;
   7796       /*
   7797         Vary color saturation.
   7798       */
   7799       XSetCursorState(display,windows,MagickTrue);
   7800       XCheckRefreshWindows(display,windows);
   7801       (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
   7802       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
   7803         MagickPathExtent);
   7804       (void) ModulateImage(*image,modulate_factors,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 BrightnessCommand:
   7813     {
   7814       static char
   7815         brightness_percent[MagickPathExtent] = "110";
   7816 
   7817       /*
   7818         Query user for percent brightness change.
   7819       */
   7820       (void) XDialogWidget(display,windows,"Apply",
   7821         "Enter percent change in color brightness (0-200):",brightness_percent);
   7822       if (*brightness_percent == '\0')
   7823         break;
   7824       /*
   7825         Vary the color brightness.
   7826       */
   7827       XSetCursorState(display,windows,MagickTrue);
   7828       XCheckRefreshWindows(display,windows);
   7829       (void) CopyMagickString(modulate_factors,brightness_percent,
   7830         MagickPathExtent);
   7831       (void) ModulateImage(*image,modulate_factors,exception);
   7832       XSetCursorState(display,windows,MagickFalse);
   7833       if (windows->image.orphan != MagickFalse )
   7834         break;
   7835       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7836       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7837       break;
   7838     }
   7839     case GammaCommand:
   7840     {
   7841       static char
   7842         factor[MagickPathExtent] = "1.6";
   7843 
   7844       /*
   7845         Query user for gamma value.
   7846       */
   7847       (void) XDialogWidget(display,windows,"Gamma",
   7848         "Enter gamma value (e.g. 1.2):",factor);
   7849       if (*factor == '\0')
   7850         break;
   7851       /*
   7852         Gamma correct image.
   7853       */
   7854       XSetCursorState(display,windows,MagickTrue);
   7855       XCheckRefreshWindows(display,windows);
   7856       (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
   7857       XSetCursorState(display,windows,MagickFalse);
   7858       if (windows->image.orphan != MagickFalse )
   7859         break;
   7860       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7861       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7862       break;
   7863     }
   7864     case SpiffCommand:
   7865     {
   7866       /*
   7867         Sharpen the image contrast.
   7868       */
   7869       XSetCursorState(display,windows,MagickTrue);
   7870       XCheckRefreshWindows(display,windows);
   7871       (void) ContrastImage(*image,MagickTrue,exception);
   7872       XSetCursorState(display,windows,MagickFalse);
   7873       if (windows->image.orphan != MagickFalse )
   7874         break;
   7875       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7876       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7877       break;
   7878     }
   7879     case DullCommand:
   7880     {
   7881       /*
   7882         Dull the image contrast.
   7883       */
   7884       XSetCursorState(display,windows,MagickTrue);
   7885       XCheckRefreshWindows(display,windows);
   7886       (void) ContrastImage(*image,MagickFalse,exception);
   7887       XSetCursorState(display,windows,MagickFalse);
   7888       if (windows->image.orphan != MagickFalse )
   7889         break;
   7890       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7891       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7892       break;
   7893     }
   7894     case ContrastStretchCommand:
   7895     {
   7896       double
   7897         black_point,
   7898         white_point;
   7899 
   7900       static char
   7901         levels[MagickPathExtent] = "1%";
   7902 
   7903       /*
   7904         Query user for gamma value.
   7905       */
   7906       (void) XDialogWidget(display,windows,"Contrast Stretch",
   7907         "Enter black and white points:",levels);
   7908       if (*levels == '\0')
   7909         break;
   7910       /*
   7911         Contrast stretch image.
   7912       */
   7913       XSetCursorState(display,windows,MagickTrue);
   7914       XCheckRefreshWindows(display,windows);
   7915       flags=ParseGeometry(levels,&geometry_info);
   7916       black_point=geometry_info.rho;
   7917       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
   7918       if ((flags & PercentValue) != 0)
   7919         {
   7920           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
   7921           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
   7922         }
   7923       white_point=(double) (*image)->columns*(*image)->rows-white_point;
   7924       (void) ContrastStretchImage(*image,black_point,white_point,
   7925         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 SigmoidalContrastCommand:
   7934     {
   7935       GeometryInfo
   7936         geometry_info;
   7937 
   7938       MagickStatusType
   7939         flags;
   7940 
   7941       static char
   7942         levels[MagickPathExtent] = "3x50%";
   7943 
   7944       /*
   7945         Query user for gamma value.
   7946       */
   7947       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
   7948         "Enter contrast and midpoint:",levels);
   7949       if (*levels == '\0')
   7950         break;
   7951       /*
   7952         Contrast stretch image.
   7953       */
   7954       XSetCursorState(display,windows,MagickTrue);
   7955       XCheckRefreshWindows(display,windows);
   7956       flags=ParseGeometry(levels,&geometry_info);
   7957       if ((flags & SigmaValue) == 0)
   7958         geometry_info.sigma=1.0*QuantumRange/2.0;
   7959       if ((flags & PercentValue) != 0)
   7960         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
   7961       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
   7962         geometry_info.sigma,exception);
   7963       XSetCursorState(display,windows,MagickFalse);
   7964       if (windows->image.orphan != MagickFalse )
   7965         break;
   7966       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7967       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7968       break;
   7969     }
   7970     case NormalizeCommand:
   7971     {
   7972       /*
   7973         Perform histogram normalization on the image.
   7974       */
   7975       XSetCursorState(display,windows,MagickTrue);
   7976       XCheckRefreshWindows(display,windows);
   7977       (void) NormalizeImage(*image,exception);
   7978       XSetCursorState(display,windows,MagickFalse);
   7979       if (windows->image.orphan != MagickFalse )
   7980         break;
   7981       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7982       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7983       break;
   7984     }
   7985     case EqualizeCommand:
   7986     {
   7987       /*
   7988         Perform histogram equalization on the image.
   7989       */
   7990       XSetCursorState(display,windows,MagickTrue);
   7991       XCheckRefreshWindows(display,windows);
   7992       (void) EqualizeImage(*image,exception);
   7993       XSetCursorState(display,windows,MagickFalse);
   7994       if (windows->image.orphan != MagickFalse )
   7995         break;
   7996       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   7997       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   7998       break;
   7999     }
   8000     case NegateCommand:
   8001     {
   8002       /*
   8003         Negate colors in image.
   8004       */
   8005       XSetCursorState(display,windows,MagickTrue);
   8006       XCheckRefreshWindows(display,windows);
   8007       (void) NegateImage(*image,MagickFalse,exception);
   8008       XSetCursorState(display,windows,MagickFalse);
   8009       if (windows->image.orphan != MagickFalse )
   8010         break;
   8011       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8012       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8013       break;
   8014     }
   8015     case GrayscaleCommand:
   8016     {
   8017       /*
   8018         Convert image to grayscale.
   8019       */
   8020       XSetCursorState(display,windows,MagickTrue);
   8021       XCheckRefreshWindows(display,windows);
   8022       (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
   8023         GrayscaleType : GrayscaleAlphaType,exception);
   8024       XSetCursorState(display,windows,MagickFalse);
   8025       if (windows->image.orphan != MagickFalse )
   8026         break;
   8027       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8028       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8029       break;
   8030     }
   8031     case MapCommand:
   8032     {
   8033       Image
   8034         *affinity_image;
   8035 
   8036       static char
   8037         filename[MagickPathExtent] = "\0";
   8038 
   8039       /*
   8040         Request image file name from user.
   8041       */
   8042       XFileBrowserWidget(display,windows,"Map",filename);
   8043       if (*filename == '\0')
   8044         break;
   8045       /*
   8046         Map image.
   8047       */
   8048       XSetCursorState(display,windows,MagickTrue);
   8049       XCheckRefreshWindows(display,windows);
   8050       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
   8051       affinity_image=ReadImage(image_info,exception);
   8052       if (affinity_image != (Image *) NULL)
   8053         {
   8054           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
   8055           affinity_image=DestroyImage(affinity_image);
   8056         }
   8057       CatchException(exception);
   8058       XSetCursorState(display,windows,MagickFalse);
   8059       if (windows->image.orphan != MagickFalse )
   8060         break;
   8061       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8062       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8063       break;
   8064     }
   8065     case QuantizeCommand:
   8066     {
   8067       int
   8068         status;
   8069 
   8070       static char
   8071         colors[MagickPathExtent] = "256";
   8072 
   8073       /*
   8074         Query user for maximum number of colors.
   8075       */
   8076       status=XDialogWidget(display,windows,"Quantize",
   8077         "Maximum number of colors:",colors);
   8078       if (*colors == '\0')
   8079         break;
   8080       /*
   8081         Color reduce the image.
   8082       */
   8083       XSetCursorState(display,windows,MagickTrue);
   8084       XCheckRefreshWindows(display,windows);
   8085       quantize_info.number_colors=StringToUnsignedLong(colors);
   8086       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
   8087         NoDitherMethod;
   8088       (void) QuantizeImage(&quantize_info,*image,exception);
   8089       XSetCursorState(display,windows,MagickFalse);
   8090       if (windows->image.orphan != MagickFalse )
   8091         break;
   8092       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8093       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8094       break;
   8095     }
   8096     case DespeckleCommand:
   8097     {
   8098       Image
   8099         *despeckle_image;
   8100 
   8101       /*
   8102         Despeckle image.
   8103       */
   8104       XSetCursorState(display,windows,MagickTrue);
   8105       XCheckRefreshWindows(display,windows);
   8106       despeckle_image=DespeckleImage(*image,exception);
   8107       if (despeckle_image != (Image *) NULL)
   8108         {
   8109           *image=DestroyImage(*image);
   8110           *image=despeckle_image;
   8111         }
   8112       CatchException(exception);
   8113       XSetCursorState(display,windows,MagickFalse);
   8114       if (windows->image.orphan != MagickFalse )
   8115         break;
   8116       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8117       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8118       break;
   8119     }
   8120     case EmbossCommand:
   8121     {
   8122       Image
   8123         *emboss_image;
   8124 
   8125       static char
   8126         radius[MagickPathExtent] = "0.0x1.0";
   8127 
   8128       /*
   8129         Query user for emboss radius.
   8130       */
   8131       (void) XDialogWidget(display,windows,"Emboss",
   8132         "Enter the emboss radius and standard deviation:",radius);
   8133       if (*radius == '\0')
   8134         break;
   8135       /*
   8136         Reduce noise in the image.
   8137       */
   8138       XSetCursorState(display,windows,MagickTrue);
   8139       XCheckRefreshWindows(display,windows);
   8140       flags=ParseGeometry(radius,&geometry_info);
   8141       if ((flags & SigmaValue) == 0)
   8142         geometry_info.sigma=1.0;
   8143       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
   8144         exception);
   8145       if (emboss_image != (Image *) NULL)
   8146         {
   8147           *image=DestroyImage(*image);
   8148           *image=emboss_image;
   8149         }
   8150       CatchException(exception);
   8151       XSetCursorState(display,windows,MagickFalse);
   8152       if (windows->image.orphan != MagickFalse )
   8153         break;
   8154       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8155       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8156       break;
   8157     }
   8158     case ReduceNoiseCommand:
   8159     {
   8160       Image
   8161         *noise_image;
   8162 
   8163       static char
   8164         radius[MagickPathExtent] = "0";
   8165 
   8166       /*
   8167         Query user for noise radius.
   8168       */
   8169       (void) XDialogWidget(display,windows,"Reduce Noise",
   8170         "Enter the noise radius:",radius);
   8171       if (*radius == '\0')
   8172         break;
   8173       /*
   8174         Reduce noise in the image.
   8175       */
   8176       XSetCursorState(display,windows,MagickTrue);
   8177       XCheckRefreshWindows(display,windows);
   8178       flags=ParseGeometry(radius,&geometry_info);
   8179       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
   8180         geometry_info.rho,(size_t) geometry_info.rho,exception);
   8181       if (noise_image != (Image *) NULL)
   8182         {
   8183           *image=DestroyImage(*image);
   8184           *image=noise_image;
   8185         }
   8186       CatchException(exception);
   8187       XSetCursorState(display,windows,MagickFalse);
   8188       if (windows->image.orphan != MagickFalse )
   8189         break;
   8190       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8191       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8192       break;
   8193     }
   8194     case AddNoiseCommand:
   8195     {
   8196       char
   8197         **noises;
   8198 
   8199       Image
   8200         *noise_image;
   8201 
   8202       static char
   8203         noise_type[MagickPathExtent] = "Gaussian";
   8204 
   8205       /*
   8206         Add noise to the image.
   8207       */
   8208       noises=GetCommandOptions(MagickNoiseOptions);
   8209       if (noises == (char **) NULL)
   8210         break;
   8211       XListBrowserWidget(display,windows,&windows->widget,
   8212         (const char **) noises,"Add Noise",
   8213         "Select a type of noise to add to your image:",noise_type);
   8214       noises=DestroyStringList(noises);
   8215       if (*noise_type == '\0')
   8216         break;
   8217       XSetCursorState(display,windows,MagickTrue);
   8218       XCheckRefreshWindows(display,windows);
   8219       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
   8220         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
   8221       if (noise_image != (Image *) NULL)
   8222         {
   8223           *image=DestroyImage(*image);
   8224           *image=noise_image;
   8225         }
   8226       CatchException(exception);
   8227       XSetCursorState(display,windows,MagickFalse);
   8228       if (windows->image.orphan != MagickFalse )
   8229         break;
   8230       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8231       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8232       break;
   8233     }
   8234     case SharpenCommand:
   8235     {
   8236       Image
   8237         *sharp_image;
   8238 
   8239       static char
   8240         radius[MagickPathExtent] = "0.0x1.0";
   8241 
   8242       /*
   8243         Query user for sharpen radius.
   8244       */
   8245       (void) XDialogWidget(display,windows,"Sharpen",
   8246         "Enter the sharpen radius and standard deviation:",radius);
   8247       if (*radius == '\0')
   8248         break;
   8249       /*
   8250         Sharpen image scanlines.
   8251       */
   8252       XSetCursorState(display,windows,MagickTrue);
   8253       XCheckRefreshWindows(display,windows);
   8254       flags=ParseGeometry(radius,&geometry_info);
   8255       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
   8256         exception);
   8257       if (sharp_image != (Image *) NULL)
   8258         {
   8259           *image=DestroyImage(*image);
   8260           *image=sharp_image;
   8261         }
   8262       CatchException(exception);
   8263       XSetCursorState(display,windows,MagickFalse);
   8264       if (windows->image.orphan != MagickFalse )
   8265         break;
   8266       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8267       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8268       break;
   8269     }
   8270     case BlurCommand:
   8271     {
   8272       Image
   8273         *blur_image;
   8274 
   8275       static char
   8276         radius[MagickPathExtent] = "0.0x1.0";
   8277 
   8278       /*
   8279         Query user for blur radius.
   8280       */
   8281       (void) XDialogWidget(display,windows,"Blur",
   8282         "Enter the blur radius and standard deviation:",radius);
   8283       if (*radius == '\0')
   8284         break;
   8285       /*
   8286         Blur an image.
   8287       */
   8288       XSetCursorState(display,windows,MagickTrue);
   8289       XCheckRefreshWindows(display,windows);
   8290       flags=ParseGeometry(radius,&geometry_info);
   8291       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
   8292         exception);
   8293       if (blur_image != (Image *) NULL)
   8294         {
   8295           *image=DestroyImage(*image);
   8296           *image=blur_image;
   8297         }
   8298       CatchException(exception);
   8299       XSetCursorState(display,windows,MagickFalse);
   8300       if (windows->image.orphan != MagickFalse )
   8301         break;
   8302       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8303       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8304       break;
   8305     }
   8306     case ThresholdCommand:
   8307     {
   8308       double
   8309         threshold;
   8310 
   8311       static char
   8312         factor[MagickPathExtent] = "128";
   8313 
   8314       /*
   8315         Query user for threshold value.
   8316       */
   8317       (void) XDialogWidget(display,windows,"Threshold",
   8318         "Enter threshold value:",factor);
   8319       if (*factor == '\0')
   8320         break;
   8321       /*
   8322         Gamma correct image.
   8323       */
   8324       XSetCursorState(display,windows,MagickTrue);
   8325       XCheckRefreshWindows(display,windows);
   8326       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
   8327       (void) BilevelImage(*image,threshold,exception);
   8328       XSetCursorState(display,windows,MagickFalse);
   8329       if (windows->image.orphan != MagickFalse )
   8330         break;
   8331       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8332       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8333       break;
   8334     }
   8335     case EdgeDetectCommand:
   8336     {
   8337       Image
   8338         *edge_image;
   8339 
   8340       static char
   8341         radius[MagickPathExtent] = "0";
   8342 
   8343       /*
   8344         Query user for edge factor.
   8345       */
   8346       (void) XDialogWidget(display,windows,"Detect Edges",
   8347         "Enter the edge detect radius:",radius);
   8348       if (*radius == '\0')
   8349         break;
   8350       /*
   8351         Detect edge in image.
   8352       */
   8353       XSetCursorState(display,windows,MagickTrue);
   8354       XCheckRefreshWindows(display,windows);
   8355       flags=ParseGeometry(radius,&geometry_info);
   8356       edge_image=EdgeImage(*image,geometry_info.rho,exception);
   8357       if (edge_image != (Image *) NULL)
   8358         {
   8359           *image=DestroyImage(*image);
   8360           *image=edge_image;
   8361         }
   8362       CatchException(exception);
   8363       XSetCursorState(display,windows,MagickFalse);
   8364       if (windows->image.orphan != MagickFalse )
   8365         break;
   8366       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8367       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8368       break;
   8369     }
   8370     case SpreadCommand:
   8371     {
   8372       Image
   8373         *spread_image;
   8374 
   8375       static char
   8376         amount[MagickPathExtent] = "2";
   8377 
   8378       /*
   8379         Query user for spread amount.
   8380       */
   8381       (void) XDialogWidget(display,windows,"Spread",
   8382         "Enter the displacement amount:",amount);
   8383       if (*amount == '\0')
   8384         break;
   8385       /*
   8386         Displace image pixels by a random amount.
   8387       */
   8388       XSetCursorState(display,windows,MagickTrue);
   8389       XCheckRefreshWindows(display,windows);
   8390       flags=ParseGeometry(amount,&geometry_info);
   8391       spread_image=EdgeImage(*image,geometry_info.rho,exception);
   8392       if (spread_image != (Image *) NULL)
   8393         {
   8394           *image=DestroyImage(*image);
   8395           *image=spread_image;
   8396         }
   8397       CatchException(exception);
   8398       XSetCursorState(display,windows,MagickFalse);
   8399       if (windows->image.orphan != MagickFalse )
   8400         break;
   8401       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8402       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8403       break;
   8404     }
   8405     case ShadeCommand:
   8406     {
   8407       Image
   8408         *shade_image;
   8409 
   8410       int
   8411         status;
   8412 
   8413       static char
   8414         geometry[MagickPathExtent] = "30x30";
   8415 
   8416       /*
   8417         Query user for the shade geometry.
   8418       */
   8419       status=XDialogWidget(display,windows,"Shade",
   8420         "Enter the azimuth and elevation of the light source:",geometry);
   8421       if (*geometry == '\0')
   8422         break;
   8423       /*
   8424         Shade image pixels.
   8425       */
   8426       XSetCursorState(display,windows,MagickTrue);
   8427       XCheckRefreshWindows(display,windows);
   8428       flags=ParseGeometry(geometry,&geometry_info);
   8429       if ((flags & SigmaValue) == 0)
   8430         geometry_info.sigma=1.0;
   8431       shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
   8432         geometry_info.rho,geometry_info.sigma,exception);
   8433       if (shade_image != (Image *) NULL)
   8434         {
   8435           *image=DestroyImage(*image);
   8436           *image=shade_image;
   8437         }
   8438       CatchException(exception);
   8439       XSetCursorState(display,windows,MagickFalse);
   8440       if (windows->image.orphan != MagickFalse )
   8441         break;
   8442       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8443       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8444       break;
   8445     }
   8446     case RaiseCommand:
   8447     {
   8448       static char
   8449         bevel_width[MagickPathExtent] = "10";
   8450 
   8451       /*
   8452         Query user for bevel width.
   8453       */
   8454       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
   8455       if (*bevel_width == '\0')
   8456         break;
   8457       /*
   8458         Raise an image.
   8459       */
   8460       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
   8461         exception);
   8462       XSetCursorState(display,windows,MagickTrue);
   8463       XCheckRefreshWindows(display,windows);
   8464       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
   8465         exception);
   8466       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
   8467       XSetCursorState(display,windows,MagickFalse);
   8468       if (windows->image.orphan != MagickFalse )
   8469         break;
   8470       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8471       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8472       break;
   8473     }
   8474     case SegmentCommand:
   8475     {
   8476       static char
   8477         threshold[MagickPathExtent] = "1.0x1.5";
   8478 
   8479       /*
   8480         Query user for smoothing threshold.
   8481       */
   8482       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
   8483         threshold);
   8484       if (*threshold == '\0')
   8485         break;
   8486       /*
   8487         Segment an image.
   8488       */
   8489       XSetCursorState(display,windows,MagickTrue);
   8490       XCheckRefreshWindows(display,windows);
   8491       flags=ParseGeometry(threshold,&geometry_info);
   8492       if ((flags & SigmaValue) == 0)
   8493         geometry_info.sigma=1.0;
   8494       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
   8495         geometry_info.sigma,exception);
   8496       XSetCursorState(display,windows,MagickFalse);
   8497       if (windows->image.orphan != MagickFalse )
   8498         break;
   8499       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8500       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8501       break;
   8502     }
   8503     case SepiaToneCommand:
   8504     {
   8505       double
   8506         threshold;
   8507 
   8508       Image
   8509         *sepia_image;
   8510 
   8511       static char
   8512         factor[MagickPathExtent] = "80%";
   8513 
   8514       /*
   8515         Query user for sepia-tone factor.
   8516       */
   8517       (void) XDialogWidget(display,windows,"Sepia Tone",
   8518         "Enter the sepia tone factor (0 - 99.9%):",factor);
   8519       if (*factor == '\0')
   8520         break;
   8521       /*
   8522         Sepia tone image pixels.
   8523       */
   8524       XSetCursorState(display,windows,MagickTrue);
   8525       XCheckRefreshWindows(display,windows);
   8526       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
   8527       sepia_image=SepiaToneImage(*image,threshold,exception);
   8528       if (sepia_image != (Image *) NULL)
   8529         {
   8530           *image=DestroyImage(*image);
   8531           *image=sepia_image;
   8532         }
   8533       CatchException(exception);
   8534       XSetCursorState(display,windows,MagickFalse);
   8535       if (windows->image.orphan != MagickFalse )
   8536         break;
   8537       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8538       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8539       break;
   8540     }
   8541     case SolarizeCommand:
   8542     {
   8543       double
   8544         threshold;
   8545 
   8546       static char
   8547         factor[MagickPathExtent] = "60%";
   8548 
   8549       /*
   8550         Query user for solarize factor.
   8551       */
   8552       (void) XDialogWidget(display,windows,"Solarize",
   8553         "Enter the solarize factor (0 - 99.9%):",factor);
   8554       if (*factor == '\0')
   8555         break;
   8556       /*
   8557         Solarize image pixels.
   8558       */
   8559       XSetCursorState(display,windows,MagickTrue);
   8560       XCheckRefreshWindows(display,windows);
   8561       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
   8562       (void) SolarizeImage(*image,threshold,exception);
   8563       XSetCursorState(display,windows,MagickFalse);
   8564       if (windows->image.orphan != MagickFalse )
   8565         break;
   8566       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8567       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8568       break;
   8569     }
   8570     case SwirlCommand:
   8571     {
   8572       Image
   8573         *swirl_image;
   8574 
   8575       static char
   8576         degrees[MagickPathExtent] = "60";
   8577 
   8578       /*
   8579         Query user for swirl angle.
   8580       */
   8581       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
   8582         degrees);
   8583       if (*degrees == '\0')
   8584         break;
   8585       /*
   8586         Swirl image pixels about the center.
   8587       */
   8588       XSetCursorState(display,windows,MagickTrue);
   8589       XCheckRefreshWindows(display,windows);
   8590       flags=ParseGeometry(degrees,&geometry_info);
   8591       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
   8592         exception);
   8593       if (swirl_image != (Image *) NULL)
   8594         {
   8595           *image=DestroyImage(*image);
   8596           *image=swirl_image;
   8597         }
   8598       CatchException(exception);
   8599       XSetCursorState(display,windows,MagickFalse);
   8600       if (windows->image.orphan != MagickFalse )
   8601         break;
   8602       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8603       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8604       break;
   8605     }
   8606     case ImplodeCommand:
   8607     {
   8608       Image
   8609         *implode_image;
   8610 
   8611       static char
   8612         factor[MagickPathExtent] = "0.3";
   8613 
   8614       /*
   8615         Query user for implode factor.
   8616       */
   8617       (void) XDialogWidget(display,windows,"Implode",
   8618         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
   8619       if (*factor == '\0')
   8620         break;
   8621       /*
   8622         Implode image pixels about the center.
   8623       */
   8624       XSetCursorState(display,windows,MagickTrue);
   8625       XCheckRefreshWindows(display,windows);
   8626       flags=ParseGeometry(factor,&geometry_info);
   8627       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
   8628         exception);
   8629       if (implode_image != (Image *) NULL)
   8630         {
   8631           *image=DestroyImage(*image);
   8632           *image=implode_image;
   8633         }
   8634       CatchException(exception);
   8635       XSetCursorState(display,windows,MagickFalse);
   8636       if (windows->image.orphan != MagickFalse )
   8637         break;
   8638       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8639       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8640       break;
   8641     }
   8642     case VignetteCommand:
   8643     {
   8644       Image
   8645         *vignette_image;
   8646 
   8647       static char
   8648         geometry[MagickPathExtent] = "0x20";
   8649 
   8650       /*
   8651         Query user for the vignette geometry.
   8652       */
   8653       (void) XDialogWidget(display,windows,"Vignette",
   8654         "Enter the radius, sigma, and x and y offsets:",geometry);
   8655       if (*geometry == '\0')
   8656         break;
   8657       /*
   8658         Soften the edges of the image in vignette style
   8659       */
   8660       XSetCursorState(display,windows,MagickTrue);
   8661       XCheckRefreshWindows(display,windows);
   8662       flags=ParseGeometry(geometry,&geometry_info);
   8663       if ((flags & SigmaValue) == 0)
   8664         geometry_info.sigma=1.0;
   8665       if ((flags & XiValue) == 0)
   8666         geometry_info.xi=0.1*(*image)->columns;
   8667       if ((flags & PsiValue) == 0)
   8668         geometry_info.psi=0.1*(*image)->rows;
   8669       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
   8670         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
   8671         exception);
   8672       if (vignette_image != (Image *) NULL)
   8673         {
   8674           *image=DestroyImage(*image);
   8675           *image=vignette_image;
   8676         }
   8677       CatchException(exception);
   8678       XSetCursorState(display,windows,MagickFalse);
   8679       if (windows->image.orphan != MagickFalse )
   8680         break;
   8681       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8682       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8683       break;
   8684     }
   8685     case WaveCommand:
   8686     {
   8687       Image
   8688         *wave_image;
   8689 
   8690       static char
   8691         geometry[MagickPathExtent] = "25x150";
   8692 
   8693       /*
   8694         Query user for the wave geometry.
   8695       */
   8696       (void) XDialogWidget(display,windows,"Wave",
   8697         "Enter the amplitude and length of the wave:",geometry);
   8698       if (*geometry == '\0')
   8699         break;
   8700       /*
   8701         Alter an image along a sine wave.
   8702       */
   8703       XSetCursorState(display,windows,MagickTrue);
   8704       XCheckRefreshWindows(display,windows);
   8705       flags=ParseGeometry(geometry,&geometry_info);
   8706       if ((flags & SigmaValue) == 0)
   8707         geometry_info.sigma=1.0;
   8708       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
   8709         (*image)->interpolate,exception);
   8710       if (wave_image != (Image *) NULL)
   8711         {
   8712           *image=DestroyImage(*image);
   8713           *image=wave_image;
   8714         }
   8715       CatchException(exception);
   8716       XSetCursorState(display,windows,MagickFalse);
   8717       if (windows->image.orphan != MagickFalse )
   8718         break;
   8719       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8720       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8721       break;
   8722     }
   8723     case OilPaintCommand:
   8724     {
   8725       Image
   8726         *paint_image;
   8727 
   8728       static char
   8729         radius[MagickPathExtent] = "0";
   8730 
   8731       /*
   8732         Query user for circular neighborhood radius.
   8733       */
   8734       (void) XDialogWidget(display,windows,"Oil Paint",
   8735         "Enter the mask radius:",radius);
   8736       if (*radius == '\0')
   8737         break;
   8738       /*
   8739         OilPaint image scanlines.
   8740       */
   8741       XSetCursorState(display,windows,MagickTrue);
   8742       XCheckRefreshWindows(display,windows);
   8743       flags=ParseGeometry(radius,&geometry_info);
   8744       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
   8745         exception);
   8746       if (paint_image != (Image *) NULL)
   8747         {
   8748           *image=DestroyImage(*image);
   8749           *image=paint_image;
   8750         }
   8751       CatchException(exception);
   8752       XSetCursorState(display,windows,MagickFalse);
   8753       if (windows->image.orphan != MagickFalse )
   8754         break;
   8755       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8756       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8757       break;
   8758     }
   8759     case CharcoalDrawCommand:
   8760     {
   8761       Image
   8762         *charcoal_image;
   8763 
   8764       static char
   8765         radius[MagickPathExtent] = "0x1";
   8766 
   8767       /*
   8768         Query user for charcoal radius.
   8769       */
   8770       (void) XDialogWidget(display,windows,"Charcoal Draw",
   8771         "Enter the charcoal radius and sigma:",radius);
   8772       if (*radius == '\0')
   8773         break;
   8774       /*
   8775         Charcoal the image.
   8776       */
   8777       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
   8778         exception);
   8779       XSetCursorState(display,windows,MagickTrue);
   8780       XCheckRefreshWindows(display,windows);
   8781       flags=ParseGeometry(radius,&geometry_info);
   8782       if ((flags & SigmaValue) == 0)
   8783         geometry_info.sigma=geometry_info.rho;
   8784       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
   8785         exception);
   8786       if (charcoal_image != (Image *) NULL)
   8787         {
   8788           *image=DestroyImage(*image);
   8789           *image=charcoal_image;
   8790         }
   8791       CatchException(exception);
   8792       XSetCursorState(display,windows,MagickFalse);
   8793       if (windows->image.orphan != MagickFalse )
   8794         break;
   8795       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8796       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8797       break;
   8798     }
   8799     case AnnotateCommand:
   8800     {
   8801       /*
   8802         Annotate the image with text.
   8803       */
   8804       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
   8805       if (status == MagickFalse)
   8806         {
   8807           XNoticeWidget(display,windows,"Unable to annotate X image",
   8808             (*image)->filename);
   8809           break;
   8810         }
   8811       break;
   8812     }
   8813     case DrawCommand:
   8814     {
   8815       /*
   8816         Draw image.
   8817       */
   8818       status=XDrawEditImage(display,resource_info,windows,image,exception);
   8819       if (status == MagickFalse)
   8820         {
   8821           XNoticeWidget(display,windows,"Unable to draw on the X image",
   8822             (*image)->filename);
   8823           break;
   8824         }
   8825       break;
   8826     }
   8827     case ColorCommand:
   8828     {
   8829       /*
   8830         Color edit.
   8831       */
   8832       status=XColorEditImage(display,resource_info,windows,image,exception);
   8833       if (status == MagickFalse)
   8834         {
   8835           XNoticeWidget(display,windows,"Unable to pixel edit X image",
   8836             (*image)->filename);
   8837           break;
   8838         }
   8839       break;
   8840     }
   8841     case MatteCommand:
   8842     {
   8843       /*
   8844         Matte edit.
   8845       */
   8846       status=XMatteEditImage(display,resource_info,windows,image,exception);
   8847       if (status == MagickFalse)
   8848         {
   8849           XNoticeWidget(display,windows,"Unable to matte edit X image",
   8850             (*image)->filename);
   8851           break;
   8852         }
   8853       break;
   8854     }
   8855     case CompositeCommand:
   8856     {
   8857       /*
   8858         Composite image.
   8859       */
   8860       status=XCompositeImage(display,resource_info,windows,*image,
   8861         exception);
   8862       if (status == MagickFalse)
   8863         {
   8864           XNoticeWidget(display,windows,"Unable to composite X image",
   8865             (*image)->filename);
   8866           break;
   8867         }
   8868       break;
   8869     }
   8870     case AddBorderCommand:
   8871     {
   8872       Image
   8873         *border_image;
   8874 
   8875       static char
   8876         geometry[MagickPathExtent] = "6x6";
   8877 
   8878       /*
   8879         Query user for border color and geometry.
   8880       */
   8881       XColorBrowserWidget(display,windows,"Select",color);
   8882       if (*color == '\0')
   8883         break;
   8884       (void) XDialogWidget(display,windows,"Add Border",
   8885         "Enter border geometry:",geometry);
   8886       if (*geometry == '\0')
   8887         break;
   8888       /*
   8889         Add a border to the image.
   8890       */
   8891       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
   8892         exception);
   8893       XSetCursorState(display,windows,MagickTrue);
   8894       XCheckRefreshWindows(display,windows);
   8895       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
   8896         exception);
   8897       (void) ParsePageGeometry(*image,geometry,&page_geometry,
   8898         exception);
   8899       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
   8900         exception);
   8901       if (border_image != (Image *) NULL)
   8902         {
   8903           *image=DestroyImage(*image);
   8904           *image=border_image;
   8905         }
   8906       CatchException(exception);
   8907       XSetCursorState(display,windows,MagickFalse);
   8908       if (windows->image.orphan != MagickFalse )
   8909         break;
   8910       windows->image.window_changes.width=(int) (*image)->columns;
   8911       windows->image.window_changes.height=(int) (*image)->rows;
   8912       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8913       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8914       break;
   8915     }
   8916     case AddFrameCommand:
   8917     {
   8918       FrameInfo
   8919         frame_info;
   8920 
   8921       Image
   8922         *frame_image;
   8923 
   8924       static char
   8925         geometry[MagickPathExtent] = "6x6";
   8926 
   8927       /*
   8928         Query user for frame color and geometry.
   8929       */
   8930       XColorBrowserWidget(display,windows,"Select",color);
   8931       if (*color == '\0')
   8932         break;
   8933       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
   8934         geometry);
   8935       if (*geometry == '\0')
   8936         break;
   8937       /*
   8938         Surround image with an ornamental border.
   8939       */
   8940       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
   8941         exception);
   8942       XSetCursorState(display,windows,MagickTrue);
   8943       XCheckRefreshWindows(display,windows);
   8944       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
   8945         exception);
   8946       (void) ParsePageGeometry(*image,geometry,&page_geometry,
   8947         exception);
   8948       frame_info.width=page_geometry.width;
   8949       frame_info.height=page_geometry.height;
   8950       frame_info.outer_bevel=page_geometry.x;
   8951       frame_info.inner_bevel=page_geometry.y;
   8952       frame_info.x=(ssize_t) frame_info.width;
   8953       frame_info.y=(ssize_t) frame_info.height;
   8954       frame_info.width=(*image)->columns+2*frame_info.width;
   8955       frame_info.height=(*image)->rows+2*frame_info.height;
   8956       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
   8957       if (frame_image != (Image *) NULL)
   8958         {
   8959           *image=DestroyImage(*image);
   8960           *image=frame_image;
   8961         }
   8962       CatchException(exception);
   8963       XSetCursorState(display,windows,MagickFalse);
   8964       if (windows->image.orphan != MagickFalse )
   8965         break;
   8966       windows->image.window_changes.width=(int) (*image)->columns;
   8967       windows->image.window_changes.height=(int) (*image)->rows;
   8968       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   8969       (void) XConfigureImage(display,resource_info,windows,*image,exception);
   8970       break;
   8971     }
   8972     case CommentCommand:
   8973     {
   8974       const char
   8975         *value;
   8976 
   8977       FILE
   8978         *file;
   8979 
   8980       int
   8981         unique_file;
   8982 
   8983       /*
   8984         Edit image comment.
   8985       */
   8986       unique_file=AcquireUniqueFileResource(image_info->filename);
   8987       if (unique_file == -1)
   8988         XNoticeWidget(display,windows,"Unable to edit image comment",
   8989           image_info->filename);
   8990       value=GetImageProperty(*image,"comment",exception);
   8991       if (value == (char *) NULL)
   8992         unique_file=close(unique_file)-1;
   8993       else
   8994         {
   8995           register const char
   8996             *p;
   8997 
   8998           file=fdopen(unique_file,"w");
   8999           if (file == (FILE *) NULL)
   9000             {
   9001               XNoticeWidget(display,windows,"Unable to edit image comment",
   9002                 image_info->filename);
   9003               break;
   9004             }
   9005           for (p=value; *p != '\0'; p++)
   9006             (void) fputc((int) *p,file);
   9007           (void) fputc('\n',file);
   9008           (void) fclose(file);
   9009         }
   9010       XSetCursorState(display,windows,MagickTrue);
   9011       XCheckRefreshWindows(display,windows);
   9012       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
   9013         exception);
   9014       if (status == MagickFalse)
   9015         XNoticeWidget(display,windows,"Unable to edit image comment",
   9016           (char *) NULL);
   9017       else
   9018         {
   9019           char
   9020             *comment;
   9021 
   9022           comment=FileToString(image_info->filename,~0UL,exception);
   9023           if (comment != (char *) NULL)
   9024             {
   9025               (void) SetImageProperty(*image,"comment",comment,exception);
   9026               (*image)->taint=MagickTrue;
   9027             }
   9028         }
   9029       (void) RelinquishUniqueFileResource(image_info->filename);
   9030       XSetCursorState(display,windows,MagickFalse);
   9031       break;
   9032     }
   9033     case LaunchCommand:
   9034     {
   9035       /*
   9036         Launch program.
   9037       */
   9038       XSetCursorState(display,windows,MagickTrue);
   9039       XCheckRefreshWindows(display,windows);
   9040       (void) AcquireUniqueFilename(filename);
   9041       (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
   9042         filename);
   9043       status=WriteImage(image_info,*image,exception);
   9044       if (status == MagickFalse)
   9045         XNoticeWidget(display,windows,"Unable to launch image editor",
   9046           (char *) NULL);
   9047       else
   9048         {
   9049           nexus=ReadImage(resource_info->image_info,exception);
   9050           CatchException(exception);
   9051           XClientMessage(display,windows->image.id,windows->im_protocols,
   9052             windows->im_next_image,CurrentTime);
   9053         }
   9054       (void) RelinquishUniqueFileResource(filename);
   9055       XSetCursorState(display,windows,MagickFalse);
   9056       break;
   9057     }
   9058     case RegionofInterestCommand:
   9059     {
   9060       /*
   9061         Apply an image processing technique to a region of interest.
   9062       */
   9063       (void) XROIImage(display,resource_info,windows,image,exception);
   9064       break;
   9065     }
   9066     case InfoCommand:
   9067       break;
   9068     case ZoomCommand:
   9069     {
   9070       /*
   9071         Zoom image.
   9072       */
   9073       if (windows->magnify.mapped != MagickFalse )
   9074         (void) XRaiseWindow(display,windows->magnify.id);
   9075       else
   9076         {
   9077           /*
   9078             Make magnify image.
   9079           */
   9080           XSetCursorState(display,windows,MagickTrue);
   9081           (void) XMapRaised(display,windows->magnify.id);
   9082           XSetCursorState(display,windows,MagickFalse);
   9083         }
   9084       break;
   9085     }
   9086     case ShowPreviewCommand:
   9087     {
   9088       char
   9089         **previews,
   9090         value[MagickPathExtent];
   9091 
   9092       Image
   9093         *preview_image;
   9094 
   9095       PreviewType
   9096         preview;
   9097 
   9098       static char
   9099         preview_type[MagickPathExtent] = "Gamma";
   9100 
   9101       /*
   9102         Select preview type from menu.
   9103       */
   9104       previews=GetCommandOptions(MagickPreviewOptions);
   9105       if (previews == (char **) NULL)
   9106         break;
   9107       XListBrowserWidget(display,windows,&windows->widget,
   9108         (const char **) previews,"Preview",
   9109         "Select an enhancement, effect, or F/X:",preview_type);
   9110       previews=DestroyStringList(previews);
   9111       if (*preview_type == '\0')
   9112         break;
   9113       /*
   9114         Show image preview.
   9115       */
   9116       XSetCursorState(display,windows,MagickTrue);
   9117       XCheckRefreshWindows(display,windows);
   9118       preview=(PreviewType) ParseCommandOption(MagickPreviewOptions,
   9119         MagickFalse,preview_type);
   9120       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
   9121         windows->image.id);
   9122       (void) SetImageProperty(*image,"group",value,exception);
   9123       (void) DeleteImageProperty(*image,"label");
   9124       (void) SetImageProperty(*image,"label","Preview",exception);
   9125       preview_image=PreviewImage(*image,preview,exception);
   9126       if (preview_image == (Image *) NULL)
   9127         break;
   9128       (void) AcquireUniqueFilename(filename);
   9129       (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
   9130         "show:%s",filename);
   9131       status=WriteImage(image_info,preview_image,exception);
   9132       (void) RelinquishUniqueFileResource(filename);
   9133       preview_image=DestroyImage(preview_image);
   9134       if (status == MagickFalse)
   9135         XNoticeWidget(display,windows,"Unable to show image preview",
   9136           (*image)->filename);
   9137       XDelay(display,1500);
   9138       XSetCursorState(display,windows,MagickFalse);
   9139       break;
   9140     }
   9141     case ShowHistogramCommand:
   9142     {
   9143       char
   9144         value[MagickPathExtent];
   9145 
   9146       Image
   9147         *histogram_image;
   9148 
   9149       /*
   9150         Show image histogram.
   9151       */
   9152       XSetCursorState(display,windows,MagickTrue);
   9153       XCheckRefreshWindows(display,windows);
   9154       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
   9155         windows->image.id);
   9156       (void) SetImageProperty(*image,"group",value,exception);
   9157       (void) DeleteImageProperty(*image,"label");
   9158       (void) SetImageProperty(*image,"label","Histogram",exception);
   9159       (void) AcquireUniqueFilename(filename);
   9160       (void) FormatLocaleString((*image)->filename,MagickPathExtent,
   9161         "histogram:%s",filename);
   9162       status=WriteImage(image_info,*image,exception);
   9163       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
   9164       histogram_image=ReadImage(image_info,exception);
   9165       (void) RelinquishUniqueFileResource(filename);
   9166       if (histogram_image == (Image *) NULL)
   9167         break;
   9168       (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
   9169         "show:%s",filename);
   9170       status=WriteImage(image_info,histogram_image,exception);
   9171       histogram_image=DestroyImage(histogram_image);
   9172       if (status == MagickFalse)
   9173         XNoticeWidget(display,windows,"Unable to show histogram",
   9174           (*image)->filename);
   9175       XDelay(display,1500);
   9176       XSetCursorState(display,windows,MagickFalse);
   9177       break;
   9178     }
   9179     case ShowMatteCommand:
   9180     {
   9181       char
   9182         value[MagickPathExtent];
   9183 
   9184       Image
   9185         *matte_image;
   9186 
   9187       if ((*image)->alpha_trait == UndefinedPixelTrait)
   9188         {
   9189           XNoticeWidget(display,windows,
   9190             "Image does not have any matte information",(*image)->filename);
   9191           break;
   9192         }
   9193       /*
   9194         Show image matte.
   9195       */
   9196       XSetCursorState(display,windows,MagickTrue);
   9197       XCheckRefreshWindows(display,windows);
   9198       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
   9199         windows->image.id);
   9200       (void) SetImageProperty(*image,"group",value,exception);
   9201       (void) DeleteImageProperty(*image,"label");
   9202       (void) SetImageProperty(*image,"label","Matte",exception);
   9203       (void) AcquireUniqueFilename(filename);
   9204       (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
   9205         filename);
   9206       status=WriteImage(image_info,*image,exception);
   9207       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
   9208       matte_image=ReadImage(image_info,exception);
   9209       (void) RelinquishUniqueFileResource(filename);
   9210       if (matte_image == (Image *) NULL)
   9211         break;
   9212       (void) FormatLocaleString(matte_image->filename,MagickPathExtent,"show:%s",
   9213         filename);
   9214       status=WriteImage(image_info,matte_image,exception);
   9215       matte_image=DestroyImage(matte_image);
   9216       if (status == MagickFalse)
   9217         XNoticeWidget(display,windows,"Unable to show matte",
   9218           (*image)->filename);
   9219       XDelay(display,1500);
   9220       XSetCursorState(display,windows,MagickFalse);
   9221       break;
   9222     }
   9223     case BackgroundCommand:
   9224     {
   9225       /*
   9226         Background image.
   9227       */
   9228       status=XBackgroundImage(display,resource_info,windows,image,exception);
   9229       if (status == MagickFalse)
   9230         break;
   9231       nexus=CloneImage(*image,0,0,MagickTrue,exception);
   9232       if (nexus != (Image *) NULL)
   9233         XClientMessage(display,windows->image.id,windows->im_protocols,
   9234           windows->im_next_image,CurrentTime);
   9235       break;
   9236     }
   9237     case SlideShowCommand:
   9238     {
   9239       static char
   9240         delay[MagickPathExtent] = "5";
   9241 
   9242       /*
   9243         Display next image after pausing.
   9244       */
   9245       (void) XDialogWidget(display,windows,"Slide Show",
   9246         "Pause how many 1/100ths of a second between images:",delay);
   9247       if (*delay == '\0')
   9248         break;
   9249       resource_info->delay=StringToUnsignedLong(delay);
   9250       XClientMessage(display,windows->image.id,windows->im_protocols,
   9251         windows->im_next_image,CurrentTime);
   9252       break;
   9253     }
   9254     case PreferencesCommand:
   9255     {
   9256       /*
   9257         Set user preferences.
   9258       */
   9259       status=XPreferencesWidget(display,resource_info,windows);
   9260       if (status == MagickFalse)
   9261         break;
   9262       nexus=CloneImage(*image,0,0,MagickTrue,exception);
   9263       if (nexus != (Image *) NULL)
   9264         XClientMessage(display,windows->image.id,windows->im_protocols,
   9265           windows->im_next_image,CurrentTime);
   9266       break;
   9267     }
   9268     case HelpCommand:
   9269     {
   9270       /*
   9271         User requested help.
   9272       */
   9273       XTextViewWidget(display,resource_info,windows,MagickFalse,
   9274         "Help Viewer - Display",DisplayHelp);
   9275       break;
   9276     }
   9277     case BrowseDocumentationCommand:
   9278     {
   9279       Atom
   9280         mozilla_atom;
   9281 
   9282       Window
   9283         mozilla_window,
   9284         root_window;
   9285 
   9286       /*
   9287         Browse the ImageMagick documentation.
   9288       */
   9289       root_window=XRootWindow(display,XDefaultScreen(display));
   9290       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
   9291       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
   9292       if (mozilla_window != (Window) NULL)
   9293         {
   9294           char
   9295             command[MagickPathExtent],
   9296             *url;
   9297 
   9298           /*
   9299             Display documentation using Netscape remote control.
   9300           */
   9301           url=GetMagickHomeURL();
   9302           (void) FormatLocaleString(command,MagickPathExtent,
   9303             "openurl(%s,new-tab)",url);
   9304           url=DestroyString(url);
   9305           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
   9306           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
   9307             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
   9308           XSetCursorState(display,windows,MagickFalse);
   9309           break;
   9310         }
   9311       XSetCursorState(display,windows,MagickTrue);
   9312       XCheckRefreshWindows(display,windows);
   9313       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
   9314         exception);
   9315       if (status == MagickFalse)
   9316         XNoticeWidget(display,windows,"Unable to browse documentation",
   9317           (char *) NULL);
   9318       XDelay(display,1500);
   9319       XSetCursorState(display,windows,MagickFalse);
   9320       break;
   9321     }
   9322     case VersionCommand:
   9323     {
   9324       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
   9325         GetMagickCopyright());
   9326       break;
   9327     }
   9328     case SaveToUndoBufferCommand:
   9329       break;
   9330     default:
   9331     {
   9332       (void) XBell(display,0);
   9333       break;
   9334     }
   9335   }
   9336   image_info=DestroyImageInfo(image_info);
   9337   return(nexus);
   9338 }
   9339 
   9340 /*
   9342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   9343 %                                                                             %
   9344 %                                                                             %
   9345 %                                                                             %
   9346 +   X M a g n i f y I m a g e                                                 %
   9347 %                                                                             %
   9348 %                                                                             %
   9349 %                                                                             %
   9350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   9351 %
   9352 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
   9353 %  The magnified portion is displayed in a separate window.
   9354 %
   9355 %  The format of the XMagnifyImage method is:
   9356 %
   9357 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
   9358 %        ExceptionInfo *exception)
   9359 %
   9360 %  A description of each parameter follows:
   9361 %
   9362 %    o display: Specifies a connection to an X server;  returned from
   9363 %      XOpenDisplay.
   9364 %
   9365 %    o windows: Specifies a pointer to a XWindows structure.
   9366 %
   9367 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
   9368 %      the entire image is refreshed.
   9369 %
   9370 %    o exception: return any errors or warnings in this structure.
   9371 %
   9372 */
   9373 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
   9374   ExceptionInfo *exception)
   9375 {
   9376   char
   9377     text[MagickPathExtent];
   9378 
   9379   register int
   9380     x,
   9381     y;
   9382 
   9383   size_t
   9384     state;
   9385 
   9386   /*
   9387     Update magnified image until the mouse button is released.
   9388   */
   9389   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
   9390   state=DefaultState;
   9391   x=event->xbutton.x;
   9392   y=event->xbutton.y;
   9393   windows->magnify.x=(int) windows->image.x+x;
   9394   windows->magnify.y=(int) windows->image.y+y;
   9395   do
   9396   {
   9397     /*
   9398       Map and unmap Info widget as text cursor crosses its boundaries.
   9399     */
   9400     if (windows->info.mapped != MagickFalse )
   9401       {
   9402         if ((x < (int) (windows->info.x+windows->info.width)) &&
   9403             (y < (int) (windows->info.y+windows->info.height)))
   9404           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   9405       }
   9406     else
   9407       if ((x > (int) (windows->info.x+windows->info.width)) ||
   9408           (y > (int) (windows->info.y+windows->info.height)))
   9409         (void) XMapWindow(display,windows->info.id);
   9410     if (windows->info.mapped != MagickFalse )
   9411       {
   9412         /*
   9413           Display pointer position.
   9414         */
   9415         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
   9416           windows->magnify.x,windows->magnify.y);
   9417         XInfoWidget(display,windows,text);
   9418       }
   9419     /*
   9420       Wait for next event.
   9421     */
   9422     XScreenEvent(display,windows,event,exception);
   9423     switch (event->type)
   9424     {
   9425       case ButtonPress:
   9426         break;
   9427       case ButtonRelease:
   9428       {
   9429         /*
   9430           User has finished magnifying image.
   9431         */
   9432         x=event->xbutton.x;
   9433         y=event->xbutton.y;
   9434         state|=ExitState;
   9435         break;
   9436       }
   9437       case Expose:
   9438         break;
   9439       case MotionNotify:
   9440       {
   9441         x=event->xmotion.x;
   9442         y=event->xmotion.y;
   9443         break;
   9444       }
   9445       default:
   9446         break;
   9447     }
   9448     /*
   9449       Check boundary conditions.
   9450     */
   9451     if (x < 0)
   9452       x=0;
   9453     else
   9454       if (x >= (int) windows->image.width)
   9455         x=(int) windows->image.width-1;
   9456     if (y < 0)
   9457       y=0;
   9458     else
   9459      if (y >= (int) windows->image.height)
   9460        y=(int) windows->image.height-1;
   9461   } while ((state & ExitState) == 0);
   9462   /*
   9463     Display magnified image.
   9464   */
   9465   XSetCursorState(display,windows,MagickFalse);
   9466 }
   9467 
   9468 /*
   9470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   9471 %                                                                             %
   9472 %                                                                             %
   9473 %                                                                             %
   9474 +   X M a g n i f y W i n d o w C o m m a n d                                 %
   9475 %                                                                             %
   9476 %                                                                             %
   9477 %                                                                             %
   9478 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   9479 %
   9480 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
   9481 %  pixel as specified by the key symbol.
   9482 %
   9483 %  The format of the XMagnifyWindowCommand method is:
   9484 %
   9485 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
   9486 %        const MagickStatusType state,const KeySym key_symbol,
   9487 %        ExceptionInfo *exception)
   9488 %
   9489 %  A description of each parameter follows:
   9490 %
   9491 %    o display: Specifies a connection to an X server; returned from
   9492 %      XOpenDisplay.
   9493 %
   9494 %    o windows: Specifies a pointer to a XWindows structure.
   9495 %
   9496 %    o state: key mask.
   9497 %
   9498 %    o key_symbol: Specifies a KeySym which indicates which side of the image
   9499 %      to trim.
   9500 %
   9501 %    o exception: return any errors or warnings in this structure.
   9502 %
   9503 */
   9504 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
   9505   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
   9506 {
   9507   unsigned int
   9508     quantum;
   9509 
   9510   /*
   9511     User specified a magnify factor or position.
   9512   */
   9513   quantum=1;
   9514   if ((state & Mod1Mask) != 0)
   9515     quantum=10;
   9516   switch ((int) key_symbol)
   9517   {
   9518     case QuitCommand:
   9519     {
   9520       (void) XWithdrawWindow(display,windows->magnify.id,
   9521         windows->magnify.screen);
   9522       break;
   9523     }
   9524     case XK_Home:
   9525     case XK_KP_Home:
   9526     {
   9527       windows->magnify.x=(int) windows->image.width/2;
   9528       windows->magnify.y=(int) windows->image.height/2;
   9529       break;
   9530     }
   9531     case XK_Left:
   9532     case XK_KP_Left:
   9533     {
   9534       if (windows->magnify.x > 0)
   9535         windows->magnify.x-=quantum;
   9536       break;
   9537     }
   9538     case XK_Up:
   9539     case XK_KP_Up:
   9540     {
   9541       if (windows->magnify.y > 0)
   9542         windows->magnify.y-=quantum;
   9543       break;
   9544     }
   9545     case XK_Right:
   9546     case XK_KP_Right:
   9547     {
   9548       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
   9549         windows->magnify.x+=quantum;
   9550       break;
   9551     }
   9552     case XK_Down:
   9553     case XK_KP_Down:
   9554     {
   9555       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
   9556         windows->magnify.y+=quantum;
   9557       break;
   9558     }
   9559     case XK_0:
   9560     case XK_1:
   9561     case XK_2:
   9562     case XK_3:
   9563     case XK_4:
   9564     case XK_5:
   9565     case XK_6:
   9566     case XK_7:
   9567     case XK_8:
   9568     case XK_9:
   9569     {
   9570       windows->magnify.data=(key_symbol-XK_0);
   9571       break;
   9572     }
   9573     case XK_KP_0:
   9574     case XK_KP_1:
   9575     case XK_KP_2:
   9576     case XK_KP_3:
   9577     case XK_KP_4:
   9578     case XK_KP_5:
   9579     case XK_KP_6:
   9580     case XK_KP_7:
   9581     case XK_KP_8:
   9582     case XK_KP_9:
   9583     {
   9584       windows->magnify.data=(key_symbol-XK_KP_0);
   9585       break;
   9586     }
   9587     default:
   9588       break;
   9589   }
   9590   XMakeMagnifyImage(display,windows,exception);
   9591 }
   9592 
   9593 /*
   9595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   9596 %                                                                             %
   9597 %                                                                             %
   9598 %                                                                             %
   9599 +   X M a k e P a n I m a g e                                                 %
   9600 %                                                                             %
   9601 %                                                                             %
   9602 %                                                                             %
   9603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   9604 %
   9605 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
   9606 %  icon window.
   9607 %
   9608 %  The format of the XMakePanImage method is:
   9609 %
   9610 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
   9611 %          XWindows *windows,Image *image,ExceptionInfo *exception)
   9612 %
   9613 %  A description of each parameter follows:
   9614 %
   9615 %    o display: Specifies a connection to an X server;  returned from
   9616 %      XOpenDisplay.
   9617 %
   9618 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   9619 %
   9620 %    o windows: Specifies a pointer to a XWindows structure.
   9621 %
   9622 %    o image: the image.
   9623 %
   9624 %    o exception: return any errors or warnings in this structure.
   9625 %
   9626 */
   9627 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
   9628   XWindows *windows,Image *image,ExceptionInfo *exception)
   9629 {
   9630   MagickStatusType
   9631     status;
   9632 
   9633   /*
   9634     Create and display image for panning icon.
   9635   */
   9636   XSetCursorState(display,windows,MagickTrue);
   9637   XCheckRefreshWindows(display,windows);
   9638   windows->pan.x=(int) windows->image.x;
   9639   windows->pan.y=(int) windows->image.y;
   9640   status=XMakeImage(display,resource_info,&windows->pan,image,
   9641     windows->pan.width,windows->pan.height,exception);
   9642   if (status == MagickFalse)
   9643     ThrowXWindowException(ResourceLimitError,
   9644      "MemoryAllocationFailed",image->filename);
   9645   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
   9646     windows->pan.pixmap);
   9647   (void) XClearWindow(display,windows->pan.id);
   9648   XDrawPanRectangle(display,windows);
   9649   XSetCursorState(display,windows,MagickFalse);
   9650 }
   9651 
   9652 /*
   9654 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   9655 %                                                                             %
   9656 %                                                                             %
   9657 %                                                                             %
   9658 +   X M a t t a E d i t I m a g e                                             %
   9659 %                                                                             %
   9660 %                                                                             %
   9661 %                                                                             %
   9662 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   9663 %
   9664 %  XMatteEditImage() allows the user to interactively change the Matte channel
   9665 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
   9666 %  before the matte information is stored.
   9667 %
   9668 %  The format of the XMatteEditImage method is:
   9669 %
   9670 %      MagickBooleanType XMatteEditImage(Display *display,
   9671 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
   9672 %        ExceptionInfo *exception)
   9673 %
   9674 %  A description of each parameter follows:
   9675 %
   9676 %    o display: Specifies a connection to an X server;  returned from
   9677 %      XOpenDisplay.
   9678 %
   9679 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   9680 %
   9681 %    o windows: Specifies a pointer to a XWindows structure.
   9682 %
   9683 %    o image: the image; returned from ReadImage.
   9684 %
   9685 %    o exception: return any errors or warnings in this structure.
   9686 %
   9687 */
   9688 static MagickBooleanType XMatteEditImage(Display *display,
   9689   XResourceInfo *resource_info,XWindows *windows,Image **image,
   9690   ExceptionInfo *exception)
   9691 {
   9692   static char
   9693     matte[MagickPathExtent] = "0";
   9694 
   9695   static const char
   9696     *MatteEditMenu[] =
   9697     {
   9698       "Method",
   9699       "Border Color",
   9700       "Fuzz",
   9701       "Matte Value",
   9702       "Undo",
   9703       "Help",
   9704       "Dismiss",
   9705       (char *) NULL
   9706     };
   9707 
   9708   static const ModeType
   9709     MatteEditCommands[] =
   9710     {
   9711       MatteEditMethod,
   9712       MatteEditBorderCommand,
   9713       MatteEditFuzzCommand,
   9714       MatteEditValueCommand,
   9715       MatteEditUndoCommand,
   9716       MatteEditHelpCommand,
   9717       MatteEditDismissCommand
   9718     };
   9719 
   9720   static PaintMethod
   9721     method = PointMethod;
   9722 
   9723   static XColor
   9724     border_color = { 0, 0, 0, 0, 0, 0 };
   9725 
   9726   char
   9727     command[MagickPathExtent],
   9728     text[MagickPathExtent];
   9729 
   9730   Cursor
   9731     cursor;
   9732 
   9733   int
   9734     entry,
   9735     id,
   9736     x,
   9737     x_offset,
   9738     y,
   9739     y_offset;
   9740 
   9741   register int
   9742     i;
   9743 
   9744   register Quantum
   9745     *q;
   9746 
   9747   unsigned int
   9748     height,
   9749     width;
   9750 
   9751   size_t
   9752     state;
   9753 
   9754   XEvent
   9755     event;
   9756 
   9757   /*
   9758     Map Command widget.
   9759   */
   9760   (void) CloneString(&windows->command.name,"Matte Edit");
   9761   windows->command.data=4;
   9762   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
   9763   (void) XMapRaised(display,windows->command.id);
   9764   XClientMessage(display,windows->image.id,windows->im_protocols,
   9765     windows->im_update_widget,CurrentTime);
   9766   /*
   9767     Make cursor.
   9768   */
   9769   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
   9770     resource_info->background_color,resource_info->foreground_color);
   9771   (void) XCheckDefineCursor(display,windows->image.id,cursor);
   9772   /*
   9773     Track pointer until button 1 is pressed.
   9774   */
   9775   XQueryPosition(display,windows->image.id,&x,&y);
   9776   (void) XSelectInput(display,windows->image.id,
   9777     windows->image.attributes.event_mask | PointerMotionMask);
   9778   state=DefaultState;
   9779   do
   9780   {
   9781     if (windows->info.mapped != MagickFalse )
   9782       {
   9783         /*
   9784           Display pointer position.
   9785         */
   9786         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
   9787           x+windows->image.x,y+windows->image.y);
   9788         XInfoWidget(display,windows,text);
   9789       }
   9790     /*
   9791       Wait for next event.
   9792     */
   9793     XScreenEvent(display,windows,&event,exception);
   9794     if (event.xany.window == windows->command.id)
   9795       {
   9796         /*
   9797           Select a command from the Command widget.
   9798         */
   9799         id=XCommandWidget(display,windows,MatteEditMenu,&event);
   9800         if (id < 0)
   9801           {
   9802             (void) XCheckDefineCursor(display,windows->image.id,cursor);
   9803             continue;
   9804           }
   9805         switch (MatteEditCommands[id])
   9806         {
   9807           case MatteEditMethod:
   9808           {
   9809             char
   9810               **methods;
   9811 
   9812             /*
   9813               Select a method from the pop-up menu.
   9814             */
   9815             methods=GetCommandOptions(MagickMethodOptions);
   9816             if (methods == (char **) NULL)
   9817               break;
   9818             entry=XMenuWidget(display,windows,MatteEditMenu[id],
   9819               (const char **) methods,command);
   9820             if (entry >= 0)
   9821               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
   9822                 MagickFalse,methods[entry]);
   9823             methods=DestroyStringList(methods);
   9824             break;
   9825           }
   9826           case MatteEditBorderCommand:
   9827           {
   9828             const char
   9829               *ColorMenu[MaxNumberPens];
   9830 
   9831             int
   9832               pen_number;
   9833 
   9834             /*
   9835               Initialize menu selections.
   9836             */
   9837             for (i=0; i < (int) (MaxNumberPens-2); i++)
   9838               ColorMenu[i]=resource_info->pen_colors[i];
   9839             ColorMenu[MaxNumberPens-2]="Browser...";
   9840             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
   9841             /*
   9842               Select a pen color from the pop-up menu.
   9843             */
   9844             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
   9845               (const char **) ColorMenu,command);
   9846             if (pen_number < 0)
   9847               break;
   9848             if (pen_number == (MaxNumberPens-2))
   9849               {
   9850                 static char
   9851                   color_name[MagickPathExtent] = "gray";
   9852 
   9853                 /*
   9854                   Select a pen color from a dialog.
   9855                 */
   9856                 resource_info->pen_colors[pen_number]=color_name;
   9857                 XColorBrowserWidget(display,windows,"Select",color_name);
   9858                 if (*color_name == '\0')
   9859                   break;
   9860               }
   9861             /*
   9862               Set border color.
   9863             */
   9864             (void) XParseColor(display,windows->map_info->colormap,
   9865               resource_info->pen_colors[pen_number],&border_color);
   9866             break;
   9867           }
   9868           case MatteEditFuzzCommand:
   9869           {
   9870             static char
   9871               fuzz[MagickPathExtent];
   9872 
   9873             static const char
   9874               *FuzzMenu[] =
   9875               {
   9876                 "0%",
   9877                 "2%",
   9878                 "5%",
   9879                 "10%",
   9880                 "15%",
   9881                 "Dialog...",
   9882                 (char *) NULL,
   9883               };
   9884 
   9885             /*
   9886               Select a command from the pop-up menu.
   9887             */
   9888             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
   9889               command);
   9890             if (entry < 0)
   9891               break;
   9892             if (entry != 5)
   9893               {
   9894                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
   9895                   QuantumRange+1.0);
   9896                 break;
   9897               }
   9898             (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
   9899             (void) XDialogWidget(display,windows,"Ok",
   9900               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
   9901             if (*fuzz == '\0')
   9902               break;
   9903             (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
   9904             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
   9905               1.0);
   9906             break;
   9907           }
   9908           case MatteEditValueCommand:
   9909           {
   9910             static char
   9911               message[MagickPathExtent];
   9912 
   9913             static const char
   9914               *MatteMenu[] =
   9915               {
   9916                 "Opaque",
   9917                 "Transparent",
   9918                 "Dialog...",
   9919                 (char *) NULL,
   9920               };
   9921 
   9922             /*
   9923               Select a command from the pop-up menu.
   9924             */
   9925             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
   9926               command);
   9927             if (entry < 0)
   9928               break;
   9929             if (entry != 2)
   9930               {
   9931                 (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
   9932                   OpaqueAlpha);
   9933                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
   9934                   (void) FormatLocaleString(matte,MagickPathExtent,
   9935                     QuantumFormat,(Quantum) TransparentAlpha);
   9936                 break;
   9937               }
   9938             (void) FormatLocaleString(message,MagickPathExtent,
   9939               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
   9940               QuantumRange);
   9941             (void) XDialogWidget(display,windows,"Matte",message,matte);
   9942             if (*matte == '\0')
   9943               break;
   9944             break;
   9945           }
   9946           case MatteEditUndoCommand:
   9947           {
   9948             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
   9949               image,exception);
   9950             break;
   9951           }
   9952           case MatteEditHelpCommand:
   9953           {
   9954             XTextViewWidget(display,resource_info,windows,MagickFalse,
   9955               "Help Viewer - Matte Edit",ImageMatteEditHelp);
   9956             break;
   9957           }
   9958           case MatteEditDismissCommand:
   9959           {
   9960             /*
   9961               Prematurely exit.
   9962             */
   9963             state|=EscapeState;
   9964             state|=ExitState;
   9965             break;
   9966           }
   9967           default:
   9968             break;
   9969         }
   9970         (void) XCheckDefineCursor(display,windows->image.id,cursor);
   9971         continue;
   9972       }
   9973     switch (event.type)
   9974     {
   9975       case ButtonPress:
   9976       {
   9977         if (event.xbutton.button != Button1)
   9978           break;
   9979         if ((event.xbutton.window != windows->image.id) &&
   9980             (event.xbutton.window != windows->magnify.id))
   9981           break;
   9982         /*
   9983           Update matte data.
   9984         */
   9985         x=event.xbutton.x;
   9986         y=event.xbutton.y;
   9987         (void) XMagickCommand(display,resource_info,windows,
   9988           SaveToUndoBufferCommand,image,exception);
   9989         state|=UpdateConfigurationState;
   9990         break;
   9991       }
   9992       case ButtonRelease:
   9993       {
   9994         if (event.xbutton.button != Button1)
   9995           break;
   9996         if ((event.xbutton.window != windows->image.id) &&
   9997             (event.xbutton.window != windows->magnify.id))
   9998           break;
   9999         /*
   10000           Update colormap information.
   10001         */
   10002         x=event.xbutton.x;
   10003         y=event.xbutton.y;
   10004         XConfigureImageColormap(display,resource_info,windows,*image,exception);
   10005         (void) XConfigureImage(display,resource_info,windows,*image,exception);
   10006         XInfoWidget(display,windows,text);
   10007         (void) XCheckDefineCursor(display,windows->image.id,cursor);
   10008         state&=(~UpdateConfigurationState);
   10009         break;
   10010       }
   10011       case Expose:
   10012         break;
   10013       case KeyPress:
   10014       {
   10015         char
   10016           command[MagickPathExtent];
   10017 
   10018         KeySym
   10019           key_symbol;
   10020 
   10021         if (event.xkey.window == windows->magnify.id)
   10022           {
   10023             Window
   10024               window;
   10025 
   10026             window=windows->magnify.id;
   10027             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
   10028           }
   10029         if (event.xkey.window != windows->image.id)
   10030           break;
   10031         /*
   10032           Respond to a user key press.
   10033         */
   10034         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   10035           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   10036         switch ((int) key_symbol)
   10037         {
   10038           case XK_Escape:
   10039           case XK_F20:
   10040           {
   10041             /*
   10042               Prematurely exit.
   10043             */
   10044             state|=ExitState;
   10045             break;
   10046           }
   10047           case XK_F1:
   10048           case XK_Help:
   10049           {
   10050             XTextViewWidget(display,resource_info,windows,MagickFalse,
   10051               "Help Viewer - Matte Edit",ImageMatteEditHelp);
   10052             break;
   10053           }
   10054           default:
   10055           {
   10056             (void) XBell(display,0);
   10057             break;
   10058           }
   10059         }
   10060         break;
   10061       }
   10062       case MotionNotify:
   10063       {
   10064         /*
   10065           Map and unmap Info widget as cursor crosses its boundaries.
   10066         */
   10067         x=event.xmotion.x;
   10068         y=event.xmotion.y;
   10069         if (windows->info.mapped != MagickFalse )
   10070           {
   10071             if ((x < (int) (windows->info.x+windows->info.width)) &&
   10072                 (y < (int) (windows->info.y+windows->info.height)))
   10073               (void) XWithdrawWindow(display,windows->info.id,
   10074                 windows->info.screen);
   10075           }
   10076         else
   10077           if ((x > (int) (windows->info.x+windows->info.width)) ||
   10078               (y > (int) (windows->info.y+windows->info.height)))
   10079             (void) XMapWindow(display,windows->info.id);
   10080         break;
   10081       }
   10082       default:
   10083         break;
   10084     }
   10085     if (event.xany.window == windows->magnify.id)
   10086       {
   10087         x=windows->magnify.x-windows->image.x;
   10088         y=windows->magnify.y-windows->image.y;
   10089       }
   10090     x_offset=x;
   10091     y_offset=y;
   10092     if ((state & UpdateConfigurationState) != 0)
   10093       {
   10094         CacheView
   10095           *image_view;
   10096 
   10097         int
   10098           x,
   10099           y;
   10100 
   10101         /*
   10102           Matte edit is relative to image configuration.
   10103         */
   10104         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
   10105           MagickTrue);
   10106         XPutPixel(windows->image.ximage,x_offset,y_offset,
   10107           windows->pixel_info->background_color.pixel);
   10108         width=(unsigned int) (*image)->columns;
   10109         height=(unsigned int) (*image)->rows;
   10110         x=0;
   10111         y=0;
   10112         if (windows->image.crop_geometry != (char *) NULL)
   10113           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
   10114             &height);
   10115         x_offset=(int) (width*(windows->image.x+x_offset)/
   10116           windows->image.ximage->width+x);
   10117         y_offset=(int) (height*(windows->image.y+y_offset)/
   10118           windows->image.ximage->height+y);
   10119         if ((x_offset < 0) || (y_offset < 0))
   10120           continue;
   10121         if ((x_offset >= (int) (*image)->columns) ||
   10122             (y_offset >= (int) (*image)->rows))
   10123           continue;
   10124         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
   10125           return(MagickFalse);
   10126         if ((*image)->alpha_trait == UndefinedPixelTrait)
   10127           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
   10128         image_view=AcquireAuthenticCacheView(*image,exception);
   10129         switch (method)
   10130         {
   10131           case PointMethod:
   10132           default:
   10133           {
   10134             /*
   10135               Update matte information using point algorithm.
   10136             */
   10137             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
   10138               (ssize_t) y_offset,1,1,exception);
   10139             if (q == (Quantum *) NULL)
   10140               break;
   10141             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
   10142             (void) SyncCacheViewAuthenticPixels(image_view,exception);
   10143             break;
   10144           }
   10145           case ReplaceMethod:
   10146           {
   10147             PixelInfo
   10148               pixel,
   10149               target;
   10150 
   10151             /*
   10152               Update matte information using replace algorithm.
   10153             */
   10154             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
   10155               x_offset,(ssize_t) y_offset,&target,exception);
   10156             for (y=0; y < (int) (*image)->rows; y++)
   10157             {
   10158               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
   10159                 (*image)->columns,1,exception);
   10160               if (q == (Quantum *) NULL)
   10161                 break;
   10162               for (x=0; x < (int) (*image)->columns; x++)
   10163               {
   10164                 GetPixelInfoPixel(*image,q,&pixel);
   10165                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
   10166                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
   10167                 q+=GetPixelChannels(*image);
   10168               }
   10169               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   10170                 break;
   10171             }
   10172             break;
   10173           }
   10174           case FloodfillMethod:
   10175           case FillToBorderMethod:
   10176           {
   10177             ChannelType
   10178               channel_mask;
   10179 
   10180             DrawInfo
   10181               *draw_info;
   10182 
   10183             PixelInfo
   10184               target;
   10185 
   10186             /*
   10187               Update matte information using floodfill algorithm.
   10188             */
   10189             (void) GetOneVirtualPixelInfo(*image,
   10190               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
   10191               y_offset,&target,exception);
   10192             if (method == FillToBorderMethod)
   10193               {
   10194                 target.red=(double) ScaleShortToQuantum(
   10195                   border_color.red);
   10196                 target.green=(double) ScaleShortToQuantum(
   10197                   border_color.green);
   10198                 target.blue=(double) ScaleShortToQuantum(
   10199                   border_color.blue);
   10200               }
   10201             draw_info=CloneDrawInfo(resource_info->image_info,
   10202               (DrawInfo *) NULL);
   10203             draw_info->fill.alpha=(double) ClampToQuantum(
   10204               StringToDouble(matte,(char **) NULL));
   10205             channel_mask=SetImageChannelMask(*image,AlphaChannel);
   10206             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
   10207               x_offset,(ssize_t) y_offset,
   10208               method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
   10209             (void) SetPixelChannelMask(*image,channel_mask);
   10210             draw_info=DestroyDrawInfo(draw_info);
   10211             break;
   10212           }
   10213           case ResetMethod:
   10214           {
   10215             /*
   10216               Update matte information using reset algorithm.
   10217             */
   10218             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
   10219               return(MagickFalse);
   10220             for (y=0; y < (int) (*image)->rows; y++)
   10221             {
   10222               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
   10223                 (*image)->columns,1,exception);
   10224               if (q == (Quantum *) NULL)
   10225                 break;
   10226               for (x=0; x < (int) (*image)->columns; x++)
   10227               {
   10228                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
   10229                 q+=GetPixelChannels(*image);
   10230               }
   10231               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   10232                 break;
   10233             }
   10234             if (StringToLong(matte) == (long) OpaqueAlpha)
   10235               (*image)->alpha_trait=UndefinedPixelTrait;
   10236             break;
   10237           }
   10238         }
   10239         image_view=DestroyCacheView(image_view);
   10240         state&=(~UpdateConfigurationState);
   10241       }
   10242   } while ((state & ExitState) == 0);
   10243   (void) XSelectInput(display,windows->image.id,
   10244     windows->image.attributes.event_mask);
   10245   XSetCursorState(display,windows,MagickFalse);
   10246   (void) XFreeCursor(display,cursor);
   10247   return(MagickTrue);
   10248 }
   10249 
   10250 /*
   10252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   10253 %                                                                             %
   10254 %                                                                             %
   10255 %                                                                             %
   10256 +   X O p e n I m a g e                                                       %
   10257 %                                                                             %
   10258 %                                                                             %
   10259 %                                                                             %
   10260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   10261 %
   10262 %  XOpenImage() loads an image from a file.
   10263 %
   10264 %  The format of the XOpenImage method is:
   10265 %
   10266 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
   10267 %       XWindows *windows,const unsigned int command)
   10268 %
   10269 %  A description of each parameter follows:
   10270 %
   10271 %    o display: Specifies a connection to an X server; returned from
   10272 %      XOpenDisplay.
   10273 %
   10274 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   10275 %
   10276 %    o windows: Specifies a pointer to a XWindows structure.
   10277 %
   10278 %    o command: A value other than zero indicates that the file is selected
   10279 %      from the command line argument list.
   10280 %
   10281 */
   10282 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
   10283   XWindows *windows,const MagickBooleanType command)
   10284 {
   10285   const MagickInfo
   10286     *magick_info;
   10287 
   10288   ExceptionInfo
   10289     *exception;
   10290 
   10291   Image
   10292     *nexus;
   10293 
   10294   ImageInfo
   10295     *image_info;
   10296 
   10297   static char
   10298     filename[MagickPathExtent] = "\0";
   10299 
   10300   /*
   10301     Request file name from user.
   10302   */
   10303   if (command == MagickFalse)
   10304     XFileBrowserWidget(display,windows,"Open",filename);
   10305   else
   10306     {
   10307       char
   10308         **filelist,
   10309         **files;
   10310 
   10311       int
   10312         count,
   10313         status;
   10314 
   10315       register int
   10316         i,
   10317         j;
   10318 
   10319       /*
   10320         Select next image from the command line.
   10321       */
   10322       status=XGetCommand(display,windows->image.id,&files,&count);
   10323       if (status == 0)
   10324         {
   10325           ThrowXWindowException(XServerError,"UnableToGetProperty","...");
   10326           return((Image *) NULL);
   10327         }
   10328       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
   10329       if (filelist == (char **) NULL)
   10330         {
   10331           ThrowXWindowException(ResourceLimitError,
   10332             "MemoryAllocationFailed","...");
   10333           (void) XFreeStringList(files);
   10334           return((Image *) NULL);
   10335         }
   10336       j=0;
   10337       for (i=1; i < count; i++)
   10338         if (*files[i] != '-')
   10339           filelist[j++]=files[i];
   10340       filelist[j]=(char *) NULL;
   10341       XListBrowserWidget(display,windows,&windows->widget,
   10342         (const char **) filelist,"Load","Select Image to Load:",filename);
   10343       filelist=(char **) RelinquishMagickMemory(filelist);
   10344       (void) XFreeStringList(files);
   10345     }
   10346   if (*filename == '\0')
   10347     return((Image *) NULL);
   10348   image_info=CloneImageInfo(resource_info->image_info);
   10349   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
   10350     (void *) NULL);
   10351   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
   10352   exception=AcquireExceptionInfo();
   10353   (void) SetImageInfo(image_info,0,exception);
   10354   if (LocaleCompare(image_info->magick,"X") == 0)
   10355     {
   10356       char
   10357         seconds[MagickPathExtent];
   10358 
   10359       /*
   10360         User may want to delay the X server screen grab.
   10361       */
   10362       (void) CopyMagickString(seconds,"0",MagickPathExtent);
   10363       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
   10364         seconds);
   10365       if (*seconds == '\0')
   10366         return((Image *) NULL);
   10367       XDelay(display,(size_t) (1000*StringToLong(seconds)));
   10368     }
   10369   magick_info=GetMagickInfo(image_info->magick,exception);
   10370   if ((magick_info != (const MagickInfo *) NULL) &&
   10371       GetMagickRawSupport(magick_info) == MagickTrue)
   10372     {
   10373       char
   10374         geometry[MagickPathExtent];
   10375 
   10376       /*
   10377         Request image size from the user.
   10378       */
   10379       (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
   10380       if (image_info->size != (char *) NULL)
   10381         (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
   10382       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
   10383         geometry);
   10384       (void) CloneString(&image_info->size,geometry);
   10385     }
   10386   /*
   10387     Load the image.
   10388   */
   10389   XSetCursorState(display,windows,MagickTrue);
   10390   XCheckRefreshWindows(display,windows);
   10391   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
   10392   nexus=ReadImage(image_info,exception);
   10393   CatchException(exception);
   10394   XSetCursorState(display,windows,MagickFalse);
   10395   if (nexus != (Image *) NULL)
   10396     XClientMessage(display,windows->image.id,windows->im_protocols,
   10397       windows->im_next_image,CurrentTime);
   10398   else
   10399     {
   10400       char
   10401         *text,
   10402         **textlist;
   10403 
   10404       /*
   10405         Unknown image format.
   10406       */
   10407       text=FileToString(filename,~0UL,exception);
   10408       if (text == (char *) NULL)
   10409         return((Image *) NULL);
   10410       textlist=StringToList(text);
   10411       if (textlist != (char **) NULL)
   10412         {
   10413           char
   10414             title[MagickPathExtent];
   10415 
   10416           register int
   10417             i;
   10418 
   10419           (void) FormatLocaleString(title,MagickPathExtent,
   10420             "Unknown format: %s",filename);
   10421           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
   10422             (const char **) textlist);
   10423           for (i=0; textlist[i] != (char *) NULL; i++)
   10424             textlist[i]=DestroyString(textlist[i]);
   10425           textlist=(char **) RelinquishMagickMemory(textlist);
   10426         }
   10427       text=DestroyString(text);
   10428     }
   10429   exception=DestroyExceptionInfo(exception);
   10430   image_info=DestroyImageInfo(image_info);
   10431   return(nexus);
   10432 }
   10433 
   10434 /*
   10436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   10437 %                                                                             %
   10438 %                                                                             %
   10439 %                                                                             %
   10440 +   X P a n I m a g e                                                         %
   10441 %                                                                             %
   10442 %                                                                             %
   10443 %                                                                             %
   10444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   10445 %
   10446 %  XPanImage() pans the image until the mouse button is released.
   10447 %
   10448 %  The format of the XPanImage method is:
   10449 %
   10450 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
   10451 %        ExceptionInfo *exception)
   10452 %
   10453 %  A description of each parameter follows:
   10454 %
   10455 %    o display: Specifies a connection to an X server;  returned from
   10456 %      XOpenDisplay.
   10457 %
   10458 %    o windows: Specifies a pointer to a XWindows structure.
   10459 %
   10460 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
   10461 %      the entire image is refreshed.
   10462 %
   10463 %    o exception: return any errors or warnings in this structure.
   10464 %
   10465 */
   10466 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
   10467   ExceptionInfo *exception)
   10468 {
   10469   char
   10470     text[MagickPathExtent];
   10471 
   10472   Cursor
   10473     cursor;
   10474 
   10475   double
   10476     x_factor,
   10477     y_factor;
   10478 
   10479   RectangleInfo
   10480     pan_info;
   10481 
   10482   size_t
   10483     state;
   10484 
   10485   /*
   10486     Define cursor.
   10487   */
   10488   if ((windows->image.ximage->width > (int) windows->image.width) &&
   10489       (windows->image.ximage->height > (int) windows->image.height))
   10490     cursor=XCreateFontCursor(display,XC_fleur);
   10491   else
   10492     if (windows->image.ximage->width > (int) windows->image.width)
   10493       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
   10494     else
   10495       if (windows->image.ximage->height > (int) windows->image.height)
   10496         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
   10497       else
   10498         cursor=XCreateFontCursor(display,XC_arrow);
   10499   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
   10500   /*
   10501     Pan image as pointer moves until the mouse button is released.
   10502   */
   10503   x_factor=(double) windows->image.ximage->width/windows->pan.width;
   10504   y_factor=(double) windows->image.ximage->height/windows->pan.height;
   10505   pan_info.width=windows->pan.width*windows->image.width/
   10506     windows->image.ximage->width;
   10507   pan_info.height=windows->pan.height*windows->image.height/
   10508     windows->image.ximage->height;
   10509   pan_info.x=0;
   10510   pan_info.y=0;
   10511   state=UpdateConfigurationState;
   10512   do
   10513   {
   10514     switch (event->type)
   10515     {
   10516       case ButtonPress:
   10517       {
   10518         /*
   10519           User choose an initial pan location.
   10520         */
   10521         pan_info.x=(ssize_t) event->xbutton.x;
   10522         pan_info.y=(ssize_t) event->xbutton.y;
   10523         state|=UpdateConfigurationState;
   10524         break;
   10525       }
   10526       case ButtonRelease:
   10527       {
   10528         /*
   10529           User has finished panning the image.
   10530         */
   10531         pan_info.x=(ssize_t) event->xbutton.x;
   10532         pan_info.y=(ssize_t) event->xbutton.y;
   10533         state|=UpdateConfigurationState | ExitState;
   10534         break;
   10535       }
   10536       case MotionNotify:
   10537       {
   10538         pan_info.x=(ssize_t) event->xmotion.x;
   10539         pan_info.y=(ssize_t) event->xmotion.y;
   10540         state|=UpdateConfigurationState;
   10541       }
   10542       default:
   10543         break;
   10544     }
   10545     if ((state & UpdateConfigurationState) != 0)
   10546       {
   10547         /*
   10548           Check boundary conditions.
   10549         */
   10550         if (pan_info.x < (ssize_t) (pan_info.width/2))
   10551           pan_info.x=0;
   10552         else
   10553           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
   10554         if (pan_info.x < 0)
   10555           pan_info.x=0;
   10556         else
   10557           if ((int) (pan_info.x+windows->image.width) >
   10558               windows->image.ximage->width)
   10559             pan_info.x=(ssize_t)
   10560               (windows->image.ximage->width-windows->image.width);
   10561         if (pan_info.y < (ssize_t) (pan_info.height/2))
   10562           pan_info.y=0;
   10563         else
   10564           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
   10565         if (pan_info.y < 0)
   10566           pan_info.y=0;
   10567         else
   10568           if ((int) (pan_info.y+windows->image.height) >
   10569               windows->image.ximage->height)
   10570             pan_info.y=(ssize_t)
   10571               (windows->image.ximage->height-windows->image.height);
   10572         if ((windows->image.x != (int) pan_info.x) ||
   10573             (windows->image.y != (int) pan_info.y))
   10574           {
   10575             /*
   10576               Display image pan offset.
   10577             */
   10578             windows->image.x=(int) pan_info.x;
   10579             windows->image.y=(int) pan_info.y;
   10580             (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
   10581               windows->image.width,windows->image.height,windows->image.x,
   10582               windows->image.y);
   10583             XInfoWidget(display,windows,text);
   10584             /*
   10585               Refresh Image window.
   10586             */
   10587             XDrawPanRectangle(display,windows);
   10588             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
   10589           }
   10590         state&=(~UpdateConfigurationState);
   10591       }
   10592     /*
   10593       Wait for next event.
   10594     */
   10595     if ((state & ExitState) == 0)
   10596       XScreenEvent(display,windows,event,exception);
   10597   } while ((state & ExitState) == 0);
   10598   /*
   10599     Restore cursor.
   10600   */
   10601   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
   10602   (void) XFreeCursor(display,cursor);
   10603   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   10604 }
   10605 
   10606 /*
   10608 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   10609 %                                                                             %
   10610 %                                                                             %
   10611 %                                                                             %
   10612 +   X P a s t e I m a g e                                                     %
   10613 %                                                                             %
   10614 %                                                                             %
   10615 %                                                                             %
   10616 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   10617 %
   10618 %  XPasteImage() pastes an image previously saved with XCropImage in the X
   10619 %  window image at a location the user chooses with the pointer.
   10620 %
   10621 %  The format of the XPasteImage method is:
   10622 %
   10623 %      MagickBooleanType XPasteImage(Display *display,
   10624 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
   10625 %        ExceptionInfo *exception)
   10626 %
   10627 %  A description of each parameter follows:
   10628 %
   10629 %    o display: Specifies a connection to an X server;  returned from
   10630 %      XOpenDisplay.
   10631 %
   10632 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   10633 %
   10634 %    o windows: Specifies a pointer to a XWindows structure.
   10635 %
   10636 %    o image: the image; returned from ReadImage.
   10637 %
   10638 %    o exception: return any errors or warnings in this structure.
   10639 %
   10640 */
   10641 static MagickBooleanType XPasteImage(Display *display,
   10642   XResourceInfo *resource_info,XWindows *windows,Image *image,
   10643   ExceptionInfo *exception)
   10644 {
   10645   static const char
   10646     *PasteMenu[] =
   10647     {
   10648       "Operator",
   10649       "Help",
   10650       "Dismiss",
   10651       (char *) NULL
   10652     };
   10653 
   10654   static const ModeType
   10655     PasteCommands[] =
   10656     {
   10657       PasteOperatorsCommand,
   10658       PasteHelpCommand,
   10659       PasteDismissCommand
   10660     };
   10661 
   10662   static CompositeOperator
   10663     compose = CopyCompositeOp;
   10664 
   10665   char
   10666     text[MagickPathExtent];
   10667 
   10668   Cursor
   10669     cursor;
   10670 
   10671   Image
   10672     *paste_image;
   10673 
   10674   int
   10675     entry,
   10676     id,
   10677     x,
   10678     y;
   10679 
   10680   double
   10681     scale_factor;
   10682 
   10683   RectangleInfo
   10684     highlight_info,
   10685     paste_info;
   10686 
   10687   unsigned int
   10688     height,
   10689     width;
   10690 
   10691   size_t
   10692     state;
   10693 
   10694   XEvent
   10695     event;
   10696 
   10697   /*
   10698     Copy image.
   10699   */
   10700   if (resource_info->copy_image == (Image *) NULL)
   10701     return(MagickFalse);
   10702   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
   10703   if (paste_image == (Image *) NULL)
   10704     return(MagickFalse);
   10705   /*
   10706     Map Command widget.
   10707   */
   10708   (void) CloneString(&windows->command.name,"Paste");
   10709   windows->command.data=1;
   10710   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
   10711   (void) XMapRaised(display,windows->command.id);
   10712   XClientMessage(display,windows->image.id,windows->im_protocols,
   10713     windows->im_update_widget,CurrentTime);
   10714   /*
   10715     Track pointer until button 1 is pressed.
   10716   */
   10717   XSetCursorState(display,windows,MagickFalse);
   10718   XQueryPosition(display,windows->image.id,&x,&y);
   10719   (void) XSelectInput(display,windows->image.id,
   10720     windows->image.attributes.event_mask | PointerMotionMask);
   10721   paste_info.x=(ssize_t) windows->image.x+x;
   10722   paste_info.y=(ssize_t) windows->image.y+y;
   10723   paste_info.width=0;
   10724   paste_info.height=0;
   10725   cursor=XCreateFontCursor(display,XC_ul_angle);
   10726   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
   10727   state=DefaultState;
   10728   do
   10729   {
   10730     if (windows->info.mapped != MagickFalse )
   10731       {
   10732         /*
   10733           Display pointer position.
   10734         */
   10735         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
   10736           (long) paste_info.x,(long) paste_info.y);
   10737         XInfoWidget(display,windows,text);
   10738       }
   10739     highlight_info=paste_info;
   10740     highlight_info.x=paste_info.x-windows->image.x;
   10741     highlight_info.y=paste_info.y-windows->image.y;
   10742     XHighlightRectangle(display,windows->image.id,
   10743       windows->image.highlight_context,&highlight_info);
   10744     /*
   10745       Wait for next event.
   10746     */
   10747     XScreenEvent(display,windows,&event,exception);
   10748     XHighlightRectangle(display,windows->image.id,
   10749       windows->image.highlight_context,&highlight_info);
   10750     if (event.xany.window == windows->command.id)
   10751       {
   10752         /*
   10753           Select a command from the Command widget.
   10754         */
   10755         id=XCommandWidget(display,windows,PasteMenu,&event);
   10756         if (id < 0)
   10757           continue;
   10758         switch (PasteCommands[id])
   10759         {
   10760           case PasteOperatorsCommand:
   10761           {
   10762             char
   10763               command[MagickPathExtent],
   10764               **operators;
   10765 
   10766             /*
   10767               Select a command from the pop-up menu.
   10768             */
   10769             operators=GetCommandOptions(MagickComposeOptions);
   10770             if (operators == (char **) NULL)
   10771               break;
   10772             entry=XMenuWidget(display,windows,PasteMenu[id],
   10773               (const char **) operators,command);
   10774             if (entry >= 0)
   10775               compose=(CompositeOperator) ParseCommandOption(
   10776                 MagickComposeOptions,MagickFalse,operators[entry]);
   10777             operators=DestroyStringList(operators);
   10778             break;
   10779           }
   10780           case PasteHelpCommand:
   10781           {
   10782             XTextViewWidget(display,resource_info,windows,MagickFalse,
   10783               "Help Viewer - Image Composite",ImagePasteHelp);
   10784             break;
   10785           }
   10786           case PasteDismissCommand:
   10787           {
   10788             /*
   10789               Prematurely exit.
   10790             */
   10791             state|=EscapeState;
   10792             state|=ExitState;
   10793             break;
   10794           }
   10795           default:
   10796             break;
   10797         }
   10798         continue;
   10799       }
   10800     switch (event.type)
   10801     {
   10802       case ButtonPress:
   10803       {
   10804         if (image->debug != MagickFalse )
   10805           (void) LogMagickEvent(X11Event,GetMagickModule(),
   10806             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
   10807             event.xbutton.button,event.xbutton.x,event.xbutton.y);
   10808         if (event.xbutton.button != Button1)
   10809           break;
   10810         if (event.xbutton.window != windows->image.id)
   10811           break;
   10812         /*
   10813           Paste rectangle is relative to image configuration.
   10814         */
   10815         width=(unsigned int) image->columns;
   10816         height=(unsigned int) image->rows;
   10817         x=0;
   10818         y=0;
   10819         if (windows->image.crop_geometry != (char *) NULL)
   10820           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
   10821             &width,&height);
   10822         scale_factor=(double) windows->image.ximage->width/width;
   10823         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
   10824         scale_factor=(double) windows->image.ximage->height/height;
   10825         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
   10826         (void) XCheckDefineCursor(display,windows->image.id,cursor);
   10827         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
   10828         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
   10829         break;
   10830       }
   10831       case ButtonRelease:
   10832       {
   10833         if (image->debug != MagickFalse )
   10834           (void) LogMagickEvent(X11Event,GetMagickModule(),
   10835             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
   10836             event.xbutton.button,event.xbutton.x,event.xbutton.y);
   10837         if (event.xbutton.button != Button1)
   10838           break;
   10839         if (event.xbutton.window != windows->image.id)
   10840           break;
   10841         if ((paste_info.width != 0) && (paste_info.height != 0))
   10842           {
   10843             /*
   10844               User has selected the location of the paste image.
   10845             */
   10846             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
   10847             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
   10848             state|=ExitState;
   10849           }
   10850         break;
   10851       }
   10852       case Expose:
   10853         break;
   10854       case KeyPress:
   10855       {
   10856         char
   10857           command[MagickPathExtent];
   10858 
   10859         KeySym
   10860           key_symbol;
   10861 
   10862         int
   10863           length;
   10864 
   10865         if (event.xkey.window != windows->image.id)
   10866           break;
   10867         /*
   10868           Respond to a user key press.
   10869         */
   10870         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
   10871           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   10872         *(command+length)='\0';
   10873         if (image->debug != MagickFalse )
   10874           (void) LogMagickEvent(X11Event,GetMagickModule(),
   10875             "Key press: 0x%lx (%s)",(long) key_symbol,command);
   10876         switch ((int) key_symbol)
   10877         {
   10878           case XK_Escape:
   10879           case XK_F20:
   10880           {
   10881             /*
   10882               Prematurely exit.
   10883             */
   10884             paste_image=DestroyImage(paste_image);
   10885             state|=EscapeState;
   10886             state|=ExitState;
   10887             break;
   10888           }
   10889           case XK_F1:
   10890           case XK_Help:
   10891           {
   10892             (void) XSetFunction(display,windows->image.highlight_context,
   10893               GXcopy);
   10894             XTextViewWidget(display,resource_info,windows,MagickFalse,
   10895               "Help Viewer - Image Composite",ImagePasteHelp);
   10896             (void) XSetFunction(display,windows->image.highlight_context,
   10897               GXinvert);
   10898             break;
   10899           }
   10900           default:
   10901           {
   10902             (void) XBell(display,0);
   10903             break;
   10904           }
   10905         }
   10906         break;
   10907       }
   10908       case MotionNotify:
   10909       {
   10910         /*
   10911           Map and unmap Info widget as text cursor crosses its boundaries.
   10912         */
   10913         x=event.xmotion.x;
   10914         y=event.xmotion.y;
   10915         if (windows->info.mapped != MagickFalse )
   10916           {
   10917             if ((x < (int) (windows->info.x+windows->info.width)) &&
   10918                 (y < (int) (windows->info.y+windows->info.height)))
   10919               (void) XWithdrawWindow(display,windows->info.id,
   10920                 windows->info.screen);
   10921           }
   10922         else
   10923           if ((x > (int) (windows->info.x+windows->info.width)) ||
   10924               (y > (int) (windows->info.y+windows->info.height)))
   10925             (void) XMapWindow(display,windows->info.id);
   10926         paste_info.x=(ssize_t) windows->image.x+x;
   10927         paste_info.y=(ssize_t) windows->image.y+y;
   10928         break;
   10929       }
   10930       default:
   10931       {
   10932         if (image->debug != MagickFalse )
   10933           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
   10934             event.type);
   10935         break;
   10936       }
   10937     }
   10938   } while ((state & ExitState) == 0);
   10939   (void) XSelectInput(display,windows->image.id,
   10940     windows->image.attributes.event_mask);
   10941   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
   10942   XSetCursorState(display,windows,MagickFalse);
   10943   (void) XFreeCursor(display,cursor);
   10944   if ((state & EscapeState) != 0)
   10945     return(MagickTrue);
   10946   /*
   10947     Image pasting is relative to image configuration.
   10948   */
   10949   XSetCursorState(display,windows,MagickTrue);
   10950   XCheckRefreshWindows(display,windows);
   10951   width=(unsigned int) image->columns;
   10952   height=(unsigned int) image->rows;
   10953   x=0;
   10954   y=0;
   10955   if (windows->image.crop_geometry != (char *) NULL)
   10956     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
   10957   scale_factor=(double) width/windows->image.ximage->width;
   10958   paste_info.x+=x;
   10959   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
   10960   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
   10961   scale_factor=(double) height/windows->image.ximage->height;
   10962   paste_info.y+=y;
   10963   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
   10964   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
   10965   /*
   10966     Paste image with X Image window.
   10967   */
   10968   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
   10969     paste_info.y,exception);
   10970   paste_image=DestroyImage(paste_image);
   10971   XSetCursorState(display,windows,MagickFalse);
   10972   /*
   10973     Update image colormap.
   10974   */
   10975   XConfigureImageColormap(display,resource_info,windows,image,exception);
   10976   (void) XConfigureImage(display,resource_info,windows,image,exception);
   10977   return(MagickTrue);
   10978 }
   10979 
   10980 /*
   10982 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   10983 %                                                                             %
   10984 %                                                                             %
   10985 %                                                                             %
   10986 +   X P r i n t I m a g e                                                     %
   10987 %                                                                             %
   10988 %                                                                             %
   10989 %                                                                             %
   10990 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   10991 %
   10992 %  XPrintImage() prints an image to a Postscript printer.
   10993 %
   10994 %  The format of the XPrintImage method is:
   10995 %
   10996 %      MagickBooleanType XPrintImage(Display *display,
   10997 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
   10998 %        ExceptionInfo *exception)
   10999 %
   11000 %  A description of each parameter follows:
   11001 %
   11002 %    o display: Specifies a connection to an X server; returned from
   11003 %      XOpenDisplay.
   11004 %
   11005 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   11006 %
   11007 %    o windows: Specifies a pointer to a XWindows structure.
   11008 %
   11009 %    o image: the image.
   11010 %
   11011 %    o exception: return any errors or warnings in this structure.
   11012 %
   11013 */
   11014 static MagickBooleanType XPrintImage(Display *display,
   11015   XResourceInfo *resource_info,XWindows *windows,Image *image,
   11016   ExceptionInfo *exception)
   11017 {
   11018   char
   11019     filename[MagickPathExtent],
   11020     geometry[MagickPathExtent];
   11021 
   11022   Image
   11023     *print_image;
   11024 
   11025   ImageInfo
   11026     *image_info;
   11027 
   11028   MagickStatusType
   11029     status;
   11030 
   11031   /*
   11032     Request Postscript page geometry from user.
   11033   */
   11034   image_info=CloneImageInfo(resource_info->image_info);
   11035   (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
   11036   if (image_info->page != (char *) NULL)
   11037     (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
   11038   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
   11039     "Select Postscript Page Geometry:",geometry);
   11040   if (*geometry == '\0')
   11041     return(MagickTrue);
   11042   image_info->page=GetPageGeometry(geometry);
   11043   /*
   11044     Apply image transforms.
   11045   */
   11046   XSetCursorState(display,windows,MagickTrue);
   11047   XCheckRefreshWindows(display,windows);
   11048   print_image=CloneImage(image,0,0,MagickTrue,exception);
   11049   if (print_image == (Image *) NULL)
   11050     return(MagickFalse);
   11051   (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
   11052     windows->image.ximage->width,windows->image.ximage->height);
   11053   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
   11054     exception);
   11055   /*
   11056     Print image.
   11057   */
   11058   (void) AcquireUniqueFilename(filename);
   11059   (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
   11060     filename);
   11061   status=WriteImage(image_info,print_image,exception);
   11062   (void) RelinquishUniqueFileResource(filename);
   11063   print_image=DestroyImage(print_image);
   11064   image_info=DestroyImageInfo(image_info);
   11065   XSetCursorState(display,windows,MagickFalse);
   11066   return(status != 0 ? MagickTrue : MagickFalse);
   11067 }
   11068 
   11069 /*
   11071 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   11072 %                                                                             %
   11073 %                                                                             %
   11074 %                                                                             %
   11075 +   X R O I I m a g e                                                         %
   11076 %                                                                             %
   11077 %                                                                             %
   11078 %                                                                             %
   11079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   11080 %
   11081 %  XROIImage() applies an image processing technique to a region of interest.
   11082 %
   11083 %  The format of the XROIImage method is:
   11084 %
   11085 %      MagickBooleanType XROIImage(Display *display,
   11086 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
   11087 %        ExceptionInfo *exception)
   11088 %
   11089 %  A description of each parameter follows:
   11090 %
   11091 %    o display: Specifies a connection to an X server; returned from
   11092 %      XOpenDisplay.
   11093 %
   11094 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   11095 %
   11096 %    o windows: Specifies a pointer to a XWindows structure.
   11097 %
   11098 %    o image: the image; returned from ReadImage.
   11099 %
   11100 %    o exception: return any errors or warnings in this structure.
   11101 %
   11102 */
   11103 static MagickBooleanType XROIImage(Display *display,
   11104   XResourceInfo *resource_info,XWindows *windows,Image **image,
   11105   ExceptionInfo *exception)
   11106 {
   11107 #define ApplyMenus  7
   11108 
   11109   static const char
   11110     *ROIMenu[] =
   11111     {
   11112       "Help",
   11113       "Dismiss",
   11114       (char *) NULL
   11115     },
   11116     *ApplyMenu[] =
   11117     {
   11118       "File",
   11119       "Edit",
   11120       "Transform",
   11121       "Enhance",
   11122       "Effects",
   11123       "F/X",
   11124       "Miscellany",
   11125       "Help",
   11126       "Dismiss",
   11127       (char *) NULL
   11128     },
   11129     *FileMenu[] =
   11130     {
   11131       "Save...",
   11132       "Print...",
   11133       (char *) NULL
   11134     },
   11135     *EditMenu[] =
   11136     {
   11137       "Undo",
   11138       "Redo",
   11139       (char *) NULL
   11140     },
   11141     *TransformMenu[] =
   11142     {
   11143       "Flop",
   11144       "Flip",
   11145       "Rotate Right",
   11146       "Rotate Left",
   11147       (char *) NULL
   11148     },
   11149     *EnhanceMenu[] =
   11150     {
   11151       "Hue...",
   11152       "Saturation...",
   11153       "Brightness...",
   11154       "Gamma...",
   11155       "Spiff",
   11156       "Dull",
   11157       "Contrast Stretch...",
   11158       "Sigmoidal Contrast...",
   11159       "Normalize",
   11160       "Equalize",
   11161       "Negate",
   11162       "Grayscale",
   11163       "Map...",
   11164       "Quantize...",
   11165       (char *) NULL
   11166     },
   11167     *EffectsMenu[] =
   11168     {
   11169       "Despeckle",
   11170       "Emboss",
   11171       "Reduce Noise",
   11172       "Add Noise",
   11173       "Sharpen...",
   11174       "Blur...",
   11175       "Threshold...",
   11176       "Edge Detect...",
   11177       "Spread...",
   11178       "Shade...",
   11179       "Raise...",
   11180       "Segment...",
   11181       (char *) NULL
   11182     },
   11183     *FXMenu[] =
   11184     {
   11185       "Solarize...",
   11186       "Sepia Tone...",
   11187       "Swirl...",
   11188       "Implode...",
   11189       "Vignette...",
   11190       "Wave...",
   11191       "Oil Paint...",
   11192       "Charcoal Draw...",
   11193       (char *) NULL
   11194     },
   11195     *MiscellanyMenu[] =
   11196     {
   11197       "Image Info",
   11198       "Zoom Image",
   11199       "Show Preview...",
   11200       "Show Histogram",
   11201       "Show Matte",
   11202       (char *) NULL
   11203     };
   11204 
   11205   static const char
   11206     **Menus[ApplyMenus] =
   11207     {
   11208       FileMenu,
   11209       EditMenu,
   11210       TransformMenu,
   11211       EnhanceMenu,
   11212       EffectsMenu,
   11213       FXMenu,
   11214       MiscellanyMenu
   11215     };
   11216 
   11217   static const CommandType
   11218     ApplyCommands[] =
   11219     {
   11220       NullCommand,
   11221       NullCommand,
   11222       NullCommand,
   11223       NullCommand,
   11224       NullCommand,
   11225       NullCommand,
   11226       NullCommand,
   11227       HelpCommand,
   11228       QuitCommand
   11229     },
   11230     FileCommands[] =
   11231     {
   11232       SaveCommand,
   11233       PrintCommand
   11234     },
   11235     EditCommands[] =
   11236     {
   11237       UndoCommand,
   11238       RedoCommand
   11239     },
   11240     TransformCommands[] =
   11241     {
   11242       FlopCommand,
   11243       FlipCommand,
   11244       RotateRightCommand,
   11245       RotateLeftCommand
   11246     },
   11247     EnhanceCommands[] =
   11248     {
   11249       HueCommand,
   11250       SaturationCommand,
   11251       BrightnessCommand,
   11252       GammaCommand,
   11253       SpiffCommand,
   11254       DullCommand,
   11255       ContrastStretchCommand,
   11256       SigmoidalContrastCommand,
   11257       NormalizeCommand,
   11258       EqualizeCommand,
   11259       NegateCommand,
   11260       GrayscaleCommand,
   11261       MapCommand,
   11262       QuantizeCommand
   11263     },
   11264     EffectsCommands[] =
   11265     {
   11266       DespeckleCommand,
   11267       EmbossCommand,
   11268       ReduceNoiseCommand,
   11269       AddNoiseCommand,
   11270       SharpenCommand,
   11271       BlurCommand,
   11272       EdgeDetectCommand,
   11273       SpreadCommand,
   11274       ShadeCommand,
   11275       RaiseCommand,
   11276       SegmentCommand
   11277     },
   11278     FXCommands[] =
   11279     {
   11280       SolarizeCommand,
   11281       SepiaToneCommand,
   11282       SwirlCommand,
   11283       ImplodeCommand,
   11284       VignetteCommand,
   11285       WaveCommand,
   11286       OilPaintCommand,
   11287       CharcoalDrawCommand
   11288     },
   11289     MiscellanyCommands[] =
   11290     {
   11291       InfoCommand,
   11292       ZoomCommand,
   11293       ShowPreviewCommand,
   11294       ShowHistogramCommand,
   11295       ShowMatteCommand
   11296     },
   11297     ROICommands[] =
   11298     {
   11299       ROIHelpCommand,
   11300       ROIDismissCommand
   11301     };
   11302 
   11303   static const CommandType
   11304     *Commands[ApplyMenus] =
   11305     {
   11306       FileCommands,
   11307       EditCommands,
   11308       TransformCommands,
   11309       EnhanceCommands,
   11310       EffectsCommands,
   11311       FXCommands,
   11312       MiscellanyCommands
   11313     };
   11314 
   11315   char
   11316     command[MagickPathExtent],
   11317     text[MagickPathExtent];
   11318 
   11319   CommandType
   11320     command_type;
   11321 
   11322   Cursor
   11323     cursor;
   11324 
   11325   Image
   11326     *roi_image;
   11327 
   11328   int
   11329     entry,
   11330     id,
   11331     x,
   11332     y;
   11333 
   11334   double
   11335     scale_factor;
   11336 
   11337   MagickProgressMonitor
   11338     progress_monitor;
   11339 
   11340   RectangleInfo
   11341     crop_info,
   11342     highlight_info,
   11343     roi_info;
   11344 
   11345   unsigned int
   11346     height,
   11347     width;
   11348 
   11349   size_t
   11350     state;
   11351 
   11352   XEvent
   11353     event;
   11354 
   11355   /*
   11356     Map Command widget.
   11357   */
   11358   (void) CloneString(&windows->command.name,"ROI");
   11359   windows->command.data=0;
   11360   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
   11361   (void) XMapRaised(display,windows->command.id);
   11362   XClientMessage(display,windows->image.id,windows->im_protocols,
   11363     windows->im_update_widget,CurrentTime);
   11364   /*
   11365     Track pointer until button 1 is pressed.
   11366   */
   11367   XQueryPosition(display,windows->image.id,&x,&y);
   11368   (void) XSelectInput(display,windows->image.id,
   11369     windows->image.attributes.event_mask | PointerMotionMask);
   11370   roi_info.x=(ssize_t) windows->image.x+x;
   11371   roi_info.y=(ssize_t) windows->image.y+y;
   11372   roi_info.width=0;
   11373   roi_info.height=0;
   11374   cursor=XCreateFontCursor(display,XC_fleur);
   11375   state=DefaultState;
   11376   do
   11377   {
   11378     if (windows->info.mapped != MagickFalse )
   11379       {
   11380         /*
   11381           Display pointer position.
   11382         */
   11383         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
   11384           (long) roi_info.x,(long) roi_info.y);
   11385         XInfoWidget(display,windows,text);
   11386       }
   11387     /*
   11388       Wait for next event.
   11389     */
   11390     XScreenEvent(display,windows,&event,exception);
   11391     if (event.xany.window == windows->command.id)
   11392       {
   11393         /*
   11394           Select a command from the Command widget.
   11395         */
   11396         id=XCommandWidget(display,windows,ROIMenu,&event);
   11397         if (id < 0)
   11398           continue;
   11399         switch (ROICommands[id])
   11400         {
   11401           case ROIHelpCommand:
   11402           {
   11403             XTextViewWidget(display,resource_info,windows,MagickFalse,
   11404               "Help Viewer - Region of Interest",ImageROIHelp);
   11405             break;
   11406           }
   11407           case ROIDismissCommand:
   11408           {
   11409             /*
   11410               Prematurely exit.
   11411             */
   11412             state|=EscapeState;
   11413             state|=ExitState;
   11414             break;
   11415           }
   11416           default:
   11417             break;
   11418         }
   11419         continue;
   11420       }
   11421     switch (event.type)
   11422     {
   11423       case ButtonPress:
   11424       {
   11425         if (event.xbutton.button != Button1)
   11426           break;
   11427         if (event.xbutton.window != windows->image.id)
   11428           break;
   11429         /*
   11430           Note first corner of region of interest rectangle-- exit loop.
   11431         */
   11432         (void) XCheckDefineCursor(display,windows->image.id,cursor);
   11433         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
   11434         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
   11435         state|=ExitState;
   11436         break;
   11437       }
   11438       case ButtonRelease:
   11439         break;
   11440       case Expose:
   11441         break;
   11442       case KeyPress:
   11443       {
   11444         KeySym
   11445           key_symbol;
   11446 
   11447         if (event.xkey.window != windows->image.id)
   11448           break;
   11449         /*
   11450           Respond to a user key press.
   11451         */
   11452         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   11453           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   11454         switch ((int) key_symbol)
   11455         {
   11456           case XK_Escape:
   11457           case XK_F20:
   11458           {
   11459             /*
   11460               Prematurely exit.
   11461             */
   11462             state|=EscapeState;
   11463             state|=ExitState;
   11464             break;
   11465           }
   11466           case XK_F1:
   11467           case XK_Help:
   11468           {
   11469             XTextViewWidget(display,resource_info,windows,MagickFalse,
   11470               "Help Viewer - Region of Interest",ImageROIHelp);
   11471             break;
   11472           }
   11473           default:
   11474           {
   11475             (void) XBell(display,0);
   11476             break;
   11477           }
   11478         }
   11479         break;
   11480       }
   11481       case MotionNotify:
   11482       {
   11483         /*
   11484           Map and unmap Info widget as text cursor crosses its boundaries.
   11485         */
   11486         x=event.xmotion.x;
   11487         y=event.xmotion.y;
   11488         if (windows->info.mapped != MagickFalse )
   11489           {
   11490             if ((x < (int) (windows->info.x+windows->info.width)) &&
   11491                 (y < (int) (windows->info.y+windows->info.height)))
   11492               (void) XWithdrawWindow(display,windows->info.id,
   11493                 windows->info.screen);
   11494           }
   11495         else
   11496           if ((x > (int) (windows->info.x+windows->info.width)) ||
   11497               (y > (int) (windows->info.y+windows->info.height)))
   11498             (void) XMapWindow(display,windows->info.id);
   11499         roi_info.x=(ssize_t) windows->image.x+x;
   11500         roi_info.y=(ssize_t) windows->image.y+y;
   11501         break;
   11502       }
   11503       default:
   11504         break;
   11505     }
   11506   } while ((state & ExitState) == 0);
   11507   (void) XSelectInput(display,windows->image.id,
   11508     windows->image.attributes.event_mask);
   11509   if ((state & EscapeState) != 0)
   11510     {
   11511       /*
   11512         User want to exit without region of interest.
   11513       */
   11514       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   11515       (void) XFreeCursor(display,cursor);
   11516       return(MagickTrue);
   11517     }
   11518   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
   11519   do
   11520   {
   11521     /*
   11522       Size rectangle as pointer moves until the mouse button is released.
   11523     */
   11524     x=(int) roi_info.x;
   11525     y=(int) roi_info.y;
   11526     roi_info.width=0;
   11527     roi_info.height=0;
   11528     state=DefaultState;
   11529     do
   11530     {
   11531       highlight_info=roi_info;
   11532       highlight_info.x=roi_info.x-windows->image.x;
   11533       highlight_info.y=roi_info.y-windows->image.y;
   11534       if ((highlight_info.width > 3) && (highlight_info.height > 3))
   11535         {
   11536           /*
   11537             Display info and draw region of interest rectangle.
   11538           */
   11539           if (windows->info.mapped == MagickFalse)
   11540             (void) XMapWindow(display,windows->info.id);
   11541           (void) FormatLocaleString(text,MagickPathExtent,
   11542             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
   11543             roi_info.height,(double) roi_info.x,(double) roi_info.y);
   11544           XInfoWidget(display,windows,text);
   11545           XHighlightRectangle(display,windows->image.id,
   11546             windows->image.highlight_context,&highlight_info);
   11547         }
   11548       else
   11549         if (windows->info.mapped != MagickFalse )
   11550           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   11551       /*
   11552         Wait for next event.
   11553       */
   11554       XScreenEvent(display,windows,&event,exception);
   11555       if ((highlight_info.width > 3) && (highlight_info.height > 3))
   11556         XHighlightRectangle(display,windows->image.id,
   11557           windows->image.highlight_context,&highlight_info);
   11558       switch (event.type)
   11559       {
   11560         case ButtonPress:
   11561         {
   11562           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
   11563           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
   11564           break;
   11565         }
   11566         case ButtonRelease:
   11567         {
   11568           /*
   11569             User has committed to region of interest rectangle.
   11570           */
   11571           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
   11572           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
   11573           XSetCursorState(display,windows,MagickFalse);
   11574           state|=ExitState;
   11575           if (LocaleCompare(windows->command.name,"Apply") == 0)
   11576             break;
   11577           (void) CloneString(&windows->command.name,"Apply");
   11578           windows->command.data=ApplyMenus;
   11579           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
   11580           break;
   11581         }
   11582         case Expose:
   11583           break;
   11584         case MotionNotify:
   11585         {
   11586           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
   11587           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
   11588         }
   11589         default:
   11590           break;
   11591       }
   11592       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
   11593           ((state & ExitState) != 0))
   11594         {
   11595           /*
   11596             Check boundary conditions.
   11597           */
   11598           if (roi_info.x < 0)
   11599             roi_info.x=0;
   11600           else
   11601             if (roi_info.x > (ssize_t) windows->image.ximage->width)
   11602               roi_info.x=(ssize_t) windows->image.ximage->width;
   11603           if ((int) roi_info.x < x)
   11604             roi_info.width=(unsigned int) (x-roi_info.x);
   11605           else
   11606             {
   11607               roi_info.width=(unsigned int) (roi_info.x-x);
   11608               roi_info.x=(ssize_t) x;
   11609             }
   11610           if (roi_info.y < 0)
   11611             roi_info.y=0;
   11612           else
   11613             if (roi_info.y > (ssize_t) windows->image.ximage->height)
   11614               roi_info.y=(ssize_t) windows->image.ximage->height;
   11615           if ((int) roi_info.y < y)
   11616             roi_info.height=(unsigned int) (y-roi_info.y);
   11617           else
   11618             {
   11619               roi_info.height=(unsigned int) (roi_info.y-y);
   11620               roi_info.y=(ssize_t) y;
   11621             }
   11622         }
   11623     } while ((state & ExitState) == 0);
   11624     /*
   11625       Wait for user to grab a corner of the rectangle or press return.
   11626     */
   11627     state=DefaultState;
   11628     command_type=NullCommand;
   11629     crop_info.x=0;
   11630     crop_info.y=0;
   11631     (void) XMapWindow(display,windows->info.id);
   11632     do
   11633     {
   11634       if (windows->info.mapped != MagickFalse )
   11635         {
   11636           /*
   11637             Display pointer position.
   11638           */
   11639           (void) FormatLocaleString(text,MagickPathExtent,
   11640             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
   11641             roi_info.height,(double) roi_info.x,(double) roi_info.y);
   11642           XInfoWidget(display,windows,text);
   11643         }
   11644       highlight_info=roi_info;
   11645       highlight_info.x=roi_info.x-windows->image.x;
   11646       highlight_info.y=roi_info.y-windows->image.y;
   11647       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
   11648         {
   11649           state|=EscapeState;
   11650           state|=ExitState;
   11651           break;
   11652         }
   11653       if ((state & UpdateRegionState) != 0)
   11654         {
   11655           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
   11656           switch (command_type)
   11657           {
   11658             case UndoCommand:
   11659             case RedoCommand:
   11660             {
   11661               (void) XMagickCommand(display,resource_info,windows,command_type,
   11662                 image,exception);
   11663               break;
   11664             }
   11665             default:
   11666             {
   11667               /*
   11668                 Region of interest is relative to image configuration.
   11669               */
   11670               progress_monitor=SetImageProgressMonitor(*image,
   11671                 (MagickProgressMonitor) NULL,(*image)->client_data);
   11672               crop_info=roi_info;
   11673               width=(unsigned int) (*image)->columns;
   11674               height=(unsigned int) (*image)->rows;
   11675               x=0;
   11676               y=0;
   11677               if (windows->image.crop_geometry != (char *) NULL)
   11678                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
   11679                   &width,&height);
   11680               scale_factor=(double) width/windows->image.ximage->width;
   11681               crop_info.x+=x;
   11682               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
   11683               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
   11684               scale_factor=(double)
   11685                 height/windows->image.ximage->height;
   11686               crop_info.y+=y;
   11687               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
   11688               crop_info.height=(unsigned int)
   11689                 (scale_factor*crop_info.height+0.5);
   11690               roi_image=CropImage(*image,&crop_info,exception);
   11691               (void) SetImageProgressMonitor(*image,progress_monitor,
   11692                 (*image)->client_data);
   11693               if (roi_image == (Image *) NULL)
   11694                 continue;
   11695               /*
   11696                 Apply image processing technique to the region of interest.
   11697               */
   11698               windows->image.orphan=MagickTrue;
   11699               (void) XMagickCommand(display,resource_info,windows,command_type,
   11700                 &roi_image,exception);
   11701               progress_monitor=SetImageProgressMonitor(*image,
   11702                 (MagickProgressMonitor) NULL,(*image)->client_data);
   11703               (void) XMagickCommand(display,resource_info,windows,
   11704                 SaveToUndoBufferCommand,image,exception);
   11705               windows->image.orphan=MagickFalse;
   11706               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
   11707                 MagickTrue,crop_info.x,crop_info.y,exception);
   11708               roi_image=DestroyImage(roi_image);
   11709               (void) SetImageProgressMonitor(*image,progress_monitor,
   11710                 (*image)->client_data);
   11711               break;
   11712             }
   11713           }
   11714           if (command_type != InfoCommand)
   11715             {
   11716               XConfigureImageColormap(display,resource_info,windows,*image,
   11717                 exception);
   11718               (void) XConfigureImage(display,resource_info,windows,*image,
   11719                 exception);
   11720             }
   11721           XCheckRefreshWindows(display,windows);
   11722           XInfoWidget(display,windows,text);
   11723           (void) XSetFunction(display,windows->image.highlight_context,
   11724             GXinvert);
   11725           state&=(~UpdateRegionState);
   11726         }
   11727       XHighlightRectangle(display,windows->image.id,
   11728         windows->image.highlight_context,&highlight_info);
   11729       XScreenEvent(display,windows,&event,exception);
   11730       if (event.xany.window == windows->command.id)
   11731         {
   11732           /*
   11733             Select a command from the Command widget.
   11734           */
   11735           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
   11736           command_type=NullCommand;
   11737           id=XCommandWidget(display,windows,ApplyMenu,&event);
   11738           if (id >= 0)
   11739             {
   11740               (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
   11741               command_type=ApplyCommands[id];
   11742               if (id < ApplyMenus)
   11743                 {
   11744                   /*
   11745                     Select a command from a pop-up menu.
   11746                   */
   11747                   entry=XMenuWidget(display,windows,ApplyMenu[id],
   11748                     (const char **) Menus[id],command);
   11749                   if (entry >= 0)
   11750                     {
   11751                       (void) CopyMagickString(command,Menus[id][entry],
   11752                         MagickPathExtent);
   11753                       command_type=Commands[id][entry];
   11754                     }
   11755                 }
   11756             }
   11757           (void) XSetFunction(display,windows->image.highlight_context,
   11758             GXinvert);
   11759           XHighlightRectangle(display,windows->image.id,
   11760             windows->image.highlight_context,&highlight_info);
   11761           if (command_type == HelpCommand)
   11762             {
   11763               (void) XSetFunction(display,windows->image.highlight_context,
   11764                 GXcopy);
   11765               XTextViewWidget(display,resource_info,windows,MagickFalse,
   11766                 "Help Viewer - Region of Interest",ImageROIHelp);
   11767               (void) XSetFunction(display,windows->image.highlight_context,
   11768                 GXinvert);
   11769               continue;
   11770             }
   11771           if (command_type == QuitCommand)
   11772             {
   11773               /*
   11774                 exit.
   11775               */
   11776               state|=EscapeState;
   11777               state|=ExitState;
   11778               continue;
   11779             }
   11780           if (command_type != NullCommand)
   11781             state|=UpdateRegionState;
   11782           continue;
   11783         }
   11784       XHighlightRectangle(display,windows->image.id,
   11785         windows->image.highlight_context,&highlight_info);
   11786       switch (event.type)
   11787       {
   11788         case ButtonPress:
   11789         {
   11790           x=windows->image.x;
   11791           y=windows->image.y;
   11792           if (event.xbutton.button != Button1)
   11793             break;
   11794           if (event.xbutton.window != windows->image.id)
   11795             break;
   11796           x=windows->image.x+event.xbutton.x;
   11797           y=windows->image.y+event.xbutton.y;
   11798           if ((x < (int) (roi_info.x+RoiDelta)) &&
   11799               (x > (int) (roi_info.x-RoiDelta)) &&
   11800               (y < (int) (roi_info.y+RoiDelta)) &&
   11801               (y > (int) (roi_info.y-RoiDelta)))
   11802             {
   11803               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
   11804               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
   11805               state|=UpdateConfigurationState;
   11806               break;
   11807             }
   11808           if ((x < (int) (roi_info.x+RoiDelta)) &&
   11809               (x > (int) (roi_info.x-RoiDelta)) &&
   11810               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
   11811               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
   11812             {
   11813               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
   11814               state|=UpdateConfigurationState;
   11815               break;
   11816             }
   11817           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
   11818               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
   11819               (y < (int) (roi_info.y+RoiDelta)) &&
   11820               (y > (int) (roi_info.y-RoiDelta)))
   11821             {
   11822               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
   11823               state|=UpdateConfigurationState;
   11824               break;
   11825             }
   11826           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
   11827               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
   11828               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
   11829               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
   11830             {
   11831               state|=UpdateConfigurationState;
   11832               break;
   11833             }
   11834         }
   11835         case ButtonRelease:
   11836         {
   11837           if (event.xbutton.window == windows->pan.id)
   11838             if ((highlight_info.x != crop_info.x-windows->image.x) ||
   11839                 (highlight_info.y != crop_info.y-windows->image.y))
   11840               XHighlightRectangle(display,windows->image.id,
   11841                 windows->image.highlight_context,&highlight_info);
   11842           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
   11843             event.xbutton.time);
   11844           break;
   11845         }
   11846         case Expose:
   11847         {
   11848           if (event.xexpose.window == windows->image.id)
   11849             if (event.xexpose.count == 0)
   11850               {
   11851                 event.xexpose.x=(int) highlight_info.x;
   11852                 event.xexpose.y=(int) highlight_info.y;
   11853                 event.xexpose.width=(int) highlight_info.width;
   11854                 event.xexpose.height=(int) highlight_info.height;
   11855                 XRefreshWindow(display,&windows->image,&event);
   11856               }
   11857           if (event.xexpose.window == windows->info.id)
   11858             if (event.xexpose.count == 0)
   11859               XInfoWidget(display,windows,text);
   11860           break;
   11861         }
   11862         case KeyPress:
   11863         {
   11864           KeySym
   11865             key_symbol;
   11866 
   11867           if (event.xkey.window != windows->image.id)
   11868             break;
   11869           /*
   11870             Respond to a user key press.
   11871           */
   11872           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   11873             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   11874           switch ((int) key_symbol)
   11875           {
   11876             case XK_Shift_L:
   11877             case XK_Shift_R:
   11878               break;
   11879             case XK_Escape:
   11880             case XK_F20:
   11881               state|=EscapeState;
   11882             case XK_Return:
   11883             {
   11884               state|=ExitState;
   11885               break;
   11886             }
   11887             case XK_Home:
   11888             case XK_KP_Home:
   11889             {
   11890               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
   11891               roi_info.y=(ssize_t) (windows->image.height/2L-
   11892                 roi_info.height/2L);
   11893               break;
   11894             }
   11895             case XK_Left:
   11896             case XK_KP_Left:
   11897             {
   11898               roi_info.x--;
   11899               break;
   11900             }
   11901             case XK_Up:
   11902             case XK_KP_Up:
   11903             case XK_Next:
   11904             {
   11905               roi_info.y--;
   11906               break;
   11907             }
   11908             case XK_Right:
   11909             case XK_KP_Right:
   11910             {
   11911               roi_info.x++;
   11912               break;
   11913             }
   11914             case XK_Prior:
   11915             case XK_Down:
   11916             case XK_KP_Down:
   11917             {
   11918               roi_info.y++;
   11919               break;
   11920             }
   11921             case XK_F1:
   11922             case XK_Help:
   11923             {
   11924               (void) XSetFunction(display,windows->image.highlight_context,
   11925                 GXcopy);
   11926               XTextViewWidget(display,resource_info,windows,MagickFalse,
   11927                 "Help Viewer - Region of Interest",ImageROIHelp);
   11928               (void) XSetFunction(display,windows->image.highlight_context,
   11929                 GXinvert);
   11930               break;
   11931             }
   11932             default:
   11933             {
   11934               command_type=XImageWindowCommand(display,resource_info,windows,
   11935                 event.xkey.state,key_symbol,image,exception);
   11936               if (command_type != NullCommand)
   11937                 state|=UpdateRegionState;
   11938               break;
   11939             }
   11940           }
   11941           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
   11942             event.xkey.time);
   11943           break;
   11944         }
   11945         case KeyRelease:
   11946           break;
   11947         case MotionNotify:
   11948         {
   11949           if (event.xbutton.window != windows->image.id)
   11950             break;
   11951           /*
   11952             Map and unmap Info widget as text cursor crosses its boundaries.
   11953           */
   11954           x=event.xmotion.x;
   11955           y=event.xmotion.y;
   11956           if (windows->info.mapped != MagickFalse )
   11957             {
   11958               if ((x < (int) (windows->info.x+windows->info.width)) &&
   11959                   (y < (int) (windows->info.y+windows->info.height)))
   11960                 (void) XWithdrawWindow(display,windows->info.id,
   11961                   windows->info.screen);
   11962             }
   11963           else
   11964             if ((x > (int) (windows->info.x+windows->info.width)) ||
   11965                 (y > (int) (windows->info.y+windows->info.height)))
   11966               (void) XMapWindow(display,windows->info.id);
   11967           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
   11968           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
   11969           break;
   11970         }
   11971         case SelectionRequest:
   11972         {
   11973           XSelectionEvent
   11974             notify;
   11975 
   11976           XSelectionRequestEvent
   11977             *request;
   11978 
   11979           /*
   11980             Set primary selection.
   11981           */
   11982           (void) FormatLocaleString(text,MagickPathExtent,
   11983             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
   11984             roi_info.height,(double) roi_info.x,(double) roi_info.y);
   11985           request=(&(event.xselectionrequest));
   11986           (void) XChangeProperty(request->display,request->requestor,
   11987             request->property,request->target,8,PropModeReplace,
   11988             (unsigned char *) text,(int) strlen(text));
   11989           notify.type=SelectionNotify;
   11990           notify.display=request->display;
   11991           notify.requestor=request->requestor;
   11992           notify.selection=request->selection;
   11993           notify.target=request->target;
   11994           notify.time=request->time;
   11995           if (request->property == None)
   11996             notify.property=request->target;
   11997           else
   11998             notify.property=request->property;
   11999           (void) XSendEvent(request->display,request->requestor,False,0,
   12000             (XEvent *) &notify);
   12001         }
   12002         default:
   12003           break;
   12004       }
   12005       if ((state & UpdateConfigurationState) != 0)
   12006         {
   12007           (void) XPutBackEvent(display,&event);
   12008           (void) XCheckDefineCursor(display,windows->image.id,cursor);
   12009           break;
   12010         }
   12011     } while ((state & ExitState) == 0);
   12012   } while ((state & ExitState) == 0);
   12013   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
   12014   XSetCursorState(display,windows,MagickFalse);
   12015   if ((state & EscapeState) != 0)
   12016     return(MagickTrue);
   12017   return(MagickTrue);
   12018 }
   12019 
   12020 /*
   12022 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   12023 %                                                                             %
   12024 %                                                                             %
   12025 %                                                                             %
   12026 +   X R o t a t e I m a g e                                                   %
   12027 %                                                                             %
   12028 %                                                                             %
   12029 %                                                                             %
   12030 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   12031 %
   12032 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
   12033 %  rotation angle is computed from the slope of a line drawn by the user.
   12034 %
   12035 %  The format of the XRotateImage method is:
   12036 %
   12037 %      MagickBooleanType XRotateImage(Display *display,
   12038 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
   12039 %        Image **image,ExceptionInfo *exception)
   12040 %
   12041 %  A description of each parameter follows:
   12042 %
   12043 %    o display: Specifies a connection to an X server; returned from
   12044 %      XOpenDisplay.
   12045 %
   12046 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   12047 %
   12048 %    o windows: Specifies a pointer to a XWindows structure.
   12049 %
   12050 %    o degrees: Specifies the number of degrees to rotate the image.
   12051 %
   12052 %    o image: the image.
   12053 %
   12054 %    o exception: return any errors or warnings in this structure.
   12055 %
   12056 */
   12057 static MagickBooleanType XRotateImage(Display *display,
   12058   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
   12059   ExceptionInfo *exception)
   12060 {
   12061   static const char
   12062     *RotateMenu[] =
   12063     {
   12064       "Pixel Color",
   12065       "Direction",
   12066       "Help",
   12067       "Dismiss",
   12068       (char *) NULL
   12069     };
   12070 
   12071   static ModeType
   12072     direction = HorizontalRotateCommand;
   12073 
   12074   static const ModeType
   12075     DirectionCommands[] =
   12076     {
   12077       HorizontalRotateCommand,
   12078       VerticalRotateCommand
   12079     },
   12080     RotateCommands[] =
   12081     {
   12082       RotateColorCommand,
   12083       RotateDirectionCommand,
   12084       RotateHelpCommand,
   12085       RotateDismissCommand
   12086     };
   12087 
   12088   static unsigned int
   12089     pen_id = 0;
   12090 
   12091   char
   12092     command[MagickPathExtent],
   12093     text[MagickPathExtent];
   12094 
   12095   Image
   12096     *rotate_image;
   12097 
   12098   int
   12099     id,
   12100     x,
   12101     y;
   12102 
   12103   double
   12104     normalized_degrees;
   12105 
   12106   register int
   12107     i;
   12108 
   12109   unsigned int
   12110     height,
   12111     rotations,
   12112     width;
   12113 
   12114   if (degrees == 0.0)
   12115     {
   12116       unsigned int
   12117         distance;
   12118 
   12119       size_t
   12120         state;
   12121 
   12122       XEvent
   12123         event;
   12124 
   12125       XSegment
   12126         rotate_info;
   12127 
   12128       /*
   12129         Map Command widget.
   12130       */
   12131       (void) CloneString(&windows->command.name,"Rotate");
   12132       windows->command.data=2;
   12133       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
   12134       (void) XMapRaised(display,windows->command.id);
   12135       XClientMessage(display,windows->image.id,windows->im_protocols,
   12136         windows->im_update_widget,CurrentTime);
   12137       /*
   12138         Wait for first button press.
   12139       */
   12140       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
   12141       XQueryPosition(display,windows->image.id,&x,&y);
   12142       rotate_info.x1=x;
   12143       rotate_info.y1=y;
   12144       rotate_info.x2=x;
   12145       rotate_info.y2=y;
   12146       state=DefaultState;
   12147       do
   12148       {
   12149         XHighlightLine(display,windows->image.id,
   12150           windows->image.highlight_context,&rotate_info);
   12151         /*
   12152           Wait for next event.
   12153         */
   12154         XScreenEvent(display,windows,&event,exception);
   12155         XHighlightLine(display,windows->image.id,
   12156           windows->image.highlight_context,&rotate_info);
   12157         if (event.xany.window == windows->command.id)
   12158           {
   12159             /*
   12160               Select a command from the Command widget.
   12161             */
   12162             id=XCommandWidget(display,windows,RotateMenu,&event);
   12163             if (id < 0)
   12164               continue;
   12165             (void) XSetFunction(display,windows->image.highlight_context,
   12166               GXcopy);
   12167             switch (RotateCommands[id])
   12168             {
   12169               case RotateColorCommand:
   12170               {
   12171                 const char
   12172                   *ColorMenu[MaxNumberPens];
   12173 
   12174                 int
   12175                   pen_number;
   12176 
   12177                 XColor
   12178                   color;
   12179 
   12180                 /*
   12181                   Initialize menu selections.
   12182                 */
   12183                 for (i=0; i < (int) (MaxNumberPens-2); i++)
   12184                   ColorMenu[i]=resource_info->pen_colors[i];
   12185                 ColorMenu[MaxNumberPens-2]="Browser...";
   12186                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
   12187                 /*
   12188                   Select a pen color from the pop-up menu.
   12189                 */
   12190                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
   12191                   (const char **) ColorMenu,command);
   12192                 if (pen_number < 0)
   12193                   break;
   12194                 if (pen_number == (MaxNumberPens-2))
   12195                   {
   12196                     static char
   12197                       color_name[MagickPathExtent] = "gray";
   12198 
   12199                     /*
   12200                       Select a pen color from a dialog.
   12201                     */
   12202                     resource_info->pen_colors[pen_number]=color_name;
   12203                     XColorBrowserWidget(display,windows,"Select",color_name);
   12204                     if (*color_name == '\0')
   12205                       break;
   12206                   }
   12207                 /*
   12208                   Set pen color.
   12209                 */
   12210                 (void) XParseColor(display,windows->map_info->colormap,
   12211                   resource_info->pen_colors[pen_number],&color);
   12212                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
   12213                   (unsigned int) MaxColors,&color);
   12214                 windows->pixel_info->pen_colors[pen_number]=color;
   12215                 pen_id=(unsigned int) pen_number;
   12216                 break;
   12217               }
   12218               case RotateDirectionCommand:
   12219               {
   12220                 static const char
   12221                   *Directions[] =
   12222                   {
   12223                     "horizontal",
   12224                     "vertical",
   12225                     (char *) NULL,
   12226                   };
   12227 
   12228                 /*
   12229                   Select a command from the pop-up menu.
   12230                 */
   12231                 id=XMenuWidget(display,windows,RotateMenu[id],
   12232                   Directions,command);
   12233                 if (id >= 0)
   12234                   direction=DirectionCommands[id];
   12235                 break;
   12236               }
   12237               case RotateHelpCommand:
   12238               {
   12239                 XTextViewWidget(display,resource_info,windows,MagickFalse,
   12240                   "Help Viewer - Image Rotation",ImageRotateHelp);
   12241                 break;
   12242               }
   12243               case RotateDismissCommand:
   12244               {
   12245                 /*
   12246                   Prematurely exit.
   12247                 */
   12248                 state|=EscapeState;
   12249                 state|=ExitState;
   12250                 break;
   12251               }
   12252               default:
   12253                 break;
   12254             }
   12255             (void) XSetFunction(display,windows->image.highlight_context,
   12256               GXinvert);
   12257             continue;
   12258           }
   12259         switch (event.type)
   12260         {
   12261           case ButtonPress:
   12262           {
   12263             if (event.xbutton.button != Button1)
   12264               break;
   12265             if (event.xbutton.window != windows->image.id)
   12266               break;
   12267             /*
   12268               exit loop.
   12269             */
   12270             (void) XSetFunction(display,windows->image.highlight_context,
   12271               GXcopy);
   12272             rotate_info.x1=event.xbutton.x;
   12273             rotate_info.y1=event.xbutton.y;
   12274             state|=ExitState;
   12275             break;
   12276           }
   12277           case ButtonRelease:
   12278             break;
   12279           case Expose:
   12280             break;
   12281           case KeyPress:
   12282           {
   12283             char
   12284               command[MagickPathExtent];
   12285 
   12286             KeySym
   12287               key_symbol;
   12288 
   12289             if (event.xkey.window != windows->image.id)
   12290               break;
   12291             /*
   12292               Respond to a user key press.
   12293             */
   12294             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   12295               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   12296             switch ((int) key_symbol)
   12297             {
   12298               case XK_Escape:
   12299               case XK_F20:
   12300               {
   12301                 /*
   12302                   Prematurely exit.
   12303                 */
   12304                 state|=EscapeState;
   12305                 state|=ExitState;
   12306                 break;
   12307               }
   12308               case XK_F1:
   12309               case XK_Help:
   12310               {
   12311                 (void) XSetFunction(display,windows->image.highlight_context,
   12312                   GXcopy);
   12313                 XTextViewWidget(display,resource_info,windows,MagickFalse,
   12314                   "Help Viewer - Image Rotation",ImageRotateHelp);
   12315                 (void) XSetFunction(display,windows->image.highlight_context,
   12316                   GXinvert);
   12317                 break;
   12318               }
   12319               default:
   12320               {
   12321                 (void) XBell(display,0);
   12322                 break;
   12323               }
   12324             }
   12325             break;
   12326           }
   12327           case MotionNotify:
   12328           {
   12329             rotate_info.x1=event.xmotion.x;
   12330             rotate_info.y1=event.xmotion.y;
   12331           }
   12332         }
   12333         rotate_info.x2=rotate_info.x1;
   12334         rotate_info.y2=rotate_info.y1;
   12335         if (direction == HorizontalRotateCommand)
   12336           rotate_info.x2+=32;
   12337         else
   12338           rotate_info.y2-=32;
   12339       } while ((state & ExitState) == 0);
   12340       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
   12341       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   12342       if ((state & EscapeState) != 0)
   12343         return(MagickTrue);
   12344       /*
   12345         Draw line as pointer moves until the mouse button is released.
   12346       */
   12347       distance=0;
   12348       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
   12349       state=DefaultState;
   12350       do
   12351       {
   12352         if (distance > 9)
   12353           {
   12354             /*
   12355               Display info and draw rotation line.
   12356             */
   12357             if (windows->info.mapped == MagickFalse)
   12358               (void) XMapWindow(display,windows->info.id);
   12359             (void) FormatLocaleString(text,MagickPathExtent," %g",
   12360               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
   12361             XInfoWidget(display,windows,text);
   12362             XHighlightLine(display,windows->image.id,
   12363               windows->image.highlight_context,&rotate_info);
   12364           }
   12365         else
   12366           if (windows->info.mapped != MagickFalse )
   12367             (void) XWithdrawWindow(display,windows->info.id,
   12368               windows->info.screen);
   12369         /*
   12370           Wait for next event.
   12371         */
   12372         XScreenEvent(display,windows,&event,exception);
   12373         if (distance > 9)
   12374           XHighlightLine(display,windows->image.id,
   12375             windows->image.highlight_context,&rotate_info);
   12376         switch (event.type)
   12377         {
   12378           case ButtonPress:
   12379             break;
   12380           case ButtonRelease:
   12381           {
   12382             /*
   12383               User has committed to rotation line.
   12384             */
   12385             rotate_info.x2=event.xbutton.x;
   12386             rotate_info.y2=event.xbutton.y;
   12387             state|=ExitState;
   12388             break;
   12389           }
   12390           case Expose:
   12391             break;
   12392           case MotionNotify:
   12393           {
   12394             rotate_info.x2=event.xmotion.x;
   12395             rotate_info.y2=event.xmotion.y;
   12396           }
   12397           default:
   12398             break;
   12399         }
   12400         /*
   12401           Check boundary conditions.
   12402         */
   12403         if (rotate_info.x2 < 0)
   12404           rotate_info.x2=0;
   12405         else
   12406           if (rotate_info.x2 > (int) windows->image.width)
   12407             rotate_info.x2=(short) windows->image.width;
   12408         if (rotate_info.y2 < 0)
   12409           rotate_info.y2=0;
   12410         else
   12411           if (rotate_info.y2 > (int) windows->image.height)
   12412             rotate_info.y2=(short) windows->image.height;
   12413         /*
   12414           Compute rotation angle from the slope of the line.
   12415         */
   12416         degrees=0.0;
   12417         distance=(unsigned int)
   12418           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
   12419           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
   12420         if (distance > 9)
   12421           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
   12422             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
   12423       } while ((state & ExitState) == 0);
   12424       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
   12425       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   12426       if (distance <= 9)
   12427         return(MagickTrue);
   12428     }
   12429   if (direction == VerticalRotateCommand)
   12430     degrees-=90.0;
   12431   if (degrees == 0.0)
   12432     return(MagickTrue);
   12433   /*
   12434     Rotate image.
   12435   */
   12436   normalized_degrees=degrees;
   12437   while (normalized_degrees < -45.0)
   12438     normalized_degrees+=360.0;
   12439   for (rotations=0; normalized_degrees > 45.0; rotations++)
   12440     normalized_degrees-=90.0;
   12441   if (normalized_degrees != 0.0)
   12442     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
   12443       exception);
   12444   XSetCursorState(display,windows,MagickTrue);
   12445   XCheckRefreshWindows(display,windows);
   12446   (*image)->background_color.red=(double) ScaleShortToQuantum(
   12447     windows->pixel_info->pen_colors[pen_id].red);
   12448   (*image)->background_color.green=(double) ScaleShortToQuantum(
   12449     windows->pixel_info->pen_colors[pen_id].green);
   12450   (*image)->background_color.blue=(double) ScaleShortToQuantum(
   12451     windows->pixel_info->pen_colors[pen_id].blue);
   12452   rotate_image=RotateImage(*image,degrees,exception);
   12453   XSetCursorState(display,windows,MagickFalse);
   12454   if (rotate_image == (Image *) NULL)
   12455     return(MagickFalse);
   12456   *image=DestroyImage(*image);
   12457   *image=rotate_image;
   12458   if (windows->image.crop_geometry != (char *) NULL)
   12459     {
   12460       /*
   12461         Rotate crop geometry.
   12462       */
   12463       width=(unsigned int) (*image)->columns;
   12464       height=(unsigned int) (*image)->rows;
   12465       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
   12466       switch (rotations % 4)
   12467       {
   12468         default:
   12469         case 0:
   12470           break;
   12471         case 1:
   12472         {
   12473           /*
   12474             Rotate 90 degrees.
   12475           */
   12476           (void) FormatLocaleString(windows->image.crop_geometry,
   12477             MagickPathExtent,"%ux%u%+d%+d",height,width,(int) (*image)->columns-
   12478             (int) height-y,x);
   12479           break;
   12480         }
   12481         case 2:
   12482         {
   12483           /*
   12484             Rotate 180 degrees.
   12485           */
   12486           (void) FormatLocaleString(windows->image.crop_geometry,
   12487             MagickPathExtent,"%ux%u%+d%+d",width,height,(int) width-x,(int)
   12488             height-y);
   12489           break;
   12490         }
   12491         case 3:
   12492         {
   12493           /*
   12494             Rotate 270 degrees.
   12495           */
   12496           (void) FormatLocaleString(windows->image.crop_geometry,
   12497             MagickPathExtent,"%ux%u%+d%+d",height,width,y,(int) (*image)->rows-
   12498             (int) width-x);
   12499           break;
   12500         }
   12501       }
   12502     }
   12503   if (windows->image.orphan != MagickFalse )
   12504     return(MagickTrue);
   12505   if (normalized_degrees != 0.0)
   12506     {
   12507       /*
   12508         Update image colormap.
   12509       */
   12510       windows->image.window_changes.width=(int) (*image)->columns;
   12511       windows->image.window_changes.height=(int) (*image)->rows;
   12512       if (windows->image.crop_geometry != (char *) NULL)
   12513         {
   12514           /*
   12515             Obtain dimensions of image from crop geometry.
   12516           */
   12517           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
   12518             &width,&height);
   12519           windows->image.window_changes.width=(int) width;
   12520           windows->image.window_changes.height=(int) height;
   12521         }
   12522       XConfigureImageColormap(display,resource_info,windows,*image,exception);
   12523     }
   12524   else
   12525     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
   12526       {
   12527         windows->image.window_changes.width=windows->image.ximage->height;
   12528         windows->image.window_changes.height=windows->image.ximage->width;
   12529       }
   12530   /*
   12531     Update image configuration.
   12532   */
   12533   (void) XConfigureImage(display,resource_info,windows,*image,exception);
   12534   return(MagickTrue);
   12535 }
   12536 
   12537 /*
   12539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   12540 %                                                                             %
   12541 %                                                                             %
   12542 %                                                                             %
   12543 +   X S a v e I m a g e                                                       %
   12544 %                                                                             %
   12545 %                                                                             %
   12546 %                                                                             %
   12547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   12548 %
   12549 %  XSaveImage() saves an image to a file.
   12550 %
   12551 %  The format of the XSaveImage method is:
   12552 %
   12553 %      MagickBooleanType XSaveImage(Display *display,
   12554 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
   12555 %        ExceptionInfo *exception)
   12556 %
   12557 %  A description of each parameter follows:
   12558 %
   12559 %    o display: Specifies a connection to an X server; returned from
   12560 %      XOpenDisplay.
   12561 %
   12562 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   12563 %
   12564 %    o windows: Specifies a pointer to a XWindows structure.
   12565 %
   12566 %    o image: the image.
   12567 %
   12568 %    o exception: return any errors or warnings in this structure.
   12569 %
   12570 */
   12571 static MagickBooleanType XSaveImage(Display *display,
   12572   XResourceInfo *resource_info,XWindows *windows,Image *image,
   12573   ExceptionInfo *exception)
   12574 {
   12575   char
   12576     filename[MagickPathExtent],
   12577     geometry[MagickPathExtent];
   12578 
   12579   Image
   12580     *save_image;
   12581 
   12582   ImageInfo
   12583     *image_info;
   12584 
   12585   MagickStatusType
   12586     status;
   12587 
   12588   /*
   12589     Request file name from user.
   12590   */
   12591   if (resource_info->write_filename != (char *) NULL)
   12592     (void) CopyMagickString(filename,resource_info->write_filename,
   12593       MagickPathExtent);
   12594   else
   12595     {
   12596       char
   12597         path[MagickPathExtent];
   12598 
   12599       int
   12600         status;
   12601 
   12602       GetPathComponent(image->filename,HeadPath,path);
   12603       GetPathComponent(image->filename,TailPath,filename);
   12604       if (*path != '\0')
   12605         {
   12606           status=chdir(path);
   12607           if (status == -1)
   12608             (void) ThrowMagickException(exception,GetMagickModule(),
   12609               FileOpenError,"UnableToOpenFile","%s",path);
   12610         }
   12611     }
   12612   XFileBrowserWidget(display,windows,"Save",filename);
   12613   if (*filename == '\0')
   12614     return(MagickTrue);
   12615   if (IsPathAccessible(filename) != MagickFalse )
   12616     {
   12617       int
   12618         status;
   12619 
   12620       /*
   12621         File exists-- seek user's permission before overwriting.
   12622       */
   12623       status=XConfirmWidget(display,windows,"Overwrite",filename);
   12624       if (status <= 0)
   12625         return(MagickTrue);
   12626     }
   12627   image_info=CloneImageInfo(resource_info->image_info);
   12628   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
   12629   (void) SetImageInfo(image_info,1,exception);
   12630   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
   12631       (LocaleCompare(image_info->magick,"JPG") == 0))
   12632     {
   12633       char
   12634         quality[MagickPathExtent];
   12635 
   12636       int
   12637         status;
   12638 
   12639       /*
   12640         Request JPEG quality from user.
   12641       */
   12642       (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
   12643         image->quality);
   12644       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
   12645         quality);
   12646       if (*quality == '\0')
   12647         return(MagickTrue);
   12648       image->quality=StringToUnsignedLong(quality);
   12649       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
   12650     }
   12651   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
   12652       (LocaleCompare(image_info->magick,"PDF") == 0) ||
   12653       (LocaleCompare(image_info->magick,"PS") == 0) ||
   12654       (LocaleCompare(image_info->magick,"PS2") == 0))
   12655     {
   12656       char
   12657         geometry[MagickPathExtent];
   12658 
   12659       /*
   12660         Request page geometry from user.
   12661       */
   12662       (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
   12663       if (LocaleCompare(image_info->magick,"PDF") == 0)
   12664         (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
   12665       if (image_info->page != (char *) NULL)
   12666         (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
   12667       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
   12668         "Select page geometry:",geometry);
   12669       if (*geometry != '\0')
   12670         image_info->page=GetPageGeometry(geometry);
   12671     }
   12672   /*
   12673     Apply image transforms.
   12674   */
   12675   XSetCursorState(display,windows,MagickTrue);
   12676   XCheckRefreshWindows(display,windows);
   12677   save_image=CloneImage(image,0,0,MagickTrue,exception);
   12678   if (save_image == (Image *) NULL)
   12679     return(MagickFalse);
   12680   (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
   12681     windows->image.ximage->width,windows->image.ximage->height);
   12682   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
   12683     exception);
   12684   /*
   12685     Write image.
   12686   */
   12687   (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
   12688   status=WriteImage(image_info,save_image,exception);
   12689   if (status != MagickFalse )
   12690     image->taint=MagickFalse;
   12691   save_image=DestroyImage(save_image);
   12692   image_info=DestroyImageInfo(image_info);
   12693   XSetCursorState(display,windows,MagickFalse);
   12694   return(status != 0 ? MagickTrue : MagickFalse);
   12695 }
   12696 
   12697 /*
   12699 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   12700 %                                                                             %
   12701 %                                                                             %
   12702 %                                                                             %
   12703 +   X S c r e e n E v e n t                                                   %
   12704 %                                                                             %
   12705 %                                                                             %
   12706 %                                                                             %
   12707 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   12708 %
   12709 %  XScreenEvent() handles global events associated with the Pan and Magnify
   12710 %  windows.
   12711 %
   12712 %  The format of the XScreenEvent function is:
   12713 %
   12714 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
   12715 %        ExceptionInfo *exception)
   12716 %
   12717 %  A description of each parameter follows:
   12718 %
   12719 %    o display: Specifies a pointer to the Display structure;  returned from
   12720 %      XOpenDisplay.
   12721 %
   12722 %    o windows: Specifies a pointer to a XWindows structure.
   12723 %
   12724 %    o event: Specifies a pointer to a X11 XEvent structure.
   12725 %
   12726 %    o exception: return any errors or warnings in this structure.
   12727 %
   12728 */
   12729 
   12730 #if defined(__cplusplus) || defined(c_plusplus)
   12731 extern "C" {
   12732 #endif
   12733 
   12734 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
   12735 {
   12736   register XWindows
   12737     *windows;
   12738 
   12739   windows=(XWindows *) data;
   12740   if ((event->type == ClientMessage) &&
   12741       (event->xclient.window == windows->image.id))
   12742     return(MagickFalse);
   12743   return(MagickTrue);
   12744 }
   12745 
   12746 #if defined(__cplusplus) || defined(c_plusplus)
   12747 }
   12748 #endif
   12749 
   12750 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
   12751   ExceptionInfo *exception)
   12752 {
   12753   register int
   12754     x,
   12755     y;
   12756 
   12757   (void) XIfEvent(display,event,XPredicate,(char *) windows);
   12758   if (event->xany.window == windows->command.id)
   12759     return;
   12760   switch (event->type)
   12761   {
   12762     case ButtonPress:
   12763     case ButtonRelease:
   12764     {
   12765       if ((event->xbutton.button == Button3) &&
   12766           (event->xbutton.state & Mod1Mask))
   12767         {
   12768           /*
   12769             Convert Alt-Button3 to Button2.
   12770           */
   12771           event->xbutton.button=Button2;
   12772           event->xbutton.state&=(~Mod1Mask);
   12773         }
   12774       if (event->xbutton.window == windows->backdrop.id)
   12775         {
   12776           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
   12777             event->xbutton.time);
   12778           break;
   12779         }
   12780       if (event->xbutton.window == windows->pan.id)
   12781         {
   12782           XPanImage(display,windows,event,exception);
   12783           break;
   12784         }
   12785       if (event->xbutton.window == windows->image.id)
   12786         if (event->xbutton.button == Button2)
   12787           {
   12788             /*
   12789               Update magnified image.
   12790             */
   12791             x=event->xbutton.x;
   12792             y=event->xbutton.y;
   12793             if (x < 0)
   12794               x=0;
   12795             else
   12796               if (x >= (int) windows->image.width)
   12797                 x=(int) (windows->image.width-1);
   12798             windows->magnify.x=(int) windows->image.x+x;
   12799             if (y < 0)
   12800               y=0;
   12801             else
   12802              if (y >= (int) windows->image.height)
   12803                y=(int) (windows->image.height-1);
   12804             windows->magnify.y=windows->image.y+y;
   12805             if (windows->magnify.mapped == MagickFalse)
   12806               (void) XMapRaised(display,windows->magnify.id);
   12807             XMakeMagnifyImage(display,windows,exception);
   12808             if (event->type == ButtonRelease)
   12809               (void) XWithdrawWindow(display,windows->info.id,
   12810                 windows->info.screen);
   12811             break;
   12812           }
   12813       break;
   12814     }
   12815     case ClientMessage:
   12816     {
   12817       /*
   12818         If client window delete message, exit.
   12819       */
   12820       if (event->xclient.message_type != windows->wm_protocols)
   12821         break;
   12822       if (*event->xclient.data.l != (long) windows->wm_delete_window)
   12823         break;
   12824       if (event->xclient.window == windows->magnify.id)
   12825         {
   12826           (void) XWithdrawWindow(display,windows->magnify.id,
   12827             windows->magnify.screen);
   12828           break;
   12829         }
   12830       break;
   12831     }
   12832     case ConfigureNotify:
   12833     {
   12834       if (event->xconfigure.window == windows->magnify.id)
   12835         {
   12836           unsigned int
   12837             magnify;
   12838 
   12839           /*
   12840             Magnify window has a new configuration.
   12841           */
   12842           windows->magnify.width=(unsigned int) event->xconfigure.width;
   12843           windows->magnify.height=(unsigned int) event->xconfigure.height;
   12844           if (windows->magnify.mapped == MagickFalse)
   12845             break;
   12846           magnify=1;
   12847           while ((int) magnify <= event->xconfigure.width)
   12848             magnify<<=1;
   12849           while ((int) magnify <= event->xconfigure.height)
   12850             magnify<<=1;
   12851           magnify>>=1;
   12852           if (((int) magnify != event->xconfigure.width) ||
   12853               ((int) magnify != event->xconfigure.height))
   12854             {
   12855               XWindowChanges
   12856                 window_changes;
   12857 
   12858               window_changes.width=(int) magnify;
   12859               window_changes.height=(int) magnify;
   12860               (void) XReconfigureWMWindow(display,windows->magnify.id,
   12861                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
   12862                 &window_changes);
   12863               break;
   12864             }
   12865           XMakeMagnifyImage(display,windows,exception);
   12866           break;
   12867         }
   12868       break;
   12869     }
   12870     case Expose:
   12871     {
   12872       if (event->xexpose.window == windows->image.id)
   12873         {
   12874           XRefreshWindow(display,&windows->image,event);
   12875           break;
   12876         }
   12877       if (event->xexpose.window == windows->pan.id)
   12878         if (event->xexpose.count == 0)
   12879           {
   12880             XDrawPanRectangle(display,windows);
   12881             break;
   12882           }
   12883       if (event->xexpose.window == windows->magnify.id)
   12884         if (event->xexpose.count == 0)
   12885           {
   12886             XMakeMagnifyImage(display,windows,exception);
   12887             break;
   12888           }
   12889       break;
   12890     }
   12891     case KeyPress:
   12892     {
   12893       char
   12894         command[MagickPathExtent];
   12895 
   12896       KeySym
   12897         key_symbol;
   12898 
   12899       if (event->xkey.window != windows->magnify.id)
   12900         break;
   12901       /*
   12902         Respond to a user key press.
   12903       */
   12904       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
   12905         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   12906       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
   12907         exception);
   12908       break;
   12909     }
   12910     case MapNotify:
   12911     {
   12912       if (event->xmap.window == windows->magnify.id)
   12913         {
   12914           windows->magnify.mapped=MagickTrue;
   12915           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   12916           break;
   12917         }
   12918       if (event->xmap.window == windows->info.id)
   12919         {
   12920           windows->info.mapped=MagickTrue;
   12921           break;
   12922         }
   12923       break;
   12924     }
   12925     case MotionNotify:
   12926     {
   12927       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
   12928       if (event->xmotion.window == windows->image.id)
   12929         if (windows->magnify.mapped != MagickFalse )
   12930           {
   12931             /*
   12932               Update magnified image.
   12933             */
   12934             x=event->xmotion.x;
   12935             y=event->xmotion.y;
   12936             if (x < 0)
   12937               x=0;
   12938             else
   12939               if (x >= (int) windows->image.width)
   12940                 x=(int) (windows->image.width-1);
   12941             windows->magnify.x=(int) windows->image.x+x;
   12942             if (y < 0)
   12943               y=0;
   12944             else
   12945              if (y >= (int) windows->image.height)
   12946                y=(int) (windows->image.height-1);
   12947             windows->magnify.y=windows->image.y+y;
   12948             XMakeMagnifyImage(display,windows,exception);
   12949           }
   12950       break;
   12951     }
   12952     case UnmapNotify:
   12953     {
   12954       if (event->xunmap.window == windows->magnify.id)
   12955         {
   12956           windows->magnify.mapped=MagickFalse;
   12957           break;
   12958         }
   12959       if (event->xunmap.window == windows->info.id)
   12960         {
   12961           windows->info.mapped=MagickFalse;
   12962           break;
   12963         }
   12964       break;
   12965     }
   12966     default:
   12967       break;
   12968   }
   12969 }
   12970 
   12971 /*
   12973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   12974 %                                                                             %
   12975 %                                                                             %
   12976 %                                                                             %
   12977 +   X S e t C r o p G e o m e t r y                                           %
   12978 %                                                                             %
   12979 %                                                                             %
   12980 %                                                                             %
   12981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   12982 %
   12983 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
   12984 %  and translates it to a cropping geometry relative to the image.
   12985 %
   12986 %  The format of the XSetCropGeometry method is:
   12987 %
   12988 %      void XSetCropGeometry(Display *display,XWindows *windows,
   12989 %        RectangleInfo *crop_info,Image *image)
   12990 %
   12991 %  A description of each parameter follows:
   12992 %
   12993 %    o display: Specifies a connection to an X server; returned from
   12994 %      XOpenDisplay.
   12995 %
   12996 %    o windows: Specifies a pointer to a XWindows structure.
   12997 %
   12998 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
   12999 %      Image window to crop.
   13000 %
   13001 %    o image: the image.
   13002 %
   13003 */
   13004 static void XSetCropGeometry(Display *display,XWindows *windows,
   13005   RectangleInfo *crop_info,Image *image)
   13006 {
   13007   char
   13008     text[MagickPathExtent];
   13009 
   13010   int
   13011     x,
   13012     y;
   13013 
   13014   double
   13015     scale_factor;
   13016 
   13017   unsigned int
   13018     height,
   13019     width;
   13020 
   13021   if (windows->info.mapped != MagickFalse )
   13022     {
   13023       /*
   13024         Display info on cropping rectangle.
   13025       */
   13026       (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
   13027         (double) crop_info->width,(double) crop_info->height,(double)
   13028         crop_info->x,(double) crop_info->y);
   13029       XInfoWidget(display,windows,text);
   13030     }
   13031   /*
   13032     Cropping geometry is relative to any previous crop geometry.
   13033   */
   13034   x=0;
   13035   y=0;
   13036   width=(unsigned int) image->columns;
   13037   height=(unsigned int) image->rows;
   13038   if (windows->image.crop_geometry != (char *) NULL)
   13039     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
   13040   else
   13041     windows->image.crop_geometry=AcquireString((char *) NULL);
   13042   /*
   13043     Define the crop geometry string from the cropping rectangle.
   13044   */
   13045   scale_factor=(double) width/windows->image.ximage->width;
   13046   if (crop_info->x > 0)
   13047     x+=(int) (scale_factor*crop_info->x+0.5);
   13048   width=(unsigned int) (scale_factor*crop_info->width+0.5);
   13049   if (width == 0)
   13050     width=1;
   13051   scale_factor=(double) height/windows->image.ximage->height;
   13052   if (crop_info->y > 0)
   13053     y+=(int) (scale_factor*crop_info->y+0.5);
   13054   height=(unsigned int) (scale_factor*crop_info->height+0.5);
   13055   if (height == 0)
   13056     height=1;
   13057   (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
   13058     "%ux%u%+d%+d",width,height,x,y);
   13059 }
   13060 
   13061 /*
   13063 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   13064 %                                                                             %
   13065 %                                                                             %
   13066 %                                                                             %
   13067 +   X T i l e I m a g e                                                       %
   13068 %                                                                             %
   13069 %                                                                             %
   13070 %                                                                             %
   13071 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   13072 %
   13073 %  XTileImage() loads or deletes a selected tile from a visual image directory.
   13074 %  The load or delete command is chosen from a menu.
   13075 %
   13076 %  The format of the XTileImage method is:
   13077 %
   13078 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
   13079 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
   13080 %
   13081 %  A description of each parameter follows:
   13082 %
   13083 %    o tile_image:  XTileImage reads or deletes the tile image
   13084 %      and returns it.  A null image is returned if an error occurs.
   13085 %
   13086 %    o display: Specifies a connection to an X server;  returned from
   13087 %      XOpenDisplay.
   13088 %
   13089 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   13090 %
   13091 %    o windows: Specifies a pointer to a XWindows structure.
   13092 %
   13093 %    o image: the image; returned from ReadImage.
   13094 %
   13095 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
   13096 %      the entire image is refreshed.
   13097 %
   13098 %    o exception: return any errors or warnings in this structure.
   13099 %
   13100 */
   13101 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
   13102   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
   13103 {
   13104   static const char
   13105     *VerbMenu[] =
   13106     {
   13107       "Load",
   13108       "Next",
   13109       "Former",
   13110       "Delete",
   13111       "Update",
   13112       (char *) NULL,
   13113     };
   13114 
   13115   static const ModeType
   13116     TileCommands[] =
   13117     {
   13118       TileLoadCommand,
   13119       TileNextCommand,
   13120       TileFormerCommand,
   13121       TileDeleteCommand,
   13122       TileUpdateCommand
   13123     };
   13124 
   13125   char
   13126     command[MagickPathExtent],
   13127     filename[MagickPathExtent];
   13128 
   13129   Image
   13130     *tile_image;
   13131 
   13132   int
   13133     id,
   13134     status,
   13135     tile,
   13136     x,
   13137     y;
   13138 
   13139   double
   13140     scale_factor;
   13141 
   13142   register char
   13143     *p,
   13144     *q;
   13145 
   13146   register int
   13147     i;
   13148 
   13149   unsigned int
   13150     height,
   13151     width;
   13152 
   13153   /*
   13154     Tile image is relative to montage image configuration.
   13155   */
   13156   x=0;
   13157   y=0;
   13158   width=(unsigned int) image->columns;
   13159   height=(unsigned int) image->rows;
   13160   if (windows->image.crop_geometry != (char *) NULL)
   13161     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
   13162   scale_factor=(double) width/windows->image.ximage->width;
   13163   event->xbutton.x+=windows->image.x;
   13164   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
   13165   scale_factor=(double) height/windows->image.ximage->height;
   13166   event->xbutton.y+=windows->image.y;
   13167   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
   13168   /*
   13169     Determine size and location of each tile in the visual image directory.
   13170   */
   13171   width=(unsigned int) image->columns;
   13172   height=(unsigned int) image->rows;
   13173   x=0;
   13174   y=0;
   13175   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
   13176   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
   13177     (event->xbutton.x-x)/width;
   13178   if (tile < 0)
   13179     {
   13180       /*
   13181         Button press is outside any tile.
   13182       */
   13183       (void) XBell(display,0);
   13184       return((Image *) NULL);
   13185     }
   13186   /*
   13187     Determine file name from the tile directory.
   13188   */
   13189   p=image->directory;
   13190   for (i=tile; (i != 0) && (*p != '\0'); )
   13191   {
   13192     if (*p == '\xff')
   13193       i--;
   13194     p++;
   13195   }
   13196   if (*p == '\0')
   13197     {
   13198       /*
   13199         Button press is outside any tile.
   13200       */
   13201       (void) XBell(display,0);
   13202       return((Image *) NULL);
   13203     }
   13204   /*
   13205     Select a command from the pop-up menu.
   13206   */
   13207   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
   13208   if (id < 0)
   13209     return((Image *) NULL);
   13210   q=p;
   13211   while ((*q != '\n') && (*q != '\0'))
   13212     q++;
   13213   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
   13214   /*
   13215     Perform command for the selected tile.
   13216   */
   13217   XSetCursorState(display,windows,MagickTrue);
   13218   XCheckRefreshWindows(display,windows);
   13219   tile_image=NewImageList();
   13220   switch (TileCommands[id])
   13221   {
   13222     case TileLoadCommand:
   13223     {
   13224       /*
   13225         Load tile image.
   13226       */
   13227       XCheckRefreshWindows(display,windows);
   13228       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
   13229         MagickPathExtent);
   13230       (void) CopyMagickString(resource_info->image_info->filename,filename,
   13231         MagickPathExtent);
   13232       tile_image=ReadImage(resource_info->image_info,exception);
   13233       CatchException(exception);
   13234       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   13235       break;
   13236     }
   13237     case TileNextCommand:
   13238     {
   13239       /*
   13240         Display next image.
   13241       */
   13242       XClientMessage(display,windows->image.id,windows->im_protocols,
   13243         windows->im_next_image,CurrentTime);
   13244       break;
   13245     }
   13246     case TileFormerCommand:
   13247     {
   13248       /*
   13249         Display former image.
   13250       */
   13251       XClientMessage(display,windows->image.id,windows->im_protocols,
   13252         windows->im_former_image,CurrentTime);
   13253       break;
   13254     }
   13255     case TileDeleteCommand:
   13256     {
   13257       /*
   13258         Delete tile image.
   13259       */
   13260       if (IsPathAccessible(filename) == MagickFalse)
   13261         {
   13262           XNoticeWidget(display,windows,"Image file does not exist:",filename);
   13263           break;
   13264         }
   13265       status=XConfirmWidget(display,windows,"Really delete tile",filename);
   13266       if (status <= 0)
   13267         break;
   13268       status=ShredFile(filename);
   13269       if (status != MagickFalse )
   13270         {
   13271           XNoticeWidget(display,windows,"Unable to delete image file:",
   13272             filename);
   13273           break;
   13274         }
   13275     }
   13276     case TileUpdateCommand:
   13277     {
   13278       int
   13279         x_offset,
   13280         y_offset;
   13281 
   13282       PixelInfo
   13283         pixel;
   13284 
   13285       register int
   13286         j;
   13287 
   13288       register Quantum
   13289         *s;
   13290 
   13291       /*
   13292         Ensure all the images exist.
   13293       */
   13294       tile=0;
   13295       GetPixelInfo(image,&pixel);
   13296       for (p=image->directory; *p != '\0'; p++)
   13297       {
   13298         CacheView
   13299           *image_view;
   13300 
   13301         q=p;
   13302         while ((*q != '\xff') && (*q != '\0'))
   13303           q++;
   13304         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
   13305         p=q;
   13306         if (IsPathAccessible(filename) != MagickFalse )
   13307           {
   13308             tile++;
   13309             continue;
   13310           }
   13311         /*
   13312           Overwrite tile with background color.
   13313         */
   13314         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
   13315         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
   13316         image_view=AcquireAuthenticCacheView(image,exception);
   13317         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
   13318         for (i=0; i < (int) height; i++)
   13319         {
   13320           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
   13321             y_offset+i,width,1,exception);
   13322           if (s == (Quantum *) NULL)
   13323             break;
   13324           for (j=0; j < (int) width; j++)
   13325           {
   13326             SetPixelViaPixelInfo(image,&pixel,s);
   13327             s+=GetPixelChannels(image);
   13328           }
   13329           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   13330             break;
   13331         }
   13332         image_view=DestroyCacheView(image_view);
   13333         tile++;
   13334       }
   13335       windows->image.window_changes.width=(int) image->columns;
   13336       windows->image.window_changes.height=(int) image->rows;
   13337       XConfigureImageColormap(display,resource_info,windows,image,exception);
   13338       (void) XConfigureImage(display,resource_info,windows,image,exception);
   13339       break;
   13340     }
   13341     default:
   13342       break;
   13343   }
   13344   XSetCursorState(display,windows,MagickFalse);
   13345   return(tile_image);
   13346 }
   13347 
   13348 /*
   13350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   13351 %                                                                             %
   13352 %                                                                             %
   13353 %                                                                             %
   13354 +   X T r a n s l a t e I m a g e                                             %
   13355 %                                                                             %
   13356 %                                                                             %
   13357 %                                                                             %
   13358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   13359 %
   13360 %  XTranslateImage() translates the image within an Image window by one pixel
   13361 %  as specified by the key symbol.  If the image has a montage string the
   13362 %  translation is respect to the width and height contained within the string.
   13363 %
   13364 %  The format of the XTranslateImage method is:
   13365 %
   13366 %      void XTranslateImage(Display *display,XWindows *windows,
   13367 %        Image *image,const KeySym key_symbol)
   13368 %
   13369 %  A description of each parameter follows:
   13370 %
   13371 %    o display: Specifies a connection to an X server; returned from
   13372 %      XOpenDisplay.
   13373 %
   13374 %    o windows: Specifies a pointer to a XWindows structure.
   13375 %
   13376 %    o image: the image.
   13377 %
   13378 %    o key_symbol: Specifies a KeySym which indicates which side of the image
   13379 %      to trim.
   13380 %
   13381 */
   13382 static void XTranslateImage(Display *display,XWindows *windows,
   13383   Image *image,const KeySym key_symbol)
   13384 {
   13385   char
   13386     text[MagickPathExtent];
   13387 
   13388   int
   13389     x,
   13390     y;
   13391 
   13392   unsigned int
   13393     x_offset,
   13394     y_offset;
   13395 
   13396   /*
   13397     User specified a pan position offset.
   13398   */
   13399   x_offset=windows->image.width;
   13400   y_offset=windows->image.height;
   13401   if (image->montage != (char *) NULL)
   13402     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
   13403   switch ((int) key_symbol)
   13404   {
   13405     case XK_Home:
   13406     case XK_KP_Home:
   13407     {
   13408       windows->image.x=(int) windows->image.width/2;
   13409       windows->image.y=(int) windows->image.height/2;
   13410       break;
   13411     }
   13412     case XK_Left:
   13413     case XK_KP_Left:
   13414     {
   13415       windows->image.x-=x_offset;
   13416       break;
   13417     }
   13418     case XK_Next:
   13419     case XK_Up:
   13420     case XK_KP_Up:
   13421     {
   13422       windows->image.y-=y_offset;
   13423       break;
   13424     }
   13425     case XK_Right:
   13426     case XK_KP_Right:
   13427     {
   13428       windows->image.x+=x_offset;
   13429       break;
   13430     }
   13431     case XK_Prior:
   13432     case XK_Down:
   13433     case XK_KP_Down:
   13434     {
   13435       windows->image.y+=y_offset;
   13436       break;
   13437     }
   13438     default:
   13439       return;
   13440   }
   13441   /*
   13442     Check boundary conditions.
   13443   */
   13444   if (windows->image.x < 0)
   13445     windows->image.x=0;
   13446   else
   13447     if ((int) (windows->image.x+windows->image.width) >
   13448         windows->image.ximage->width)
   13449       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
   13450   if (windows->image.y < 0)
   13451     windows->image.y=0;
   13452   else
   13453     if ((int) (windows->image.y+windows->image.height) >
   13454         windows->image.ximage->height)
   13455       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
   13456   /*
   13457     Refresh Image window.
   13458   */
   13459   (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
   13460     windows->image.width,windows->image.height,windows->image.x,
   13461     windows->image.y);
   13462   XInfoWidget(display,windows,text);
   13463   XCheckRefreshWindows(display,windows);
   13464   XDrawPanRectangle(display,windows);
   13465   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
   13466   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   13467 }
   13468 
   13469 /*
   13471 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   13472 %                                                                             %
   13473 %                                                                             %
   13474 %                                                                             %
   13475 +   X T r i m I m a g e                                                       %
   13476 %                                                                             %
   13477 %                                                                             %
   13478 %                                                                             %
   13479 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   13480 %
   13481 %  XTrimImage() trims the edges from the Image window.
   13482 %
   13483 %  The format of the XTrimImage method is:
   13484 %
   13485 %      MagickBooleanType XTrimImage(Display *display,
   13486 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
   13487 %        ExceptionInfo *exception)
   13488 %
   13489 %  A description of each parameter follows:
   13490 %
   13491 %    o display: Specifies a connection to an X server; returned from
   13492 %      XOpenDisplay.
   13493 %
   13494 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   13495 %
   13496 %    o windows: Specifies a pointer to a XWindows structure.
   13497 %
   13498 %    o image: the image.
   13499 %
   13500 %    o exception: return any errors or warnings in this structure.
   13501 %
   13502 */
   13503 static MagickBooleanType XTrimImage(Display *display,
   13504   XResourceInfo *resource_info,XWindows *windows,Image *image,
   13505   ExceptionInfo *exception)
   13506 {
   13507   RectangleInfo
   13508     trim_info;
   13509 
   13510   register int
   13511     x,
   13512     y;
   13513 
   13514   size_t
   13515     background,
   13516     pixel;
   13517 
   13518   /*
   13519     Trim edges from image.
   13520   */
   13521   XSetCursorState(display,windows,MagickTrue);
   13522   XCheckRefreshWindows(display,windows);
   13523   /*
   13524     Crop the left edge.
   13525   */
   13526   background=XGetPixel(windows->image.ximage,0,0);
   13527   trim_info.width=(size_t) windows->image.ximage->width;
   13528   for (x=0; x < windows->image.ximage->width; x++)
   13529   {
   13530     for (y=0; y < windows->image.ximage->height; y++)
   13531     {
   13532       pixel=XGetPixel(windows->image.ximage,x,y);
   13533       if (pixel != background)
   13534         break;
   13535     }
   13536     if (y < windows->image.ximage->height)
   13537       break;
   13538   }
   13539   trim_info.x=(ssize_t) x;
   13540   if (trim_info.x == (ssize_t) windows->image.ximage->width)
   13541     {
   13542       XSetCursorState(display,windows,MagickFalse);
   13543       return(MagickFalse);
   13544     }
   13545   /*
   13546     Crop the right edge.
   13547   */
   13548   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
   13549   for (x=windows->image.ximage->width-1; x != 0; x--)
   13550   {
   13551     for (y=0; y < windows->image.ximage->height; y++)
   13552     {
   13553       pixel=XGetPixel(windows->image.ximage,x,y);
   13554       if (pixel != background)
   13555         break;
   13556     }
   13557     if (y < windows->image.ximage->height)
   13558       break;
   13559   }
   13560   trim_info.width=(size_t) (x-trim_info.x+1);
   13561   /*
   13562     Crop the top edge.
   13563   */
   13564   background=XGetPixel(windows->image.ximage,0,0);
   13565   trim_info.height=(size_t) windows->image.ximage->height;
   13566   for (y=0; y < windows->image.ximage->height; y++)
   13567   {
   13568     for (x=0; x < windows->image.ximage->width; x++)
   13569     {
   13570       pixel=XGetPixel(windows->image.ximage,x,y);
   13571       if (pixel != background)
   13572         break;
   13573     }
   13574     if (x < windows->image.ximage->width)
   13575       break;
   13576   }
   13577   trim_info.y=(ssize_t) y;
   13578   /*
   13579     Crop the bottom edge.
   13580   */
   13581   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
   13582   for (y=windows->image.ximage->height-1; y != 0; y--)
   13583   {
   13584     for (x=0; x < windows->image.ximage->width; x++)
   13585     {
   13586       pixel=XGetPixel(windows->image.ximage,x,y);
   13587       if (pixel != background)
   13588         break;
   13589     }
   13590     if (x < windows->image.ximage->width)
   13591       break;
   13592   }
   13593   trim_info.height=(size_t) y-trim_info.y+1;
   13594   if (((unsigned int) trim_info.width != windows->image.width) ||
   13595       ((unsigned int) trim_info.height != windows->image.height))
   13596     {
   13597       /*
   13598         Reconfigure Image window as defined by the trimming rectangle.
   13599       */
   13600       XSetCropGeometry(display,windows,&trim_info,image);
   13601       windows->image.window_changes.width=(int) trim_info.width;
   13602       windows->image.window_changes.height=(int) trim_info.height;
   13603       (void) XConfigureImage(display,resource_info,windows,image,exception);
   13604     }
   13605   XSetCursorState(display,windows,MagickFalse);
   13606   return(MagickTrue);
   13607 }
   13608 
   13609 /*
   13611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   13612 %                                                                             %
   13613 %                                                                             %
   13614 %                                                                             %
   13615 +   X V i s u a l D i r e c t o r y I m a g e                                 %
   13616 %                                                                             %
   13617 %                                                                             %
   13618 %                                                                             %
   13619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   13620 %
   13621 %  XVisualDirectoryImage() creates a Visual Image Directory.
   13622 %
   13623 %  The format of the XVisualDirectoryImage method is:
   13624 %
   13625 %      Image *XVisualDirectoryImage(Display *display,
   13626 %        XResourceInfo *resource_info,XWindows *windows,
   13627 %        ExceptionInfo *exception)
   13628 %
   13629 %  A description of each parameter follows:
   13630 %
   13631 %    o display: Specifies a connection to an X server; returned from
   13632 %      XOpenDisplay.
   13633 %
   13634 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   13635 %
   13636 %    o windows: Specifies a pointer to a XWindows structure.
   13637 %
   13638 %    o exception: return any errors or warnings in this structure.
   13639 %
   13640 */
   13641 static Image *XVisualDirectoryImage(Display *display,
   13642   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
   13643 {
   13644 #define TileImageTag  "Scale/Image"
   13645 #define XClientName  "montage"
   13646 
   13647   char
   13648     **filelist;
   13649 
   13650   Image
   13651     *images,
   13652     *montage_image,
   13653     *next_image,
   13654     *thumbnail_image;
   13655 
   13656   ImageInfo
   13657     *read_info;
   13658 
   13659   int
   13660     number_files;
   13661 
   13662   MagickBooleanType
   13663     backdrop;
   13664 
   13665   MagickStatusType
   13666     status;
   13667 
   13668   MontageInfo
   13669     *montage_info;
   13670 
   13671   RectangleInfo
   13672     geometry;
   13673 
   13674   register int
   13675     i;
   13676 
   13677   static char
   13678     filename[MagickPathExtent] = "\0",
   13679     filenames[MagickPathExtent] = "*";
   13680 
   13681   XResourceInfo
   13682     background_resources;
   13683 
   13684   /*
   13685     Request file name from user.
   13686   */
   13687   XFileBrowserWidget(display,windows,"Directory",filenames);
   13688   if (*filenames == '\0')
   13689     return((Image *) NULL);
   13690   /*
   13691     Expand the filenames.
   13692   */
   13693   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
   13694   if (filelist == (char **) NULL)
   13695     {
   13696       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
   13697         filenames);
   13698       return((Image *) NULL);
   13699     }
   13700   number_files=1;
   13701   filelist[0]=filenames;
   13702   status=ExpandFilenames(&number_files,&filelist);
   13703   if ((status == MagickFalse) || (number_files == 0))
   13704     {
   13705       if (number_files == 0)
   13706         ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
   13707       else
   13708         ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
   13709           filenames);
   13710       return((Image *) NULL);
   13711     }
   13712   /*
   13713     Set image background resources.
   13714   */
   13715   background_resources=(*resource_info);
   13716   background_resources.window_id=AcquireString("");
   13717   (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
   13718     "0x%lx",windows->image.id);
   13719   background_resources.backdrop=MagickTrue;
   13720   /*
   13721     Read each image and convert them to a tile.
   13722   */
   13723   backdrop=((windows->visual_info->klass == TrueColor) ||
   13724     (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
   13725   read_info=CloneImageInfo(resource_info->image_info);
   13726   (void) SetImageOption(read_info,"jpeg:size","120x120");
   13727   (void) CloneString(&read_info->size,DefaultTileGeometry);
   13728   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
   13729     (void *) NULL);
   13730   images=NewImageList();
   13731   XSetCursorState(display,windows,MagickTrue);
   13732   XCheckRefreshWindows(display,windows);
   13733   for (i=0; i < (int) number_files; i++)
   13734   {
   13735     (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
   13736     filelist[i]=DestroyString(filelist[i]);
   13737     *read_info->magick='\0';
   13738     next_image=ReadImage(read_info,exception);
   13739     CatchException(exception);
   13740     if (next_image != (Image *) NULL)
   13741       {
   13742         (void) DeleteImageProperty(next_image,"label");
   13743         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
   13744           read_info,next_image,DefaultTileLabel,exception),exception);
   13745         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
   13746           exception);
   13747         thumbnail_image=ThumbnailImage(next_image,geometry.width,
   13748           geometry.height,exception);
   13749         if (thumbnail_image != (Image *) NULL)
   13750           {
   13751             next_image=DestroyImage(next_image);
   13752             next_image=thumbnail_image;
   13753           }
   13754         if (backdrop)
   13755           {
   13756             (void) XDisplayBackgroundImage(display,&background_resources,
   13757               next_image,exception);
   13758             XSetCursorState(display,windows,MagickTrue);
   13759           }
   13760         AppendImageToList(&images,next_image);
   13761         if (images->progress_monitor != (MagickProgressMonitor) NULL)
   13762           {
   13763             MagickBooleanType
   13764               proceed;
   13765 
   13766             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
   13767               (MagickSizeType) number_files);
   13768             if (proceed == MagickFalse)
   13769               break;
   13770           }
   13771       }
   13772   }
   13773   filelist=(char **) RelinquishMagickMemory(filelist);
   13774   if (images == (Image *) NULL)
   13775     {
   13776       read_info=DestroyImageInfo(read_info);
   13777       XSetCursorState(display,windows,MagickFalse);
   13778       ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
   13779       return((Image *) NULL);
   13780     }
   13781   /*
   13782     Create the Visual Image Directory.
   13783   */
   13784   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
   13785   montage_info->pointsize=10;
   13786   if (resource_info->font != (char *) NULL)
   13787     (void) CloneString(&montage_info->font,resource_info->font);
   13788   (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
   13789   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
   13790     images),exception);
   13791   images=DestroyImageList(images);
   13792   montage_info=DestroyMontageInfo(montage_info);
   13793   read_info=DestroyImageInfo(read_info);
   13794   XSetCursorState(display,windows,MagickFalse);
   13795   if (montage_image == (Image *) NULL)
   13796     return(montage_image);
   13797   XClientMessage(display,windows->image.id,windows->im_protocols,
   13798     windows->im_next_image,CurrentTime);
   13799   return(montage_image);
   13800 }
   13801 
   13802 /*
   13804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   13805 %                                                                             %
   13806 %                                                                             %
   13807 %                                                                             %
   13808 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
   13809 %                                                                             %
   13810 %                                                                             %
   13811 %                                                                             %
   13812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   13813 %
   13814 %  XDisplayBackgroundImage() displays an image in the background of a window.
   13815 %
   13816 %  The format of the XDisplayBackgroundImage method is:
   13817 %
   13818 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
   13819 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
   13820 %
   13821 %  A description of each parameter follows:
   13822 %
   13823 %    o display: Specifies a connection to an X server;  returned from
   13824 %      XOpenDisplay.
   13825 %
   13826 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   13827 %
   13828 %    o image: the image.
   13829 %
   13830 %    o exception: return any errors or warnings in this structure.
   13831 %
   13832 */
   13833 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
   13834   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
   13835 {
   13836   char
   13837     geometry[MagickPathExtent],
   13838     visual_type[MagickPathExtent];
   13839 
   13840   int
   13841     height,
   13842     status,
   13843     width;
   13844 
   13845   RectangleInfo
   13846     geometry_info;
   13847 
   13848   static XPixelInfo
   13849     pixel;
   13850 
   13851   static XStandardColormap
   13852     *map_info;
   13853 
   13854   static XVisualInfo
   13855     *visual_info = (XVisualInfo *) NULL;
   13856 
   13857   static XWindowInfo
   13858     window_info;
   13859 
   13860   size_t
   13861     delay;
   13862 
   13863   Window
   13864     root_window;
   13865 
   13866   XGCValues
   13867     context_values;
   13868 
   13869   XResourceInfo
   13870     resources;
   13871 
   13872   XWindowAttributes
   13873     window_attributes;
   13874 
   13875   /*
   13876     Determine target window.
   13877   */
   13878   assert(image != (Image *) NULL);
   13879   assert(image->signature == MagickCoreSignature);
   13880   if (image->debug != MagickFalse )
   13881     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   13882   resources=(*resource_info);
   13883   window_info.id=(Window) NULL;
   13884   root_window=XRootWindow(display,XDefaultScreen(display));
   13885   if (LocaleCompare(resources.window_id,"root") == 0)
   13886     window_info.id=root_window;
   13887   else
   13888     {
   13889       if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
   13890         window_info.id=XWindowByID(display,root_window,
   13891           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
   13892       if (window_info.id == (Window) NULL)
   13893         window_info.id=XWindowByName(display,root_window,resources.window_id);
   13894     }
   13895   if (window_info.id == (Window) NULL)
   13896     {
   13897       ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
   13898         resources.window_id);
   13899       return(MagickFalse);
   13900     }
   13901   /*
   13902     Determine window visual id.
   13903   */
   13904   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
   13905   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
   13906   (void) CopyMagickString(visual_type,"default",MagickPathExtent);
   13907   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
   13908   if (status != 0)
   13909     (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
   13910       XVisualIDFromVisual(window_attributes.visual));
   13911   if (visual_info == (XVisualInfo *) NULL)
   13912     {
   13913       /*
   13914         Allocate standard colormap.
   13915       */
   13916       map_info=XAllocStandardColormap();
   13917       if (map_info == (XStandardColormap *) NULL)
   13918         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
   13919           image->filename);
   13920       map_info->colormap=(Colormap) NULL;
   13921       pixel.pixels=(unsigned long *) NULL;
   13922       /*
   13923         Initialize visual info.
   13924       */
   13925       resources.map_type=(char *) NULL;
   13926       resources.visual_type=visual_type;
   13927       visual_info=XBestVisualInfo(display,map_info,&resources);
   13928       if (visual_info == (XVisualInfo *) NULL)
   13929         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
   13930           resources.visual_type);
   13931       /*
   13932         Initialize window info.
   13933       */
   13934       window_info.ximage=(XImage *) NULL;
   13935       window_info.matte_image=(XImage *) NULL;
   13936       window_info.pixmap=(Pixmap) NULL;
   13937       window_info.matte_pixmap=(Pixmap) NULL;
   13938     }
   13939   /*
   13940     Free previous root colors.
   13941   */
   13942   if (window_info.id == root_window)
   13943     (void) XDestroyWindowColors(display,root_window);
   13944   /*
   13945     Initialize Standard Colormap.
   13946   */
   13947   resources.colormap=SharedColormap;
   13948   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
   13949     exception);
   13950   /*
   13951     Graphic context superclass.
   13952   */
   13953   context_values.background=pixel.background_color.pixel;
   13954   context_values.foreground=pixel.foreground_color.pixel;
   13955   pixel.annotate_context=XCreateGC(display,window_info.id,
   13956     (size_t) (GCBackground | GCForeground),&context_values);
   13957   if (pixel.annotate_context == (GC) NULL)
   13958     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
   13959       image->filename);
   13960   /*
   13961     Initialize Image window attributes.
   13962   */
   13963   window_info.name=AcquireString("\0");
   13964   window_info.icon_name=AcquireString("\0");
   13965   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
   13966     &resources,&window_info);
   13967   /*
   13968     Create the X image.
   13969   */
   13970   window_info.width=(unsigned int) image->columns;
   13971   window_info.height=(unsigned int) image->rows;
   13972   if ((image->columns != window_info.width) ||
   13973       (image->rows != window_info.height))
   13974     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
   13975       image->filename);
   13976   (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
   13977     window_attributes.width,window_attributes.height);
   13978   geometry_info.width=window_info.width;
   13979   geometry_info.height=window_info.height;
   13980   geometry_info.x=(ssize_t) window_info.x;
   13981   geometry_info.y=(ssize_t) window_info.y;
   13982   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
   13983     &geometry_info.width,&geometry_info.height);
   13984   window_info.width=(unsigned int) geometry_info.width;
   13985   window_info.height=(unsigned int) geometry_info.height;
   13986   window_info.x=(int) geometry_info.x;
   13987   window_info.y=(int) geometry_info.y;
   13988   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
   13989     window_info.height,exception);
   13990   if (status == MagickFalse)
   13991     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
   13992       image->filename);
   13993   window_info.x=0;
   13994   window_info.y=0;
   13995   if (image->debug != MagickFalse )
   13996     {
   13997       (void) LogMagickEvent(X11Event,GetMagickModule(),
   13998         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
   13999         (double) image->columns,(double) image->rows);
   14000       if (image->colors != 0)
   14001         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
   14002           image->colors);
   14003       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
   14004     }
   14005   /*
   14006     Adjust image dimensions as specified by backdrop or geometry options.
   14007   */
   14008   width=(int) window_info.width;
   14009   height=(int) window_info.height;
   14010   if (resources.backdrop != MagickFalse )
   14011     {
   14012       /*
   14013         Center image on window.
   14014       */
   14015       window_info.x=(window_attributes.width/2)-
   14016         (window_info.ximage->width/2);
   14017       window_info.y=(window_attributes.height/2)-
   14018         (window_info.ximage->height/2);
   14019       width=window_attributes.width;
   14020       height=window_attributes.height;
   14021     }
   14022   if ((resources.image_geometry != (char *) NULL) &&
   14023       (*resources.image_geometry != '\0'))
   14024     {
   14025       char
   14026         default_geometry[MagickPathExtent];
   14027 
   14028       int
   14029         flags,
   14030         gravity;
   14031 
   14032       XSizeHints
   14033         *size_hints;
   14034 
   14035       /*
   14036         User specified geometry.
   14037       */
   14038       size_hints=XAllocSizeHints();
   14039       if (size_hints == (XSizeHints *) NULL)
   14040         ThrowXWindowFatalException(ResourceLimitFatalError,
   14041           "MemoryAllocationFailed",image->filename);
   14042       size_hints->flags=0L;
   14043       (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
   14044         width,height);
   14045       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
   14046         default_geometry,window_info.border_width,size_hints,&window_info.x,
   14047         &window_info.y,&width,&height,&gravity);
   14048       if (flags & (XValue | YValue))
   14049         {
   14050           width=window_attributes.width;
   14051           height=window_attributes.height;
   14052         }
   14053       (void) XFree((void *) size_hints);
   14054     }
   14055   /*
   14056     Create the X pixmap.
   14057   */
   14058   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
   14059     (unsigned int) height,window_info.depth);
   14060   if (window_info.pixmap == (Pixmap) NULL)
   14061     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
   14062       image->filename);
   14063   /*
   14064     Display pixmap on the window.
   14065   */
   14066   if (((unsigned int) width > window_info.width) ||
   14067       ((unsigned int) height > window_info.height))
   14068     (void) XFillRectangle(display,window_info.pixmap,
   14069       window_info.annotate_context,0,0,(unsigned int) width,
   14070       (unsigned int) height);
   14071   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
   14072     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
   14073     window_info.width,(unsigned int) window_info.height);
   14074   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
   14075   (void) XClearWindow(display,window_info.id);
   14076   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
   14077   XDelay(display,delay == 0UL ? 10UL : delay);
   14078   (void) XSync(display,MagickFalse);
   14079   return(window_info.id == root_window ? MagickTrue : MagickFalse);
   14080 }
   14081 
   14082 /*
   14084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   14085 %                                                                             %
   14086 %                                                                             %
   14087 %                                                                             %
   14088 +   X D i s p l a y I m a g e                                                 %
   14089 %                                                                             %
   14090 %                                                                             %
   14091 %                                                                             %
   14092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   14093 %
   14094 %  XDisplayImage() displays an image via X11.  A new image is created and
   14095 %  returned if the user interactively transforms the displayed image.
   14096 %
   14097 %  The format of the XDisplayImage method is:
   14098 %
   14099 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
   14100 %        char **argv,int argc,Image **image,size_t *state,
   14101 %        ExceptionInfo *exception)
   14102 %
   14103 %  A description of each parameter follows:
   14104 %
   14105 %    o nexus:  Method XDisplayImage returns an image when the
   14106 %      user chooses 'Open Image' from the command menu or picks a tile
   14107 %      from the image directory.  Otherwise a null image is returned.
   14108 %
   14109 %    o display: Specifies a connection to an X server;  returned from
   14110 %      XOpenDisplay.
   14111 %
   14112 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
   14113 %
   14114 %    o argv: Specifies the application's argument list.
   14115 %
   14116 %    o argc: Specifies the number of arguments.
   14117 %
   14118 %    o image: Specifies an address to an address of an Image structure;
   14119 %
   14120 %    o exception: return any errors or warnings in this structure.
   14121 %
   14122 */
   14123 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
   14124   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
   14125 {
   14126 #define MagnifySize  256  /* must be a power of 2 */
   14127 #define MagickMenus  10
   14128 #define MagickTitle  "Commands"
   14129 
   14130   static const char
   14131     *CommandMenu[] =
   14132     {
   14133       "File",
   14134       "Edit",
   14135       "View",
   14136       "Transform",
   14137       "Enhance",
   14138       "Effects",
   14139       "F/X",
   14140       "Image Edit",
   14141       "Miscellany",
   14142       "Help",
   14143       (char *) NULL
   14144     },
   14145     *FileMenu[] =
   14146     {
   14147       "Open...",
   14148       "Next",
   14149       "Former",
   14150       "Select...",
   14151       "Save...",
   14152       "Print...",
   14153       "Delete...",
   14154       "New...",
   14155       "Visual Directory...",
   14156       "Quit",
   14157       (char *) NULL
   14158     },
   14159     *EditMenu[] =
   14160     {
   14161       "Undo",
   14162       "Redo",
   14163       "Cut",
   14164       "Copy",
   14165       "Paste",
   14166       (char *) NULL
   14167     },
   14168     *ViewMenu[] =
   14169     {
   14170       "Half Size",
   14171       "Original Size",
   14172       "Double Size",
   14173       "Resize...",
   14174       "Apply",
   14175       "Refresh",
   14176       "Restore",
   14177       (char *) NULL
   14178     },
   14179     *TransformMenu[] =
   14180     {
   14181       "Crop",
   14182       "Chop",
   14183       "Flop",
   14184       "Flip",
   14185       "Rotate Right",
   14186       "Rotate Left",
   14187       "Rotate...",
   14188       "Shear...",
   14189       "Roll...",
   14190       "Trim Edges",
   14191       (char *) NULL
   14192     },
   14193     *EnhanceMenu[] =
   14194     {
   14195       "Hue...",
   14196       "Saturation...",
   14197       "Brightness...",
   14198       "Gamma...",
   14199       "Spiff",
   14200       "Dull",
   14201       "Contrast Stretch...",
   14202       "Sigmoidal Contrast...",
   14203       "Normalize",
   14204       "Equalize",
   14205       "Negate",
   14206       "Grayscale",
   14207       "Map...",
   14208       "Quantize...",
   14209       (char *) NULL
   14210     },
   14211     *EffectsMenu[] =
   14212     {
   14213       "Despeckle",
   14214       "Emboss",
   14215       "Reduce Noise",
   14216       "Add Noise...",
   14217       "Sharpen...",
   14218       "Blur...",
   14219       "Threshold...",
   14220       "Edge Detect...",
   14221       "Spread...",
   14222       "Shade...",
   14223       "Raise...",
   14224       "Segment...",
   14225       (char *) NULL
   14226     },
   14227     *FXMenu[] =
   14228     {
   14229       "Solarize...",
   14230       "Sepia Tone...",
   14231       "Swirl...",
   14232       "Implode...",
   14233       "Vignette...",
   14234       "Wave...",
   14235       "Oil Paint...",
   14236       "Charcoal Draw...",
   14237       (char *) NULL
   14238     },
   14239     *ImageEditMenu[] =
   14240     {
   14241       "Annotate...",
   14242       "Draw...",
   14243       "Color...",
   14244       "Matte...",
   14245       "Composite...",
   14246       "Add Border...",
   14247       "Add Frame...",
   14248       "Comment...",
   14249       "Launch...",
   14250       "Region of Interest...",
   14251       (char *) NULL
   14252     },
   14253     *MiscellanyMenu[] =
   14254     {
   14255       "Image Info",
   14256       "Zoom Image",
   14257       "Show Preview...",
   14258       "Show Histogram",
   14259       "Show Matte",
   14260       "Background...",
   14261       "Slide Show...",
   14262       "Preferences...",
   14263       (char *) NULL
   14264     },
   14265     *HelpMenu[] =
   14266     {
   14267       "Overview",
   14268       "Browse Documentation",
   14269       "About Display",
   14270       (char *) NULL
   14271     },
   14272     *ShortCutsMenu[] =
   14273     {
   14274       "Next",
   14275       "Former",
   14276       "Open...",
   14277       "Save...",
   14278       "Print...",
   14279       "Undo",
   14280       "Restore",
   14281       "Image Info",
   14282       "Quit",
   14283       (char *) NULL
   14284     },
   14285     *VirtualMenu[] =
   14286     {
   14287       "Image Info",
   14288       "Print",
   14289       "Next",
   14290       "Quit",
   14291       (char *) NULL
   14292     };
   14293 
   14294   static const char
   14295     **Menus[MagickMenus] =
   14296     {
   14297       FileMenu,
   14298       EditMenu,
   14299       ViewMenu,
   14300       TransformMenu,
   14301       EnhanceMenu,
   14302       EffectsMenu,
   14303       FXMenu,
   14304       ImageEditMenu,
   14305       MiscellanyMenu,
   14306       HelpMenu
   14307     };
   14308 
   14309   static CommandType
   14310     CommandMenus[] =
   14311     {
   14312       NullCommand,
   14313       NullCommand,
   14314       NullCommand,
   14315       NullCommand,
   14316       NullCommand,
   14317       NullCommand,
   14318       NullCommand,
   14319       NullCommand,
   14320       NullCommand,
   14321       NullCommand,
   14322     },
   14323     FileCommands[] =
   14324     {
   14325       OpenCommand,
   14326       NextCommand,
   14327       FormerCommand,
   14328       SelectCommand,
   14329       SaveCommand,
   14330       PrintCommand,
   14331       DeleteCommand,
   14332       NewCommand,
   14333       VisualDirectoryCommand,
   14334       QuitCommand
   14335     },
   14336     EditCommands[] =
   14337     {
   14338       UndoCommand,
   14339       RedoCommand,
   14340       CutCommand,
   14341       CopyCommand,
   14342       PasteCommand
   14343     },
   14344     ViewCommands[] =
   14345     {
   14346       HalfSizeCommand,
   14347       OriginalSizeCommand,
   14348       DoubleSizeCommand,
   14349       ResizeCommand,
   14350       ApplyCommand,
   14351       RefreshCommand,
   14352       RestoreCommand
   14353     },
   14354     TransformCommands[] =
   14355     {
   14356       CropCommand,
   14357       ChopCommand,
   14358       FlopCommand,
   14359       FlipCommand,
   14360       RotateRightCommand,
   14361       RotateLeftCommand,
   14362       RotateCommand,
   14363       ShearCommand,
   14364       RollCommand,
   14365       TrimCommand
   14366     },
   14367     EnhanceCommands[] =
   14368     {
   14369       HueCommand,
   14370       SaturationCommand,
   14371       BrightnessCommand,
   14372       GammaCommand,
   14373       SpiffCommand,
   14374       DullCommand,
   14375       ContrastStretchCommand,
   14376       SigmoidalContrastCommand,
   14377       NormalizeCommand,
   14378       EqualizeCommand,
   14379       NegateCommand,
   14380       GrayscaleCommand,
   14381       MapCommand,
   14382       QuantizeCommand
   14383     },
   14384     EffectsCommands[] =
   14385     {
   14386       DespeckleCommand,
   14387       EmbossCommand,
   14388       ReduceNoiseCommand,
   14389       AddNoiseCommand,
   14390       SharpenCommand,
   14391       BlurCommand,
   14392       ThresholdCommand,
   14393       EdgeDetectCommand,
   14394       SpreadCommand,
   14395       ShadeCommand,
   14396       RaiseCommand,
   14397       SegmentCommand
   14398     },
   14399     FXCommands[] =
   14400     {
   14401       SolarizeCommand,
   14402       SepiaToneCommand,
   14403       SwirlCommand,
   14404       ImplodeCommand,
   14405       VignetteCommand,
   14406       WaveCommand,
   14407       OilPaintCommand,
   14408       CharcoalDrawCommand
   14409     },
   14410     ImageEditCommands[] =
   14411     {
   14412       AnnotateCommand,
   14413       DrawCommand,
   14414       ColorCommand,
   14415       MatteCommand,
   14416       CompositeCommand,
   14417       AddBorderCommand,
   14418       AddFrameCommand,
   14419       CommentCommand,
   14420       LaunchCommand,
   14421       RegionofInterestCommand
   14422     },
   14423     MiscellanyCommands[] =
   14424     {
   14425       InfoCommand,
   14426       ZoomCommand,
   14427       ShowPreviewCommand,
   14428       ShowHistogramCommand,
   14429       ShowMatteCommand,
   14430       BackgroundCommand,
   14431       SlideShowCommand,
   14432       PreferencesCommand
   14433     },
   14434     HelpCommands[] =
   14435     {
   14436       HelpCommand,
   14437       BrowseDocumentationCommand,
   14438       VersionCommand
   14439     },
   14440     ShortCutsCommands[] =
   14441     {
   14442       NextCommand,
   14443       FormerCommand,
   14444       OpenCommand,
   14445       SaveCommand,
   14446       PrintCommand,
   14447       UndoCommand,
   14448       RestoreCommand,
   14449       InfoCommand,
   14450       QuitCommand
   14451     },
   14452     VirtualCommands[] =
   14453     {
   14454       InfoCommand,
   14455       PrintCommand,
   14456       NextCommand,
   14457       QuitCommand
   14458     };
   14459 
   14460   static CommandType
   14461     *Commands[MagickMenus] =
   14462     {
   14463       FileCommands,
   14464       EditCommands,
   14465       ViewCommands,
   14466       TransformCommands,
   14467       EnhanceCommands,
   14468       EffectsCommands,
   14469       FXCommands,
   14470       ImageEditCommands,
   14471       MiscellanyCommands,
   14472       HelpCommands
   14473     };
   14474 
   14475   char
   14476     command[MagickPathExtent],
   14477     *directory,
   14478     geometry[MagickPathExtent],
   14479     resource_name[MagickPathExtent];
   14480 
   14481   CommandType
   14482     command_type;
   14483 
   14484   Image
   14485     *display_image,
   14486     *nexus;
   14487 
   14488   int
   14489     entry,
   14490     id;
   14491 
   14492   KeySym
   14493     key_symbol;
   14494 
   14495   MagickStatusType
   14496     context_mask,
   14497     status;
   14498 
   14499   RectangleInfo
   14500     geometry_info;
   14501 
   14502   register int
   14503     i;
   14504 
   14505   static char
   14506     working_directory[MagickPathExtent];
   14507 
   14508   static XPoint
   14509     vid_info;
   14510 
   14511   static XWindowInfo
   14512     *magick_windows[MaxXWindows];
   14513 
   14514   static unsigned int
   14515     number_windows;
   14516 
   14517   struct stat
   14518     attributes;
   14519 
   14520   time_t
   14521     timer,
   14522     timestamp,
   14523     update_time;
   14524 
   14525   unsigned int
   14526     height,
   14527     width;
   14528 
   14529   size_t
   14530     delay;
   14531 
   14532   WarningHandler
   14533     warning_handler;
   14534 
   14535   Window
   14536     root_window;
   14537 
   14538   XClassHint
   14539     *class_hints;
   14540 
   14541   XEvent
   14542     event;
   14543 
   14544   XFontStruct
   14545     *font_info;
   14546 
   14547   XGCValues
   14548     context_values;
   14549 
   14550   XPixelInfo
   14551     *icon_pixel,
   14552     *pixel;
   14553 
   14554   XResourceInfo
   14555     *icon_resources;
   14556 
   14557   XStandardColormap
   14558     *icon_map,
   14559     *map_info;
   14560 
   14561   XVisualInfo
   14562     *icon_visual,
   14563     *visual_info;
   14564 
   14565   XWindowChanges
   14566     window_changes;
   14567 
   14568   XWindows
   14569     *windows;
   14570 
   14571   XWMHints
   14572     *manager_hints;
   14573 
   14574   assert(image != (Image **) NULL);
   14575   assert((*image)->signature == MagickCoreSignature);
   14576   if ((*image)->debug != MagickFalse )
   14577     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
   14578   display_image=(*image);
   14579   warning_handler=(WarningHandler) NULL;
   14580   windows=XSetWindows((XWindows *) ~0);
   14581   if (windows != (XWindows *) NULL)
   14582     {
   14583       int
   14584         status;
   14585 
   14586       if (*working_directory == '\0')
   14587         (void) CopyMagickString(working_directory,".",MagickPathExtent);
   14588       status=chdir(working_directory);
   14589       if (status == -1)
   14590         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
   14591           "UnableToOpenFile","%s",working_directory);
   14592       warning_handler=resource_info->display_warnings ?
   14593         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
   14594       warning_handler=resource_info->display_warnings ?
   14595         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
   14596     }
   14597   else
   14598     {
   14599       /*
   14600         Allocate windows structure.
   14601       */
   14602       resource_info->colors=display_image->colors;
   14603       windows=XSetWindows(XInitializeWindows(display,resource_info));
   14604       if (windows == (XWindows *) NULL)
   14605         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
   14606           (*image)->filename);
   14607       /*
   14608         Initialize window id's.
   14609       */
   14610       number_windows=0;
   14611       magick_windows[number_windows++]=(&windows->icon);
   14612       magick_windows[number_windows++]=(&windows->backdrop);
   14613       magick_windows[number_windows++]=(&windows->image);
   14614       magick_windows[number_windows++]=(&windows->info);
   14615       magick_windows[number_windows++]=(&windows->command);
   14616       magick_windows[number_windows++]=(&windows->widget);
   14617       magick_windows[number_windows++]=(&windows->popup);
   14618       magick_windows[number_windows++]=(&windows->magnify);
   14619       magick_windows[number_windows++]=(&windows->pan);
   14620       for (i=0; i < (int) number_windows; i++)
   14621         magick_windows[i]->id=(Window) NULL;
   14622       vid_info.x=0;
   14623       vid_info.y=0;
   14624     }
   14625   /*
   14626     Initialize font info.
   14627   */
   14628   if (windows->font_info != (XFontStruct *) NULL)
   14629     (void) XFreeFont(display,windows->font_info);
   14630   windows->font_info=XBestFont(display,resource_info,MagickFalse);
   14631   if (windows->font_info == (XFontStruct *) NULL)
   14632     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
   14633       resource_info->font);
   14634   /*
   14635     Initialize Standard Colormap.
   14636   */
   14637   map_info=windows->map_info;
   14638   icon_map=windows->icon_map;
   14639   visual_info=windows->visual_info;
   14640   icon_visual=windows->icon_visual;
   14641   pixel=windows->pixel_info;
   14642   icon_pixel=windows->icon_pixel;
   14643   font_info=windows->font_info;
   14644   icon_resources=windows->icon_resources;
   14645   class_hints=windows->class_hints;
   14646   manager_hints=windows->manager_hints;
   14647   root_window=XRootWindow(display,visual_info->screen);
   14648   nexus=NewImageList();
   14649   if (display_image->debug != MagickFalse )
   14650     {
   14651       (void) LogMagickEvent(X11Event,GetMagickModule(),
   14652         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
   14653         (double) display_image->scene,(double) display_image->columns,
   14654         (double) display_image->rows);
   14655       if (display_image->colors != 0)
   14656         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
   14657           display_image->colors);
   14658       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
   14659         display_image->magick);
   14660     }
   14661   XMakeStandardColormap(display,visual_info,resource_info,display_image,
   14662     map_info,pixel,exception);
   14663   display_image->taint=MagickFalse;
   14664   /*
   14665     Initialize graphic context.
   14666   */
   14667   windows->context.id=(Window) NULL;
   14668   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
   14669     resource_info,&windows->context);
   14670   (void) CloneString(&class_hints->res_name,resource_info->client_name);
   14671   (void) CloneString(&class_hints->res_class,resource_info->client_name);
   14672   class_hints->res_class[0]=(char) LocaleUppercase((int)
   14673     class_hints->res_class[0]);
   14674   manager_hints->flags=InputHint | StateHint;
   14675   manager_hints->input=MagickFalse;
   14676   manager_hints->initial_state=WithdrawnState;
   14677   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
   14678     &windows->context);
   14679   if (display_image->debug != MagickFalse )
   14680     (void) LogMagickEvent(X11Event,GetMagickModule(),
   14681       "Window id: 0x%lx (context)",windows->context.id);
   14682   context_values.background=pixel->background_color.pixel;
   14683   context_values.font=font_info->fid;
   14684   context_values.foreground=pixel->foreground_color.pixel;
   14685   context_values.graphics_exposures=MagickFalse;
   14686   context_mask=(MagickStatusType)
   14687     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
   14688   if (pixel->annotate_context != (GC) NULL)
   14689     (void) XFreeGC(display,pixel->annotate_context);
   14690   pixel->annotate_context=XCreateGC(display,windows->context.id,
   14691     context_mask,&context_values);
   14692   if (pixel->annotate_context == (GC) NULL)
   14693     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
   14694       display_image->filename);
   14695   context_values.background=pixel->depth_color.pixel;
   14696   if (pixel->widget_context != (GC) NULL)
   14697     (void) XFreeGC(display,pixel->widget_context);
   14698   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
   14699     &context_values);
   14700   if (pixel->widget_context == (GC) NULL)
   14701     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
   14702       display_image->filename);
   14703   context_values.background=pixel->foreground_color.pixel;
   14704   context_values.foreground=pixel->background_color.pixel;
   14705   context_values.plane_mask=context_values.background ^
   14706     context_values.foreground;
   14707   if (pixel->highlight_context != (GC) NULL)
   14708     (void) XFreeGC(display,pixel->highlight_context);
   14709   pixel->highlight_context=XCreateGC(display,windows->context.id,
   14710     (size_t) (context_mask | GCPlaneMask),&context_values);
   14711   if (pixel->highlight_context == (GC) NULL)
   14712     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
   14713       display_image->filename);
   14714   (void) XDestroyWindow(display,windows->context.id);
   14715   /*
   14716     Initialize icon window.
   14717   */
   14718   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
   14719     icon_resources,&windows->icon);
   14720   windows->icon.geometry=resource_info->icon_geometry;
   14721   XBestIconSize(display,&windows->icon,display_image);
   14722   windows->icon.attributes.colormap=XDefaultColormap(display,
   14723     icon_visual->screen);
   14724   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
   14725   manager_hints->flags=InputHint | StateHint;
   14726   manager_hints->input=MagickFalse;
   14727   manager_hints->initial_state=IconicState;
   14728   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
   14729     &windows->icon);
   14730   if (display_image->debug != MagickFalse )
   14731     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
   14732       windows->icon.id);
   14733   /*
   14734     Initialize graphic context for icon window.
   14735   */
   14736   if (icon_pixel->annotate_context != (GC) NULL)
   14737     (void) XFreeGC(display,icon_pixel->annotate_context);
   14738   context_values.background=icon_pixel->background_color.pixel;
   14739   context_values.foreground=icon_pixel->foreground_color.pixel;
   14740   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
   14741     (size_t) (GCBackground | GCForeground),&context_values);
   14742   if (icon_pixel->annotate_context == (GC) NULL)
   14743     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
   14744       display_image->filename);
   14745   windows->icon.annotate_context=icon_pixel->annotate_context;
   14746   /*
   14747     Initialize Image window.
   14748   */
   14749   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
   14750     &windows->image);
   14751   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
   14752   if (resource_info->use_shared_memory == MagickFalse)
   14753     windows->image.shared_memory=MagickFalse;
   14754   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
   14755     {
   14756       char
   14757         *title;
   14758 
   14759       title=InterpretImageProperties(resource_info->image_info,display_image,
   14760         resource_info->title,exception);
   14761       (void) CopyMagickString(windows->image.name,title,MagickPathExtent);
   14762       (void) CopyMagickString(windows->image.icon_name,title,MagickPathExtent);
   14763       title=DestroyString(title);
   14764     }
   14765   else
   14766     {
   14767       char
   14768         filename[MagickPathExtent];
   14769 
   14770       /*
   14771         Window name is the base of the filename.
   14772       */
   14773       GetPathComponent(display_image->magick_filename,TailPath,filename);
   14774       if (display_image->scene == 0)
   14775         (void) FormatLocaleString(windows->image.name,MagickPathExtent,
   14776           "%s: %s",MagickPackageName,filename);
   14777       else
   14778         (void) FormatLocaleString(windows->image.name,MagickPathExtent,
   14779           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
   14780           (double) display_image->scene,(double) GetImageListLength(
   14781           display_image));
   14782       (void) CopyMagickString(windows->image.icon_name,filename,
   14783         MagickPathExtent);
   14784     }
   14785   if (resource_info->immutable)
   14786     windows->image.immutable=MagickTrue;
   14787   windows->image.use_pixmap=resource_info->use_pixmap;
   14788   windows->image.geometry=resource_info->image_geometry;
   14789   (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
   14790     XDisplayWidth(display,visual_info->screen),
   14791     XDisplayHeight(display,visual_info->screen));
   14792   geometry_info.width=display_image->columns;
   14793   geometry_info.height=display_image->rows;
   14794   geometry_info.x=0;
   14795   geometry_info.y=0;
   14796   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
   14797     &geometry_info.width,&geometry_info.height);
   14798   windows->image.width=(unsigned int) geometry_info.width;
   14799   windows->image.height=(unsigned int) geometry_info.height;
   14800   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
   14801     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
   14802     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
   14803     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
   14804   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
   14805     resource_info,&windows->backdrop);
   14806   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
   14807     {
   14808       /*
   14809         Initialize backdrop window.
   14810       */
   14811       windows->backdrop.x=0;
   14812       windows->backdrop.y=0;
   14813       (void) CloneString(&windows->backdrop.name,"Backdrop");
   14814       windows->backdrop.flags=(size_t) (USSize | USPosition);
   14815       windows->backdrop.width=(unsigned int)
   14816         XDisplayWidth(display,visual_info->screen);
   14817       windows->backdrop.height=(unsigned int)
   14818         XDisplayHeight(display,visual_info->screen);
   14819       windows->backdrop.border_width=0;
   14820       windows->backdrop.immutable=MagickTrue;
   14821       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
   14822         ButtonReleaseMask;
   14823       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
   14824         StructureNotifyMask;
   14825       manager_hints->flags=IconWindowHint | InputHint | StateHint;
   14826       manager_hints->icon_window=windows->icon.id;
   14827       manager_hints->input=MagickTrue;
   14828       manager_hints->initial_state=resource_info->iconic ? IconicState :
   14829         NormalState;
   14830       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
   14831         &windows->backdrop);
   14832       if (display_image->debug != MagickFalse )
   14833         (void) LogMagickEvent(X11Event,GetMagickModule(),
   14834           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
   14835       (void) XMapWindow(display,windows->backdrop.id);
   14836       (void) XClearWindow(display,windows->backdrop.id);
   14837       if (windows->image.id != (Window) NULL)
   14838         {
   14839           (void) XDestroyWindow(display,windows->image.id);
   14840           windows->image.id=(Window) NULL;
   14841         }
   14842       /*
   14843         Position image in the center the backdrop.
   14844       */
   14845       windows->image.flags|=USPosition;
   14846       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
   14847         (windows->image.width/2);
   14848       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
   14849         (windows->image.height/2);
   14850     }
   14851   manager_hints->flags=IconWindowHint | InputHint | StateHint;
   14852   manager_hints->icon_window=windows->icon.id;
   14853   manager_hints->input=MagickTrue;
   14854   manager_hints->initial_state=resource_info->iconic ? IconicState :
   14855     NormalState;
   14856   if (windows->group_leader.id != (Window) NULL)
   14857     {
   14858       /*
   14859         Follow the leader.
   14860       */
   14861       manager_hints->flags|=WindowGroupHint;
   14862       manager_hints->window_group=windows->group_leader.id;
   14863       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
   14864       if (display_image->debug != MagickFalse )
   14865         (void) LogMagickEvent(X11Event,GetMagickModule(),
   14866           "Window id: 0x%lx (group leader)",windows->group_leader.id);
   14867     }
   14868   XMakeWindow(display,
   14869     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
   14870     argv,argc,class_hints,manager_hints,&windows->image);
   14871   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
   14872     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
   14873   if (windows->group_leader.id != (Window) NULL)
   14874     (void) XSetTransientForHint(display,windows->image.id,
   14875       windows->group_leader.id);
   14876   if (display_image->debug != MagickFalse )
   14877     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
   14878       windows->image.id);
   14879   /*
   14880     Initialize Info widget.
   14881   */
   14882   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
   14883     &windows->info);
   14884   (void) CloneString(&windows->info.name,"Info");
   14885   (void) CloneString(&windows->info.icon_name,"Info");
   14886   windows->info.border_width=1;
   14887   windows->info.x=2;
   14888   windows->info.y=2;
   14889   windows->info.flags|=PPosition;
   14890   windows->info.attributes.win_gravity=UnmapGravity;
   14891   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
   14892     StructureNotifyMask;
   14893   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
   14894   manager_hints->input=MagickFalse;
   14895   manager_hints->initial_state=NormalState;
   14896   manager_hints->window_group=windows->image.id;
   14897   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
   14898     &windows->info);
   14899   windows->info.highlight_stipple=XCreateBitmapFromData(display,
   14900     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
   14901   windows->info.shadow_stipple=XCreateBitmapFromData(display,
   14902     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
   14903   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
   14904   if (windows->image.mapped != MagickFalse )
   14905     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   14906   if (display_image->debug != MagickFalse )
   14907     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
   14908       windows->info.id);
   14909   /*
   14910     Initialize Command widget.
   14911   */
   14912   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
   14913     resource_info,&windows->command);
   14914   windows->command.data=MagickMenus;
   14915   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
   14916   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
   14917     resource_info->client_name);
   14918   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
   14919     resource_name,"geometry",(char *) NULL);
   14920   (void) CloneString(&windows->command.name,MagickTitle);
   14921   windows->command.border_width=0;
   14922   windows->command.flags|=PPosition;
   14923   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
   14924     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
   14925     OwnerGrabButtonMask | StructureNotifyMask;
   14926   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
   14927   manager_hints->input=MagickTrue;
   14928   manager_hints->initial_state=NormalState;
   14929   manager_hints->window_group=windows->image.id;
   14930   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
   14931     &windows->command);
   14932   windows->command.highlight_stipple=XCreateBitmapFromData(display,
   14933     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
   14934     HighlightHeight);
   14935   windows->command.shadow_stipple=XCreateBitmapFromData(display,
   14936     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
   14937   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
   14938   if (windows->command.mapped != MagickFalse )
   14939     (void) XMapRaised(display,windows->command.id);
   14940   if (display_image->debug != MagickFalse )
   14941     (void) LogMagickEvent(X11Event,GetMagickModule(),
   14942       "Window id: 0x%lx (command)",windows->command.id);
   14943   /*
   14944     Initialize Widget window.
   14945   */
   14946   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
   14947     resource_info,&windows->widget);
   14948   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
   14949     resource_info->client_name);
   14950   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
   14951     resource_name,"geometry",(char *) NULL);
   14952   windows->widget.border_width=0;
   14953   windows->widget.flags|=PPosition;
   14954   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
   14955     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
   14956     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
   14957     StructureNotifyMask;
   14958   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
   14959   manager_hints->input=MagickTrue;
   14960   manager_hints->initial_state=NormalState;
   14961   manager_hints->window_group=windows->image.id;
   14962   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
   14963     &windows->widget);
   14964   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
   14965     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
   14966   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
   14967     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
   14968   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
   14969   if (display_image->debug != MagickFalse )
   14970     (void) LogMagickEvent(X11Event,GetMagickModule(),
   14971       "Window id: 0x%lx (widget)",windows->widget.id);
   14972   /*
   14973     Initialize popup window.
   14974   */
   14975   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
   14976     resource_info,&windows->popup);
   14977   windows->popup.border_width=0;
   14978   windows->popup.flags|=PPosition;
   14979   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
   14980     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
   14981     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
   14982   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
   14983   manager_hints->input=MagickTrue;
   14984   manager_hints->initial_state=NormalState;
   14985   manager_hints->window_group=windows->image.id;
   14986   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
   14987     &windows->popup);
   14988   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
   14989     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
   14990   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
   14991     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
   14992   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
   14993   if (display_image->debug != MagickFalse )
   14994     (void) LogMagickEvent(X11Event,GetMagickModule(),
   14995       "Window id: 0x%lx (pop up)",windows->popup.id);
   14996   /*
   14997     Initialize Magnify window and cursor.
   14998   */
   14999   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
   15000     resource_info,&windows->magnify);
   15001   if (resource_info->use_shared_memory == MagickFalse)
   15002     windows->magnify.shared_memory=MagickFalse;
   15003   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
   15004     resource_info->client_name);
   15005   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
   15006     resource_name,"geometry",(char *) NULL);
   15007   (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,"Magnify %uX",
   15008     resource_info->magnify);
   15009   if (windows->magnify.cursor != (Cursor) NULL)
   15010     (void) XFreeCursor(display,windows->magnify.cursor);
   15011   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
   15012     map_info->colormap,resource_info->background_color,
   15013     resource_info->foreground_color);
   15014   if (windows->magnify.cursor == (Cursor) NULL)
   15015     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
   15016       display_image->filename);
   15017   windows->magnify.width=MagnifySize;
   15018   windows->magnify.height=MagnifySize;
   15019   windows->magnify.flags|=PPosition;
   15020   windows->magnify.min_width=MagnifySize;
   15021   windows->magnify.min_height=MagnifySize;
   15022   windows->magnify.width_inc=MagnifySize;
   15023   windows->magnify.height_inc=MagnifySize;
   15024   windows->magnify.data=resource_info->magnify;
   15025   windows->magnify.attributes.cursor=windows->magnify.cursor;
   15026   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
   15027     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
   15028     StructureNotifyMask;
   15029   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
   15030   manager_hints->input=MagickTrue;
   15031   manager_hints->initial_state=NormalState;
   15032   manager_hints->window_group=windows->image.id;
   15033   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
   15034     &windows->magnify);
   15035   if (display_image->debug != MagickFalse )
   15036     (void) LogMagickEvent(X11Event,GetMagickModule(),
   15037       "Window id: 0x%lx (magnify)",windows->magnify.id);
   15038   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
   15039   /*
   15040     Initialize panning window.
   15041   */
   15042   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
   15043     resource_info,&windows->pan);
   15044   (void) CloneString(&windows->pan.name,"Pan Icon");
   15045   windows->pan.width=windows->icon.width;
   15046   windows->pan.height=windows->icon.height;
   15047   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
   15048     resource_info->client_name);
   15049   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
   15050     resource_name,"geometry",(char *) NULL);
   15051   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
   15052     &windows->pan.width,&windows->pan.height);
   15053   windows->pan.flags|=PPosition;
   15054   windows->pan.immutable=MagickTrue;
   15055   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
   15056     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
   15057     StructureNotifyMask;
   15058   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
   15059   manager_hints->input=MagickFalse;
   15060   manager_hints->initial_state=NormalState;
   15061   manager_hints->window_group=windows->image.id;
   15062   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
   15063     &windows->pan);
   15064   if (display_image->debug != MagickFalse )
   15065     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
   15066       windows->pan.id);
   15067   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
   15068   if (windows->info.mapped != MagickFalse )
   15069     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   15070   if ((windows->image.mapped == MagickFalse) ||
   15071       (windows->backdrop.id != (Window) NULL))
   15072     (void) XMapWindow(display,windows->image.id);
   15073   /*
   15074     Set our progress monitor and warning handlers.
   15075   */
   15076   if (warning_handler == (WarningHandler) NULL)
   15077     {
   15078       warning_handler=resource_info->display_warnings ?
   15079         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
   15080       warning_handler=resource_info->display_warnings ?
   15081         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
   15082     }
   15083   /*
   15084     Initialize Image and Magnify X images.
   15085   */
   15086   windows->image.x=0;
   15087   windows->image.y=0;
   15088   windows->magnify.shape=MagickFalse;
   15089   width=(unsigned int) display_image->columns;
   15090   height=(unsigned int) display_image->rows;
   15091   if ((display_image->columns != width) || (display_image->rows != height))
   15092     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
   15093       display_image->filename);
   15094   status=XMakeImage(display,resource_info,&windows->image,display_image,
   15095     width,height,exception);
   15096   if (status == MagickFalse)
   15097     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
   15098       display_image->filename);
   15099   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
   15100     windows->magnify.width,windows->magnify.height,exception);
   15101   if (status == MagickFalse)
   15102     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
   15103       display_image->filename);
   15104   if (windows->magnify.mapped != MagickFalse )
   15105     (void) XMapRaised(display,windows->magnify.id);
   15106   if (windows->pan.mapped != MagickFalse )
   15107     (void) XMapRaised(display,windows->pan.id);
   15108   windows->image.window_changes.width=(int) display_image->columns;
   15109   windows->image.window_changes.height=(int) display_image->rows;
   15110   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
   15111   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   15112   (void) XSync(display,MagickFalse);
   15113   /*
   15114     Respond to events.
   15115   */
   15116   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
   15117   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
   15118   update_time=0;
   15119   if (resource_info->update != MagickFalse )
   15120     {
   15121       MagickBooleanType
   15122         status;
   15123 
   15124       /*
   15125         Determine when file data was last modified.
   15126       */
   15127       status=GetPathAttributes(display_image->filename,&attributes);
   15128       if (status != MagickFalse )
   15129         update_time=attributes.st_mtime;
   15130     }
   15131   *state&=(~FormerImageState);
   15132   *state&=(~MontageImageState);
   15133   *state&=(~NextImageState);
   15134   do
   15135   {
   15136     /*
   15137       Handle a window event.
   15138     */
   15139     if (windows->image.mapped != MagickFalse )
   15140       if ((display_image->delay != 0) || (resource_info->update != 0))
   15141         {
   15142           if (timer < time((time_t *) NULL))
   15143             {
   15144               if (resource_info->update == MagickFalse)
   15145                 *state|=NextImageState | ExitState;
   15146               else
   15147                 {
   15148                   MagickBooleanType
   15149                     status;
   15150 
   15151                   /*
   15152                     Determine if image file was modified.
   15153                   */
   15154                   status=GetPathAttributes(display_image->filename,&attributes);
   15155                   if (status != MagickFalse )
   15156                     if (update_time != attributes.st_mtime)
   15157                       {
   15158                         /*
   15159                           Redisplay image.
   15160                         */
   15161                         (void) FormatLocaleString(
   15162                           resource_info->image_info->filename,MagickPathExtent,
   15163                           "%s:%s",display_image->magick,
   15164                           display_image->filename);
   15165                         nexus=ReadImage(resource_info->image_info,exception);
   15166                         if (nexus != (Image *) NULL)
   15167                           *state|=NextImageState | ExitState;
   15168                       }
   15169                   delay=display_image->delay/MagickMax(
   15170                     display_image->ticks_per_second,1L);
   15171                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
   15172                 }
   15173             }
   15174           if (XEventsQueued(display,QueuedAfterFlush) == 0)
   15175             {
   15176               /*
   15177                 Do not block if delay > 0.
   15178               */
   15179               XDelay(display,SuspendTime << 2);
   15180               continue;
   15181             }
   15182         }
   15183     timestamp=time((time_t *) NULL);
   15184     (void) XNextEvent(display,&event);
   15185     if ((windows->image.stasis == MagickFalse) ||
   15186         (windows->magnify.stasis == MagickFalse))
   15187       {
   15188         if ((time((time_t *) NULL)-timestamp) > 0)
   15189           {
   15190             windows->image.stasis=MagickTrue;
   15191             windows->magnify.stasis=MagickTrue;
   15192           }
   15193       }
   15194     if (event.xany.window == windows->command.id)
   15195       {
   15196         /*
   15197           Select a command from the Command widget.
   15198         */
   15199         id=XCommandWidget(display,windows,CommandMenu,&event);
   15200         if (id < 0)
   15201           continue;
   15202         (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
   15203         command_type=CommandMenus[id];
   15204         if (id < MagickMenus)
   15205           {
   15206             /*
   15207               Select a command from a pop-up menu.
   15208             */
   15209             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
   15210               command);
   15211             if (entry < 0)
   15212               continue;
   15213             (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
   15214             command_type=Commands[id][entry];
   15215           }
   15216         if (command_type != NullCommand)
   15217           nexus=XMagickCommand(display,resource_info,windows,command_type,
   15218             &display_image,exception);
   15219         continue;
   15220       }
   15221     switch (event.type)
   15222     {
   15223       case ButtonPress:
   15224       {
   15225         if (display_image->debug != MagickFalse )
   15226           (void) LogMagickEvent(X11Event,GetMagickModule(),
   15227             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
   15228             event.xbutton.button,event.xbutton.x,event.xbutton.y);
   15229         if ((event.xbutton.button == Button3) &&
   15230             (event.xbutton.state & Mod1Mask))
   15231           {
   15232             /*
   15233               Convert Alt-Button3 to Button2.
   15234             */
   15235             event.xbutton.button=Button2;
   15236             event.xbutton.state&=(~Mod1Mask);
   15237           }
   15238         if (event.xbutton.window == windows->backdrop.id)
   15239           {
   15240             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
   15241               event.xbutton.time);
   15242             break;
   15243           }
   15244         if (event.xbutton.window == windows->image.id)
   15245           {
   15246             switch (event.xbutton.button)
   15247             {
   15248               case Button1:
   15249               {
   15250                 if (resource_info->immutable)
   15251                   {
   15252                     /*
   15253                       Select a command from the Virtual menu.
   15254                     */
   15255                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
   15256                       command);
   15257                     if (entry >= 0)
   15258                       nexus=XMagickCommand(display,resource_info,windows,
   15259                         VirtualCommands[entry],&display_image,exception);
   15260                     break;
   15261                   }
   15262                 /*
   15263                   Map/unmap Command widget.
   15264                 */
   15265                 if (windows->command.mapped != MagickFalse )
   15266                   (void) XWithdrawWindow(display,windows->command.id,
   15267                     windows->command.screen);
   15268                 else
   15269                   {
   15270                     (void) XCommandWidget(display,windows,CommandMenu,
   15271                       (XEvent *) NULL);
   15272                     (void) XMapRaised(display,windows->command.id);
   15273                   }
   15274                 break;
   15275               }
   15276               case Button2:
   15277               {
   15278                 /*
   15279                   User pressed the image magnify button.
   15280                 */
   15281                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
   15282                   &display_image,exception);
   15283                 XMagnifyImage(display,windows,&event,exception);
   15284                 break;
   15285               }
   15286               case Button3:
   15287               {
   15288                 if (resource_info->immutable)
   15289                   {
   15290                     /*
   15291                       Select a command from the Virtual menu.
   15292                     */
   15293                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
   15294                       command);
   15295                     if (entry >= 0)
   15296                       nexus=XMagickCommand(display,resource_info,windows,
   15297                         VirtualCommands[entry],&display_image,exception);
   15298                     break;
   15299                   }
   15300                 if (display_image->montage != (char *) NULL)
   15301                   {
   15302                     /*
   15303                       Open or delete a tile from a visual image directory.
   15304                     */
   15305                     nexus=XTileImage(display,resource_info,windows,
   15306                       display_image,&event,exception);
   15307                     if (nexus != (Image *) NULL)
   15308                       *state|=MontageImageState | NextImageState | ExitState;
   15309                     vid_info.x=(short int) windows->image.x;
   15310                     vid_info.y=(short int) windows->image.y;
   15311                     break;
   15312                   }
   15313                 /*
   15314                   Select a command from the Short Cuts menu.
   15315                 */
   15316                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
   15317                   command);
   15318                 if (entry >= 0)
   15319                   nexus=XMagickCommand(display,resource_info,windows,
   15320                     ShortCutsCommands[entry],&display_image,exception);
   15321                 break;
   15322               }
   15323               case Button4:
   15324               {
   15325                 /*
   15326                   Wheel up.
   15327                 */
   15328                 XTranslateImage(display,windows,*image,XK_Up);
   15329                 break;
   15330               }
   15331               case Button5:
   15332               {
   15333                 /*
   15334                   Wheel down.
   15335                 */
   15336                 XTranslateImage(display,windows,*image,XK_Down);
   15337                 break;
   15338               }
   15339               default:
   15340                 break;
   15341             }
   15342             break;
   15343           }
   15344         if (event.xbutton.window == windows->magnify.id)
   15345           {
   15346             int
   15347               factor;
   15348 
   15349             static const char
   15350               *MagnifyMenu[] =
   15351               {
   15352                 "2",
   15353                 "4",
   15354                 "5",
   15355                 "6",
   15356                 "7",
   15357                 "8",
   15358                 "9",
   15359                 "3",
   15360                 (char *) NULL,
   15361               };
   15362 
   15363             static KeySym
   15364               MagnifyCommands[] =
   15365               {
   15366                 XK_2,
   15367                 XK_4,
   15368                 XK_5,
   15369                 XK_6,
   15370                 XK_7,
   15371                 XK_8,
   15372                 XK_9,
   15373                 XK_3
   15374               };
   15375 
   15376             /*
   15377               Select a magnify factor from the pop-up menu.
   15378             */
   15379             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
   15380             if (factor >= 0)
   15381               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
   15382                 exception);
   15383             break;
   15384           }
   15385         if (event.xbutton.window == windows->pan.id)
   15386           {
   15387             switch (event.xbutton.button)
   15388             {
   15389               case Button4:
   15390               {
   15391                 /*
   15392                   Wheel up.
   15393                 */
   15394                 XTranslateImage(display,windows,*image,XK_Up);
   15395                 break;
   15396               }
   15397               case Button5:
   15398               {
   15399                 /*
   15400                   Wheel down.
   15401                 */
   15402                 XTranslateImage(display,windows,*image,XK_Down);
   15403                 break;
   15404               }
   15405               default:
   15406               {
   15407                 XPanImage(display,windows,&event,exception);
   15408                 break;
   15409               }
   15410             }
   15411             break;
   15412           }
   15413         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
   15414           1L);
   15415         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
   15416         break;
   15417       }
   15418       case ButtonRelease:
   15419       {
   15420         if (display_image->debug != MagickFalse )
   15421           (void) LogMagickEvent(X11Event,GetMagickModule(),
   15422             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
   15423             event.xbutton.button,event.xbutton.x,event.xbutton.y);
   15424         break;
   15425       }
   15426       case ClientMessage:
   15427       {
   15428         if (display_image->debug != MagickFalse )
   15429           (void) LogMagickEvent(X11Event,GetMagickModule(),
   15430             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
   15431             event.xclient.message_type,event.xclient.format,(unsigned long)
   15432             event.xclient.data.l[0]);
   15433         if (event.xclient.message_type == windows->im_protocols)
   15434           {
   15435             if (*event.xclient.data.l == (long) windows->im_update_widget)
   15436               {
   15437                 (void) CloneString(&windows->command.name,MagickTitle);
   15438                 windows->command.data=MagickMenus;
   15439                 (void) XCommandWidget(display,windows,CommandMenu,
   15440                   (XEvent *) NULL);
   15441                 break;
   15442               }
   15443             if (*event.xclient.data.l == (long) windows->im_update_colormap)
   15444               {
   15445                 /*
   15446                   Update graphic context and window colormap.
   15447                 */
   15448                 for (i=0; i < (int) number_windows; i++)
   15449                 {
   15450                   if (magick_windows[i]->id == windows->icon.id)
   15451                     continue;
   15452                   context_values.background=pixel->background_color.pixel;
   15453                   context_values.foreground=pixel->foreground_color.pixel;
   15454                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
   15455                     context_mask,&context_values);
   15456                   (void) XChangeGC(display,magick_windows[i]->widget_context,
   15457                     context_mask,&context_values);
   15458                   context_values.background=pixel->foreground_color.pixel;
   15459                   context_values.foreground=pixel->background_color.pixel;
   15460                   context_values.plane_mask=context_values.background ^
   15461                     context_values.foreground;
   15462                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
   15463                     (size_t) (context_mask | GCPlaneMask),
   15464                     &context_values);
   15465                   magick_windows[i]->attributes.background_pixel=
   15466                     pixel->background_color.pixel;
   15467                   magick_windows[i]->attributes.border_pixel=
   15468                     pixel->border_color.pixel;
   15469                   magick_windows[i]->attributes.colormap=map_info->colormap;
   15470                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
   15471                     (unsigned long) magick_windows[i]->mask,
   15472                     &magick_windows[i]->attributes);
   15473                 }
   15474                 if (windows->pan.mapped != MagickFalse )
   15475                   {
   15476                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
   15477                       windows->pan.pixmap);
   15478                     (void) XClearWindow(display,windows->pan.id);
   15479                     XDrawPanRectangle(display,windows);
   15480                   }
   15481                 if (windows->backdrop.id != (Window) NULL)
   15482                   (void) XInstallColormap(display,map_info->colormap);
   15483                 break;
   15484               }
   15485             if (*event.xclient.data.l == (long) windows->im_former_image)
   15486               {
   15487                 *state|=FormerImageState | ExitState;
   15488                 break;
   15489               }
   15490             if (*event.xclient.data.l == (long) windows->im_next_image)
   15491               {
   15492                 *state|=NextImageState | ExitState;
   15493                 break;
   15494               }
   15495             if (*event.xclient.data.l == (long) windows->im_retain_colors)
   15496               {
   15497                 *state|=RetainColorsState;
   15498                 break;
   15499               }
   15500             if (*event.xclient.data.l == (long) windows->im_exit)
   15501               {
   15502                 *state|=ExitState;
   15503                 break;
   15504               }
   15505             break;
   15506           }
   15507         if (event.xclient.message_type == windows->dnd_protocols)
   15508           {
   15509             Atom
   15510               selection,
   15511               type;
   15512 
   15513             int
   15514               format,
   15515               status;
   15516 
   15517             unsigned char
   15518               *data;
   15519 
   15520             unsigned long
   15521               after,
   15522               length;
   15523 
   15524             /*
   15525               Display image named by the Drag-and-Drop selection.
   15526             */
   15527             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
   15528               break;
   15529             selection=XInternAtom(display,"DndSelection",MagickFalse);
   15530             status=XGetWindowProperty(display,root_window,selection,0L,(long)
   15531               MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
   15532               &length,&after,&data);
   15533             if ((status != Success) || (length == 0))
   15534               break;
   15535             if (*event.xclient.data.l == 2)
   15536               {
   15537                 /*
   15538                   Offix DND.
   15539                 */
   15540                 (void) CopyMagickString(resource_info->image_info->filename,
   15541                   (char *) data,MagickPathExtent);
   15542               }
   15543             else
   15544               {
   15545                 /*
   15546                   XDND.
   15547                 */
   15548                 if (strncmp((char *) data, "file:", 5) != 0)
   15549                   {
   15550                     (void) XFree((void *) data);
   15551                     break;
   15552                   }
   15553                 (void) CopyMagickString(resource_info->image_info->filename,
   15554                   ((char *) data)+5,MagickPathExtent);
   15555               }
   15556             nexus=ReadImage(resource_info->image_info,exception);
   15557             CatchException(exception);
   15558             if (nexus != (Image *) NULL)
   15559               *state|=NextImageState | ExitState;
   15560             (void) XFree((void *) data);
   15561             break;
   15562           }
   15563         /*
   15564           If client window delete message, exit.
   15565         */
   15566         if (event.xclient.message_type != windows->wm_protocols)
   15567           break;
   15568         if (*event.xclient.data.l != (long) windows->wm_delete_window)
   15569           break;
   15570         (void) XWithdrawWindow(display,event.xclient.window,
   15571           visual_info->screen);
   15572         if (event.xclient.window == windows->image.id)
   15573           {
   15574             *state|=ExitState;
   15575             break;
   15576           }
   15577         if (event.xclient.window == windows->pan.id)
   15578           {
   15579             /*
   15580               Restore original image size when pan window is deleted.
   15581             */
   15582             windows->image.window_changes.width=windows->image.ximage->width;
   15583             windows->image.window_changes.height=windows->image.ximage->height;
   15584             (void) XConfigureImage(display,resource_info,windows,
   15585               display_image,exception);
   15586           }
   15587         break;
   15588       }
   15589       case ConfigureNotify:
   15590       {
   15591         if (display_image->debug != MagickFalse )
   15592           (void) LogMagickEvent(X11Event,GetMagickModule(),
   15593             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
   15594             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
   15595             event.xconfigure.y,event.xconfigure.send_event);
   15596         if (event.xconfigure.window == windows->image.id)
   15597           {
   15598             /*
   15599               Image window has a new configuration.
   15600             */
   15601             if (event.xconfigure.send_event != 0)
   15602               {
   15603                 XWindowChanges
   15604                   window_changes;
   15605 
   15606                 /*
   15607                   Position the transient windows relative of the Image window.
   15608                 */
   15609                 if (windows->command.geometry == (char *) NULL)
   15610                   if (windows->command.mapped == MagickFalse)
   15611                     {
   15612                       windows->command.x=event.xconfigure.x-
   15613                         windows->command.width-25;
   15614                       windows->command.y=event.xconfigure.y;
   15615                       XConstrainWindowPosition(display,&windows->command);
   15616                       window_changes.x=windows->command.x;
   15617                       window_changes.y=windows->command.y;
   15618                       (void) XReconfigureWMWindow(display,windows->command.id,
   15619                         windows->command.screen,(unsigned int) (CWX | CWY),
   15620                         &window_changes);
   15621                     }
   15622                 if (windows->widget.geometry == (char *) NULL)
   15623                   if (windows->widget.mapped == MagickFalse)
   15624                     {
   15625                       windows->widget.x=event.xconfigure.x+
   15626                         event.xconfigure.width/10;
   15627                       windows->widget.y=event.xconfigure.y+
   15628                         event.xconfigure.height/10;
   15629                       XConstrainWindowPosition(display,&windows->widget);
   15630                       window_changes.x=windows->widget.x;
   15631                       window_changes.y=windows->widget.y;
   15632                       (void) XReconfigureWMWindow(display,windows->widget.id,
   15633                         windows->widget.screen,(unsigned int) (CWX | CWY),
   15634                         &window_changes);
   15635                     }
   15636                 if (windows->magnify.geometry == (char *) NULL)
   15637                   if (windows->magnify.mapped == MagickFalse)
   15638                     {
   15639                       windows->magnify.x=event.xconfigure.x+
   15640                         event.xconfigure.width+25;
   15641                       windows->magnify.y=event.xconfigure.y;
   15642                       XConstrainWindowPosition(display,&windows->magnify);
   15643                       window_changes.x=windows->magnify.x;
   15644                       window_changes.y=windows->magnify.y;
   15645                       (void) XReconfigureWMWindow(display,windows->magnify.id,
   15646                         windows->magnify.screen,(unsigned int) (CWX | CWY),
   15647                         &window_changes);
   15648                     }
   15649                 if (windows->pan.geometry == (char *) NULL)
   15650                   if (windows->pan.mapped == MagickFalse)
   15651                     {
   15652                       windows->pan.x=event.xconfigure.x+
   15653                         event.xconfigure.width+25;
   15654                       windows->pan.y=event.xconfigure.y+
   15655                         windows->magnify.height+50;
   15656                       XConstrainWindowPosition(display,&windows->pan);
   15657                       window_changes.x=windows->pan.x;
   15658                       window_changes.y=windows->pan.y;
   15659                       (void) XReconfigureWMWindow(display,windows->pan.id,
   15660                         windows->pan.screen,(unsigned int) (CWX | CWY),
   15661                         &window_changes);
   15662                     }
   15663               }
   15664             if ((event.xconfigure.width == (int) windows->image.width) &&
   15665                 (event.xconfigure.height == (int) windows->image.height))
   15666               break;
   15667             windows->image.width=(unsigned int) event.xconfigure.width;
   15668             windows->image.height=(unsigned int) event.xconfigure.height;
   15669             windows->image.x=0;
   15670             windows->image.y=0;
   15671             if (display_image->montage != (char *) NULL)
   15672               {
   15673                 windows->image.x=vid_info.x;
   15674                 windows->image.y=vid_info.y;
   15675               }
   15676             if (windows->image.mapped != MagickFalse &&
   15677                 windows->image.stasis != MagickFalse )
   15678               {
   15679                 /*
   15680                   Update image window configuration.
   15681                 */
   15682                 windows->image.window_changes.width=event.xconfigure.width;
   15683                 windows->image.window_changes.height=event.xconfigure.height;
   15684                 (void) XConfigureImage(display,resource_info,windows,
   15685                   display_image,exception);
   15686               }
   15687             /*
   15688               Update pan window configuration.
   15689             */
   15690             if ((event.xconfigure.width < windows->image.ximage->width) ||
   15691                 (event.xconfigure.height < windows->image.ximage->height))
   15692               {
   15693                 (void) XMapRaised(display,windows->pan.id);
   15694                 XDrawPanRectangle(display,windows);
   15695               }
   15696             else
   15697               if (windows->pan.mapped != MagickFalse )
   15698                 (void) XWithdrawWindow(display,windows->pan.id,
   15699                   windows->pan.screen);
   15700             break;
   15701           }
   15702         if (event.xconfigure.window == windows->magnify.id)
   15703           {
   15704             unsigned int
   15705               magnify;
   15706 
   15707             /*
   15708               Magnify window has a new configuration.
   15709             */
   15710             windows->magnify.width=(unsigned int) event.xconfigure.width;
   15711             windows->magnify.height=(unsigned int) event.xconfigure.height;
   15712             if (windows->magnify.mapped == MagickFalse)
   15713               break;
   15714             magnify=1;
   15715             while ((int) magnify <= event.xconfigure.width)
   15716               magnify<<=1;
   15717             while ((int) magnify <= event.xconfigure.height)
   15718               magnify<<=1;
   15719             magnify>>=1;
   15720             if (((int) magnify != event.xconfigure.width) ||
   15721                 ((int) magnify != event.xconfigure.height))
   15722               {
   15723                 window_changes.width=(int) magnify;
   15724                 window_changes.height=(int) magnify;
   15725                 (void) XReconfigureWMWindow(display,windows->magnify.id,
   15726                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
   15727                   &window_changes);
   15728                 break;
   15729               }
   15730             if (windows->magnify.mapped != MagickFalse &&
   15731                 windows->magnify.stasis != MagickFalse )
   15732               {
   15733                 status=XMakeImage(display,resource_info,&windows->magnify,
   15734                   display_image,windows->magnify.width,windows->magnify.height,
   15735                   exception);
   15736                 XMakeMagnifyImage(display,windows,exception);
   15737               }
   15738             break;
   15739           }
   15740         if (windows->magnify.mapped != MagickFalse &&
   15741             (event.xconfigure.window == windows->pan.id))
   15742           {
   15743             /*
   15744               Pan icon window has a new configuration.
   15745             */
   15746             if (event.xconfigure.send_event != 0)
   15747               {
   15748                 windows->pan.x=event.xconfigure.x;
   15749                 windows->pan.y=event.xconfigure.y;
   15750               }
   15751             windows->pan.width=(unsigned int) event.xconfigure.width;
   15752             windows->pan.height=(unsigned int) event.xconfigure.height;
   15753             break;
   15754           }
   15755         if (event.xconfigure.window == windows->icon.id)
   15756           {
   15757             /*
   15758               Icon window has a new configuration.
   15759             */
   15760             windows->icon.width=(unsigned int) event.xconfigure.width;
   15761             windows->icon.height=(unsigned int) event.xconfigure.height;
   15762             break;
   15763           }
   15764         break;
   15765       }
   15766       case DestroyNotify:
   15767       {
   15768         /*
   15769           Group leader has exited.
   15770         */
   15771         if (display_image->debug != MagickFalse )
   15772           (void) LogMagickEvent(X11Event,GetMagickModule(),
   15773             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
   15774         if (event.xdestroywindow.window == windows->group_leader.id)
   15775           {
   15776             *state|=ExitState;
   15777             break;
   15778           }
   15779         break;
   15780       }
   15781       case EnterNotify:
   15782       {
   15783         /*
   15784           Selectively install colormap.
   15785         */
   15786         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
   15787           if (event.xcrossing.mode != NotifyUngrab)
   15788             XInstallColormap(display,map_info->colormap);
   15789         break;
   15790       }
   15791       case Expose:
   15792       {
   15793         if (display_image->debug != MagickFalse )
   15794           (void) LogMagickEvent(X11Event,GetMagickModule(),
   15795             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
   15796             event.xexpose.width,event.xexpose.height,event.xexpose.x,
   15797             event.xexpose.y);
   15798         /*
   15799           Refresh windows that are now exposed.
   15800         */
   15801         if ((event.xexpose.window == windows->image.id) &&
   15802             windows->image.mapped != MagickFalse )
   15803           {
   15804             XRefreshWindow(display,&windows->image,&event);
   15805             delay=display_image->delay/MagickMax(
   15806               display_image->ticks_per_second,1L);
   15807             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
   15808             break;
   15809           }
   15810         if ((event.xexpose.window == windows->magnify.id) &&
   15811             windows->magnify.mapped != MagickFalse)
   15812           {
   15813             XMakeMagnifyImage(display,windows,exception);
   15814             break;
   15815           }
   15816         if (event.xexpose.window == windows->pan.id)
   15817           {
   15818             XDrawPanRectangle(display,windows);
   15819             break;
   15820           }
   15821         if (event.xexpose.window == windows->icon.id)
   15822           {
   15823             XRefreshWindow(display,&windows->icon,&event);
   15824             break;
   15825           }
   15826         break;
   15827       }
   15828       case KeyPress:
   15829       {
   15830         int
   15831           length;
   15832 
   15833         /*
   15834           Respond to a user key press.
   15835         */
   15836         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
   15837           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   15838         *(command+length)='\0';
   15839         if (display_image->debug != MagickFalse )
   15840           (void) LogMagickEvent(X11Event,GetMagickModule(),
   15841             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
   15842             key_symbol,command);
   15843         if (event.xkey.window == windows->image.id)
   15844           {
   15845             command_type=XImageWindowCommand(display,resource_info,windows,
   15846               event.xkey.state,key_symbol,&display_image,exception);
   15847             if (command_type != NullCommand)
   15848               nexus=XMagickCommand(display,resource_info,windows,command_type,
   15849                 &display_image,exception);
   15850           }
   15851         if (event.xkey.window == windows->magnify.id)
   15852           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
   15853             exception);
   15854         if (event.xkey.window == windows->pan.id)
   15855           {
   15856             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
   15857               (void) XWithdrawWindow(display,windows->pan.id,
   15858                 windows->pan.screen);
   15859             else
   15860               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
   15861                 XTextViewWidget(display,resource_info,windows,MagickFalse,
   15862                   "Help Viewer - Image Pan",ImagePanHelp);
   15863               else
   15864                 XTranslateImage(display,windows,*image,key_symbol);
   15865           }
   15866         delay=display_image->delay/MagickMax(
   15867           display_image->ticks_per_second,1L);
   15868         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
   15869         break;
   15870       }
   15871       case KeyRelease:
   15872       {
   15873         /*
   15874           Respond to a user key release.
   15875         */
   15876         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
   15877           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
   15878         if (display_image->debug != MagickFalse )
   15879           (void) LogMagickEvent(X11Event,GetMagickModule(),
   15880             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
   15881         break;
   15882       }
   15883       case LeaveNotify:
   15884       {
   15885         /*
   15886           Selectively uninstall colormap.
   15887         */
   15888         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
   15889           if (event.xcrossing.mode != NotifyUngrab)
   15890             XUninstallColormap(display,map_info->colormap);
   15891         break;
   15892       }
   15893       case MapNotify:
   15894       {
   15895         if (display_image->debug != MagickFalse )
   15896           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
   15897             event.xmap.window);
   15898         if (event.xmap.window == windows->backdrop.id)
   15899           {
   15900             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
   15901               CurrentTime);
   15902             windows->backdrop.mapped=MagickTrue;
   15903             break;
   15904           }
   15905         if (event.xmap.window == windows->image.id)
   15906           {
   15907             if (windows->backdrop.id != (Window) NULL)
   15908               (void) XInstallColormap(display,map_info->colormap);
   15909             if (LocaleCompare(display_image->magick,"LOGO") == 0)
   15910               {
   15911                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
   15912                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
   15913               }
   15914             if (((int) windows->image.width < windows->image.ximage->width) ||
   15915                 ((int) windows->image.height < windows->image.ximage->height))
   15916               (void) XMapRaised(display,windows->pan.id);
   15917             windows->image.mapped=MagickTrue;
   15918             break;
   15919           }
   15920         if (event.xmap.window == windows->magnify.id)
   15921           {
   15922             XMakeMagnifyImage(display,windows,exception);
   15923             windows->magnify.mapped=MagickTrue;
   15924             (void) XWithdrawWindow(display,windows->info.id,
   15925               windows->info.screen);
   15926             break;
   15927           }
   15928         if (event.xmap.window == windows->pan.id)
   15929           {
   15930             XMakePanImage(display,resource_info,windows,display_image,
   15931               exception);
   15932             windows->pan.mapped=MagickTrue;
   15933             break;
   15934           }
   15935         if (event.xmap.window == windows->info.id)
   15936           {
   15937             windows->info.mapped=MagickTrue;
   15938             break;
   15939           }
   15940         if (event.xmap.window == windows->icon.id)
   15941           {
   15942             MagickBooleanType
   15943               taint;
   15944 
   15945             /*
   15946               Create an icon image.
   15947             */
   15948             taint=display_image->taint;
   15949             XMakeStandardColormap(display,icon_visual,icon_resources,
   15950               display_image,icon_map,icon_pixel,exception);
   15951             (void) XMakeImage(display,icon_resources,&windows->icon,
   15952               display_image,windows->icon.width,windows->icon.height,
   15953               exception);
   15954             display_image->taint=taint;
   15955             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
   15956               windows->icon.pixmap);
   15957             (void) XClearWindow(display,windows->icon.id);
   15958             (void) XWithdrawWindow(display,windows->info.id,
   15959               windows->info.screen);
   15960             windows->icon.mapped=MagickTrue;
   15961             break;
   15962           }
   15963         if (event.xmap.window == windows->command.id)
   15964           {
   15965             windows->command.mapped=MagickTrue;
   15966             break;
   15967           }
   15968         if (event.xmap.window == windows->popup.id)
   15969           {
   15970             windows->popup.mapped=MagickTrue;
   15971             break;
   15972           }
   15973         if (event.xmap.window == windows->widget.id)
   15974           {
   15975             windows->widget.mapped=MagickTrue;
   15976             break;
   15977           }
   15978         break;
   15979       }
   15980       case MappingNotify:
   15981       {
   15982         (void) XRefreshKeyboardMapping(&event.xmapping);
   15983         break;
   15984       }
   15985       case NoExpose:
   15986         break;
   15987       case PropertyNotify:
   15988       {
   15989         Atom
   15990           type;
   15991 
   15992         int
   15993           format,
   15994           status;
   15995 
   15996         unsigned char
   15997           *data;
   15998 
   15999         unsigned long
   16000           after,
   16001           length;
   16002 
   16003         if (display_image->debug != MagickFalse )
   16004           (void) LogMagickEvent(X11Event,GetMagickModule(),
   16005             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
   16006             event.xproperty.atom,event.xproperty.state);
   16007         if (event.xproperty.atom != windows->im_remote_command)
   16008           break;
   16009         /*
   16010           Display image named by the remote command protocol.
   16011         */
   16012         status=XGetWindowProperty(display,event.xproperty.window,
   16013           event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
   16014           AnyPropertyType,&type,&format,&length,&after,&data);
   16015         if ((status != Success) || (length == 0))
   16016           break;
   16017         if (LocaleCompare((char *) data,"-quit") == 0)
   16018           {
   16019             XClientMessage(display,windows->image.id,windows->im_protocols,
   16020               windows->im_exit,CurrentTime);
   16021             (void) XFree((void *) data);
   16022             break;
   16023           }
   16024         (void) CopyMagickString(resource_info->image_info->filename,
   16025           (char *) data,MagickPathExtent);
   16026         (void) XFree((void *) data);
   16027         nexus=ReadImage(resource_info->image_info,exception);
   16028         CatchException(exception);
   16029         if (nexus != (Image *) NULL)
   16030           *state|=NextImageState | ExitState;
   16031         break;
   16032       }
   16033       case ReparentNotify:
   16034       {
   16035         if (display_image->debug != MagickFalse )
   16036           (void) LogMagickEvent(X11Event,GetMagickModule(),
   16037             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
   16038             event.xreparent.window);
   16039         break;
   16040       }
   16041       case UnmapNotify:
   16042       {
   16043         if (display_image->debug != MagickFalse )
   16044           (void) LogMagickEvent(X11Event,GetMagickModule(),
   16045             "Unmap Notify: 0x%lx",event.xunmap.window);
   16046         if (event.xunmap.window == windows->backdrop.id)
   16047           {
   16048             windows->backdrop.mapped=MagickFalse;
   16049             break;
   16050           }
   16051         if (event.xunmap.window == windows->image.id)
   16052           {
   16053             windows->image.mapped=MagickFalse;
   16054             break;
   16055           }
   16056         if (event.xunmap.window == windows->magnify.id)
   16057           {
   16058             windows->magnify.mapped=MagickFalse;
   16059             break;
   16060           }
   16061         if (event.xunmap.window == windows->pan.id)
   16062           {
   16063             windows->pan.mapped=MagickFalse;
   16064             break;
   16065           }
   16066         if (event.xunmap.window == windows->info.id)
   16067           {
   16068             windows->info.mapped=MagickFalse;
   16069             break;
   16070           }
   16071         if (event.xunmap.window == windows->icon.id)
   16072           {
   16073             if (map_info->colormap == icon_map->colormap)
   16074               XConfigureImageColormap(display,resource_info,windows,
   16075                 display_image,exception);
   16076             (void) XFreeStandardColormap(display,icon_visual,icon_map,
   16077               icon_pixel);
   16078             windows->icon.mapped=MagickFalse;
   16079             break;
   16080           }
   16081         if (event.xunmap.window == windows->command.id)
   16082           {
   16083             windows->command.mapped=MagickFalse;
   16084             break;
   16085           }
   16086         if (event.xunmap.window == windows->popup.id)
   16087           {
   16088             if (windows->backdrop.id != (Window) NULL)
   16089               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
   16090                 CurrentTime);
   16091             windows->popup.mapped=MagickFalse;
   16092             break;
   16093           }
   16094         if (event.xunmap.window == windows->widget.id)
   16095           {
   16096             if (windows->backdrop.id != (Window) NULL)
   16097               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
   16098                 CurrentTime);
   16099             windows->widget.mapped=MagickFalse;
   16100             break;
   16101           }
   16102         break;
   16103       }
   16104       default:
   16105       {
   16106         if (display_image->debug != MagickFalse )
   16107           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
   16108             event.type);
   16109         break;
   16110       }
   16111     }
   16112   } while (!(*state & ExitState));
   16113   if ((*state & ExitState) == 0)
   16114     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
   16115       &display_image,exception);
   16116   else
   16117     if (resource_info->confirm_edit != MagickFalse )
   16118       {
   16119         /*
   16120           Query user if image has changed.
   16121         */
   16122         if ((resource_info->immutable == MagickFalse) &&
   16123             display_image->taint != MagickFalse)
   16124           {
   16125             int
   16126               status;
   16127 
   16128             status=XConfirmWidget(display,windows,"Your image changed.",
   16129               "Do you want to save it");
   16130             if (status == 0)
   16131               *state&=(~ExitState);
   16132             else
   16133               if (status > 0)
   16134                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
   16135                   &display_image,exception);
   16136           }
   16137       }
   16138   if ((windows->visual_info->klass == GrayScale) ||
   16139       (windows->visual_info->klass == PseudoColor) ||
   16140       (windows->visual_info->klass == DirectColor))
   16141     {
   16142       /*
   16143         Withdraw pan and Magnify window.
   16144       */
   16145       if (windows->info.mapped != MagickFalse )
   16146         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
   16147       if (windows->magnify.mapped != MagickFalse )
   16148         (void) XWithdrawWindow(display,windows->magnify.id,
   16149           windows->magnify.screen);
   16150       if (windows->command.mapped != MagickFalse )
   16151         (void) XWithdrawWindow(display,windows->command.id,
   16152           windows->command.screen);
   16153     }
   16154   if (windows->pan.mapped != MagickFalse )
   16155     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
   16156   if (resource_info->backdrop == MagickFalse)
   16157     if (windows->backdrop.mapped)
   16158       {
   16159         (void) XWithdrawWindow(display,windows->backdrop.id,
   16160           windows->backdrop.screen);
   16161         (void) XDestroyWindow(display,windows->backdrop.id);
   16162         windows->backdrop.id=(Window) NULL;
   16163         (void) XWithdrawWindow(display,windows->image.id,
   16164           windows->image.screen);
   16165         (void) XDestroyWindow(display,windows->image.id);
   16166         windows->image.id=(Window) NULL;
   16167       }
   16168   XSetCursorState(display,windows,MagickTrue);
   16169   XCheckRefreshWindows(display,windows);
   16170   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
   16171     *state&=(~ExitState);
   16172   if (*state & ExitState)
   16173     {
   16174       /*
   16175         Free Standard Colormap.
   16176       */
   16177       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
   16178       if (resource_info->map_type == (char *) NULL)
   16179         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
   16180       /*
   16181         Free X resources.
   16182       */
   16183       if (resource_info->copy_image != (Image *) NULL)
   16184         {
   16185           resource_info->copy_image=DestroyImage(resource_info->copy_image);
   16186           resource_info->copy_image=NewImageList();
   16187         }
   16188       DestroyXResources();
   16189     }
   16190   (void) XSync(display,MagickFalse);
   16191   /*
   16192     Restore our progress monitor and warning handlers.
   16193   */
   16194   (void) SetErrorHandler(warning_handler);
   16195   (void) SetWarningHandler(warning_handler);
   16196   /*
   16197     Change to home directory.
   16198   */
   16199   directory=getcwd(working_directory,MagickPathExtent);
   16200   (void) directory;
   16201   {
   16202     int
   16203       status;
   16204 
   16205     if (*resource_info->home_directory == '\0')
   16206       (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
   16207     status=chdir(resource_info->home_directory);
   16208     if (status == -1)
   16209       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
   16210         "UnableToOpenFile","%s",resource_info->home_directory);
   16211   }
   16212   *image=display_image;
   16213   return(nexus);
   16214 }
   16215 #else
   16216 
   16217 /*
   16219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   16220 %                                                                             %
   16221 %                                                                             %
   16222 %                                                                             %
   16223 +   D i s p l a y I m a g e s                                                 %
   16224 %                                                                             %
   16225 %                                                                             %
   16226 %                                                                             %
   16227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   16228 %
   16229 %  DisplayImages() displays an image sequence to any X window screen.  It
   16230 %  returns a value other than 0 if successful.  Check the exception member
   16231 %  of image to determine the reason for any failure.
   16232 %
   16233 %  The format of the DisplayImages method is:
   16234 %
   16235 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
   16236 %        Image *images,ExceptionInfo *exception)
   16237 %
   16238 %  A description of each parameter follows:
   16239 %
   16240 %    o image_info: the image info.
   16241 %
   16242 %    o image: the image.
   16243 %
   16244 %    o exception: return any errors or warnings in this structure.
   16245 %
   16246 */
   16247 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
   16248   Image *image,ExceptionInfo *exception)
   16249 {
   16250   assert(image_info != (const ImageInfo *) NULL);
   16251   assert(image_info->signature == MagickCoreSignature);
   16252   assert(image != (Image *) NULL);
   16253   assert(image->signature == MagickCoreSignature);
   16254   (void) image_info;
   16255   if (image->debug != MagickFalse )
   16256     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   16257   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
   16258     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
   16259   return(MagickFalse);
   16260 }
   16261 
   16262 /*
   16264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   16265 %                                                                             %
   16266 %                                                                             %
   16267 %                                                                             %
   16268 +   R e m o t e D i s p l a y C o m m a n d                                   %
   16269 %                                                                             %
   16270 %                                                                             %
   16271 %                                                                             %
   16272 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   16273 %
   16274 %  RemoteDisplayCommand() encourages a remote display program to display the
   16275 %  specified image filename.
   16276 %
   16277 %  The format of the RemoteDisplayCommand method is:
   16278 %
   16279 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
   16280 %        const char *window,const char *filename,ExceptionInfo *exception)
   16281 %
   16282 %  A description of each parameter follows:
   16283 %
   16284 %    o image_info: the image info.
   16285 %
   16286 %    o window: Specifies the name or id of an X window.
   16287 %
   16288 %    o filename: the name of the image filename to display.
   16289 %
   16290 %    o exception: return any errors or warnings in this structure.
   16291 %
   16292 */
   16293 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
   16294   const char *window,const char *filename,ExceptionInfo *exception)
   16295 {
   16296   assert(image_info != (const ImageInfo *) NULL);
   16297   assert(image_info->signature == MagickCoreSignature);
   16298   assert(filename != (char *) NULL);
   16299   (void) window;
   16300   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
   16301   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
   16302     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
   16303   return(MagickFalse);
   16304 }
   16305 #endif
   16306