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