display.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
00007 %               D   D    I    SS     P   P  L      A   A   Y Y                %
00008 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
00009 %               D   D    I       SS  P      L      A   A    Y                 %
00010 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
00011 %                                                                             %
00012 %                                                                             %
00013 %        MagickCore Methods to Interactively Display and Edit an Image        %
00014 %                                                                             %
00015 %                             Software Design                                 %
00016 %                               John Cristy                                   %
00017 %                                July 1992                                    %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 %
00037 */
00038 
00039 /*
00040   Include declarations.
00041 */
00042 #include "magick/studio.h"
00043 #include "magick/artifact.h"
00044 #include "magick/blob.h"
00045 #include "magick/cache.h"
00046 #include "magick/client.h"
00047 #include "magick/color.h"
00048 #include "magick/colorspace.h"
00049 #include "magick/composite.h"
00050 #include "magick/constitute.h"
00051 #include "magick/decorate.h"
00052 #include "magick/delegate.h"
00053 #include "magick/display.h"
00054 #include "magick/display-private.h"
00055 #include "magick/draw.h"
00056 #include "magick/effect.h"
00057 #include "magick/enhance.h"
00058 #include "magick/exception.h"
00059 #include "magick/exception-private.h"
00060 #include "magick/fx.h"
00061 #include "magick/geometry.h"
00062 #include "magick/image.h"
00063 #include "magick/image-private.h"
00064 #include "magick/list.h"
00065 #include "magick/log.h"
00066 #include "magick/magick.h"
00067 #include "magick/memory_.h"
00068 #include "magick/monitor.h"
00069 #include "magick/monitor-private.h"
00070 #include "magick/montage.h"
00071 #include "magick/option.h"
00072 #include "magick/paint.h"
00073 #include "magick/pixel.h"
00074 #include "magick/pixel-private.h"
00075 #include "magick/PreRvIcccm.h"
00076 #include "magick/property.h"
00077 #include "magick/quantum.h"
00078 #include "magick/resize.h"
00079 #include "magick/resource_.h"
00080 #include "magick/shear.h"
00081 #include "magick/segment.h"
00082 #include "magick/string_.h"
00083 #include "magick/transform.h"
00084 #include "magick/threshold.h"
00085 #include "magick/utility.h"
00086 #include "magick/version.h"
00087 #include "magick/widget.h"
00088 #include "magick/xwindow-private.h"
00089 
00090 #if defined(MAGICKCORE_X11_DELEGATE)
00091 /*
00092   Define declarations.
00093 */
00094 #define MaxColors  MagickMin(windows->visual_info->colormap_size,256L)
00095 
00096 /*
00097   Constant declarations.
00098 */
00099 static const unsigned char
00100   HighlightBitmap[8] =
00101   {
00102     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
00103   },
00104   ShadowBitmap[8] =
00105   {
00106     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
00107   };
00108 
00109 static const char
00110   *PageSizes[] =
00111   {
00112     "Letter",
00113     "Tabloid",
00114     "Ledger",
00115     "Legal",
00116     "Statement",
00117     "Executive",
00118     "A3",
00119     "A4",
00120     "A5",
00121     "B4",
00122     "B5",
00123     "Folio",
00124     "Quarto",
00125     "10x14",
00126     (char *) NULL
00127   };
00128 
00129 /*
00130   Help widget declarations.
00131 */
00132 static const char
00133   *ImageAnnotateHelp[] =
00134   {
00135     "In annotate mode, the Command widget has these options:",
00136     "",
00137     "    Font Name",
00138     "      fixed",
00139     "      variable",
00140     "      5x8",
00141     "      6x10",
00142     "      7x13bold",
00143     "      8x13bold",
00144     "      9x15bold",
00145     "      10x20",
00146     "      12x24",
00147     "      Browser...",
00148     "    Font Color",
00149     "      black",
00150     "      blue",
00151     "      cyan",
00152     "      green",
00153     "      gray",
00154     "      red",
00155     "      magenta",
00156     "      yellow",
00157     "      white",
00158     "      transparent",
00159     "      Browser...",
00160     "    Font Color",
00161     "      black",
00162     "      blue",
00163     "      cyan",
00164     "      green",
00165     "      gray",
00166     "      red",
00167     "      magenta",
00168     "      yellow",
00169     "      white",
00170     "      transparent",
00171     "      Browser...",
00172     "    Rotate Text",
00173     "      -90",
00174     "      -45",
00175     "      -30",
00176     "      0",
00177     "      30",
00178     "      45",
00179     "      90",
00180     "      180",
00181     "      Dialog...",
00182     "    Help",
00183     "    Dismiss",
00184     "",
00185     "Choose a font name from the Font Name sub-menu.  Additional",
00186     "font names can be specified with the font browser.  You can",
00187     "change the menu names by setting the X resources font1",
00188     "through font9.",
00189     "",
00190     "Choose a font color from the Font Color sub-menu.",
00191     "Additional font colors can be specified with the color",
00192     "browser.  You can change the menu colors by setting the X",
00193     "resources pen1 through pen9.",
00194     "",
00195     "If you select the color browser and press Grab, you can",
00196     "choose the font color by moving the pointer to the desired",
00197     "color on the screen and press any button.",
00198     "",
00199     "If you choose to rotate the text, choose Rotate Text from the",
00200     "menu and select an angle.  Typically you will only want to",
00201     "rotate one line of text at a time.  Depending on the angle you",
00202     "choose, subsequent lines may end up overwriting each other.",
00203     "",
00204     "Choosing a font and its color is optional.  The default font",
00205     "is fixed and the default color is black.  However, you must",
00206     "choose a location to begin entering text and press button 1.",
00207     "An underscore character will appear at the location of the",
00208     "pointer.  The cursor changes to a pencil to indicate you are",
00209     "in text mode.  To exit immediately, press Dismiss.",
00210     "",
00211     "In text mode, any key presses will display the character at",
00212     "the location of the underscore and advance the underscore",
00213     "cursor.  Enter your text and once completed press Apply to",
00214     "finish your image annotation.  To correct errors press BACK",
00215     "SPACE.  To delete an entire line of text, press DELETE.  Any",
00216     "text that exceeds the boundaries of the image window is",
00217     "automatically continued onto the next line.",
00218     "",
00219     "The actual color you request for the font is saved in the",
00220     "image.  However, the color that appears in your image window",
00221     "may be different.  For example, on a monochrome screen the",
00222     "text will appear black or white even if you choose the color",
00223     "red as the font color.  However, the image saved to a file",
00224     "with -write is written with red lettering.  To assure the",
00225     "correct color text in the final image, any PseudoClass image",
00226     "is promoted to DirectClass (see miff(5)).  To force a",
00227     "PseudoClass image to remain PseudoClass, use -colors.",
00228     (char *) NULL,
00229   },
00230   *ImageChopHelp[] =
00231   {
00232     "In chop mode, the Command widget has these options:",
00233     "",
00234     "    Direction",
00235     "      horizontal",
00236     "      vertical",
00237     "    Help",
00238     "    Dismiss",
00239     "",
00240     "If the you choose the horizontal direction (this the",
00241     "default), the area of the image between the two horizontal",
00242     "endpoints of the chop line is removed.  Otherwise, the area",
00243     "of the image between the two vertical endpoints of the chop",
00244     "line is removed.",
00245     "",
00246     "Select a location within the image window to begin your chop,",
00247     "press and hold any button.  Next, move the pointer to",
00248     "another location in the image.  As you move a line will",
00249     "connect the initial location and the pointer.  When you",
00250     "release the button, the area within the image to chop is",
00251     "determined by which direction you choose from the Command",
00252     "widget.",
00253     "",
00254     "To cancel the image chopping, move the pointer back to the",
00255     "starting point of the line and release the button.",
00256     (char *) NULL,
00257   },
00258   *ImageColorEditHelp[] =
00259   {
00260     "In color edit mode, the Command widget has these options:",
00261     "",
00262     "    Method",
00263     "      point",
00264     "      replace",
00265     "      floodfill",
00266     "      filltoborder",
00267     "      reset",
00268     "    Pixel Color",
00269     "      black",
00270     "      blue",
00271     "      cyan",
00272     "      green",
00273     "      gray",
00274     "      red",
00275     "      magenta",
00276     "      yellow",
00277     "      white",
00278     "      Browser...",
00279     "    Border Color",
00280     "      black",
00281     "      blue",
00282     "      cyan",
00283     "      green",
00284     "      gray",
00285     "      red",
00286     "      magenta",
00287     "      yellow",
00288     "      white",
00289     "      Browser...",
00290     "    Fuzz",
00291     "      0%",
00292     "      2%",
00293     "      5%",
00294     "      10%",
00295     "      15%",
00296     "      Dialog...",
00297     "    Undo",
00298     "    Help",
00299     "    Dismiss",
00300     "",
00301     "Choose a color editing method from the Method sub-menu",
00302     "of the Command widget.  The point method recolors any pixel",
00303     "selected with the pointer until the button is released.  The",
00304     "replace method recolors any pixel that matches the color of",
00305     "the pixel you select with a button press.  Floodfill recolors",
00306     "any pixel that matches the color of the pixel you select with",
00307     "a button press and is a neighbor.  Whereas filltoborder recolors",
00308     "any neighbor pixel that is not the border color.  Finally reset",
00309     "changes the entire image to the designated color.",
00310     "",
00311     "Next, choose a pixel color from the Pixel Color sub-menu.",
00312     "Additional pixel colors can be specified with the color",
00313     "browser.  You can change the menu colors by setting the X",
00314     "resources pen1 through pen9.",
00315     "",
00316     "Now press button 1 to select a pixel within the image window",
00317     "to change its color.  Additional pixels may be recolored as",
00318     "prescribed by the method you choose.",
00319     "",
00320     "If the Magnify widget is mapped, it can be helpful in positioning",
00321     "your pointer within the image (refer to button 2).",
00322     "",
00323     "The actual color you request for the pixels is saved in the",
00324     "image.  However, the color that appears in your image window",
00325     "may be different.  For example, on a monochrome screen the",
00326     "pixel will appear black or white even if you choose the",
00327     "color red as the pixel color.  However, the image saved to a",
00328     "file with -write is written with red pixels.  To assure the",
00329     "correct color text in the final image, any PseudoClass image",
00330     "is promoted to DirectClass (see miff(5)).  To force a",
00331     "PseudoClass image to remain PseudoClass, use -colors.",
00332     (char *) NULL,
00333   },
00334   *ImageCompositeHelp[] =
00335   {
00336     "First a widget window is displayed requesting you to enter an",
00337     "image name. Press Composite, Grab or type a file name.",
00338     "Press Cancel if you choose not to create a composite image.",
00339     "When you choose Grab, move the pointer to the desired window",
00340     "and press any button.",
00341     "",
00342     "If the Composite image does not have any matte information,",
00343     "you are informed and the file browser is displayed again.",
00344     "Enter the name of a mask image.  The image is typically",
00345     "grayscale and the same size as the composite image.  If the",
00346     "image is not grayscale, it is converted to grayscale and the",
00347     "resulting intensities are used as matte information.",
00348     "",
00349     "A small window appears showing the location of the cursor in",
00350     "the image window. You are now in composite mode.  To exit",
00351     "immediately, press Dismiss.  In composite mode, the Command",
00352     "widget has these options:",
00353     "",
00354     "    Operators",
00355     "      Over",
00356     "      In",
00357     "      Out",
00358     "      Atop",
00359     "      Xor",
00360     "      Plus",
00361     "      Minus",
00362     "      Add",
00363     "      Subtract",
00364     "      Difference",
00365     "      Multiply",
00366     "      Bumpmap",
00367     "      Copy",
00368     "      CopyRed",
00369     "      CopyGreen",
00370     "      CopyBlue",
00371     "      CopyOpacity",
00372     "      Clear",
00373     "    Dissolve",
00374     "    Displace",
00375     "    Help",
00376     "    Dismiss",
00377     "",
00378     "Choose a composite operation from the Operators sub-menu of",
00379     "the Command widget.  How each operator behaves is described",
00380     "below.  Image window is the image currently displayed on",
00381     "your X server and image is the image obtained with the File",
00382     "Browser widget.",
00383     "",
00384     "Over     The result is the union of the two image shapes,",
00385     "         with image obscuring image window in the region of",
00386     "         overlap.",
00387     "",
00388     "In       The result is simply image cut by the shape of",
00389     "         image window.  None of the image data of image",
00390     "         window is in the result.",
00391     "",
00392     "Out      The resulting image is image with the shape of",
00393     "         image window cut out.",
00394     "",
00395     "Atop     The result is the same shape as image image window,",
00396     "         with image obscuring image window where the image",
00397     "         shapes overlap.  Note this differs from over",
00398     "         because the portion of image outside image window's",
00399     "         shape does not appear in the result.",
00400     "",
00401     "Xor      The result is the image data from both image and",
00402     "         image window that is outside the overlap region.",
00403     "         The overlap region is blank.",
00404     "",
00405     "Plus     The result is just the sum of the image data.",
00406     "         Output values are cropped to QuantumRange (no overflow).",
00407     "",
00408     "Minus    The result of image - image window, with underflow",
00409     "         cropped to zero.",
00410     "",
00411     "Add      The result of image + image window, with overflow",
00412     "         wrapping around (mod 256).",
00413     "",
00414     "Subtract The result of image - image window, with underflow",
00415     "         wrapping around (mod 256).  The add and subtract",
00416     "         operators can be used to perform reversible",
00417     "         transformations.",
00418     "",
00419     "Difference",
00420     "         The result of abs(image - image window).  This",
00421     "         useful for comparing two very similar images.",
00422     "",
00423     "Multiply",
00424     "         The result of image * image window.  This",
00425     "         useful for the creation of drop-shadows.",
00426     "",
00427     "Bumpmap  The result of surface normals from image * image",
00428     "         window.",
00429     "",
00430     "Copy     The resulting image is image window replaced with",
00431     "         image.  Here the matte information is ignored.",
00432     "",
00433     "CopyRed  The red layer of the image window is replace with",
00434     "         the red layer of the image.  The other layers are",
00435     "         untouched.",
00436     "",
00437     "CopyGreen",
00438     "         The green layer of the image window is replace with",
00439     "         the green layer of the image.  The other layers are",
00440     "         untouched.",
00441     "",
00442     "CopyBlue The blue layer of the image window is replace with",
00443     "         the blue layer of the image.  The other layers are",
00444     "         untouched.",
00445     "",
00446     "CopyOpacity",
00447     "         The matte layer of the image window is replace with",
00448     "         the matte layer of the image.  The other layers are",
00449     "         untouched.",
00450     "",
00451     "The image compositor requires a matte, or alpha channel in",
00452     "the image for some operations.  This extra channel usually",
00453     "defines a mask which represents a sort of a cookie-cutter",
00454     "for the image.  This the case when matte is opaque (full",
00455     "coverage) for pixels inside the shape, zero outside, and",
00456     "between 0 and QuantumRange on the boundary.  If image does not",
00457     "have a matte channel, it is initialized with 0 for any pixel",
00458     "matching in color to pixel location (0,0), otherwise QuantumRange.",
00459     "",
00460     "If you choose Dissolve, the composite operator becomes Over.  The",
00461     "image matte channel percent transparency is initialized to factor.",
00462     "The image window is initialized to (100-factor). Where factor is the",
00463     "value you specify in the Dialog widget.",
00464     "",
00465     "Displace shifts the image pixels as defined by a displacement",
00466     "map.  With this option, image is used as a displacement map.",
00467     "Black, within the displacement map, is a maximum positive",
00468     "displacement.  White is a maximum negative displacement and",
00469     "middle gray is neutral.  The displacement is scaled to determine",
00470     "the pixel shift.  By default, the displacement applies in both the",
00471     "horizontal and vertical directions.  However, if you specify a mask,",
00472     "image is the horizontal X displacement and mask the vertical Y",
00473     "displacement.",
00474     "",
00475     "Note that matte information for image window is not retained",
00476     "for colormapped X server visuals (e.g. StaticColor,",
00477     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
00478     "behavior may require a TrueColor or DirectColor visual or a",
00479     "Standard Colormap.",
00480     "",
00481     "Choosing a composite operator is optional.  The default",
00482     "operator is replace.  However, you must choose a location to",
00483     "composite your image and press button 1.  Press and hold the",
00484     "button before releasing and an outline of the image will",
00485     "appear to help you identify your location.",
00486     "",
00487     "The actual colors of the composite image is saved.  However,",
00488     "the color that appears in image window may be different.",
00489     "For example, on a monochrome screen image window will appear",
00490     "black or white even though your composited image may have",
00491     "many colors.  If the image is saved to a file it is written",
00492     "with the correct colors.  To assure the correct colors are",
00493     "saved in the final image, any PseudoClass image is promoted",
00494     "to DirectClass (see miff(5)).  To force a PseudoClass image",
00495     "to remain PseudoClass, use -colors.",
00496     (char *) NULL,
00497   },
00498   *ImageCutHelp[] =
00499   {
00500     "In cut mode, the Command widget has these options:",
00501     "",
00502     "    Help",
00503     "    Dismiss",
00504     "",
00505     "To define a cut region, press button 1 and drag.  The",
00506     "cut region is defined by a highlighted rectangle that",
00507     "expands or contracts as it follows the pointer.  Once you",
00508     "are satisfied with the cut region, release the button.",
00509     "You are now in rectify mode.  In rectify mode, the Command",
00510     "widget has these options:",
00511     "",
00512     "    Cut",
00513     "    Help",
00514     "    Dismiss",
00515     "",
00516     "You can make adjustments by moving the pointer to one of the",
00517     "cut rectangle corners, pressing a button, and dragging.",
00518     "Finally, press Cut to commit your copy region.  To",
00519     "exit without cutting the image, press Dismiss.",
00520     (char *) NULL,
00521   },
00522   *ImageCopyHelp[] =
00523   {
00524     "In copy mode, the Command widget has these options:",
00525     "",
00526     "    Help",
00527     "    Dismiss",
00528     "",
00529     "To define a copy region, press button 1 and drag.  The",
00530     "copy region is defined by a highlighted rectangle that",
00531     "expands or contracts as it follows the pointer.  Once you",
00532     "are satisfied with the copy region, release the button.",
00533     "You are now in rectify mode.  In rectify mode, the Command",
00534     "widget has these options:",
00535     "",
00536     "    Copy",
00537     "    Help",
00538     "    Dismiss",
00539     "",
00540     "You can make adjustments by moving the pointer to one of the",
00541     "copy rectangle corners, pressing a button, and dragging.",
00542     "Finally, press Copy to commit your copy region.  To",
00543     "exit without copying the image, press Dismiss.",
00544     (char *) NULL,
00545   },
00546   *ImageCropHelp[] =
00547   {
00548     "In crop mode, the Command widget has these options:",
00549     "",
00550     "    Help",
00551     "    Dismiss",
00552     "",
00553     "To define a cropping region, press button 1 and drag.  The",
00554     "cropping region is defined by a highlighted rectangle that",
00555     "expands or contracts as it follows the pointer.  Once you",
00556     "are satisfied with the cropping region, release the button.",
00557     "You are now in rectify mode.  In rectify mode, the Command",
00558     "widget has these options:",
00559     "",
00560     "    Crop",
00561     "    Help",
00562     "    Dismiss",
00563     "",
00564     "You can make adjustments by moving the pointer to one of the",
00565     "cropping rectangle corners, pressing a button, and dragging.",
00566     "Finally, press Crop to commit your cropping region.  To",
00567     "exit without cropping the image, press Dismiss.",
00568     (char *) NULL,
00569   },
00570   *ImageDrawHelp[] =
00571   {
00572     "The cursor changes to a crosshair to indicate you are in",
00573     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
00574     "the Command widget has these options:",
00575     "",
00576     "    Element",
00577     "      point",
00578     "      line",
00579     "      rectangle",
00580     "      fill rectangle",
00581     "      circle",
00582     "      fill circle",
00583     "      ellipse",
00584     "      fill ellipse",
00585     "      polygon",
00586     "      fill polygon",
00587     "    Color",
00588     "      black",
00589     "      blue",
00590     "      cyan",
00591     "      green",
00592     "      gray",
00593     "      red",
00594     "      magenta",
00595     "      yellow",
00596     "      white",
00597     "      transparent",
00598     "      Browser...",
00599     "    Stipple",
00600     "      Brick",
00601     "      Diagonal",
00602     "      Scales",
00603     "      Vertical",
00604     "      Wavy",
00605     "      Translucent",
00606     "      Opaque",
00607     "      Open...",
00608     "    Width",
00609     "      1",
00610     "      2",
00611     "      4",
00612     "      8",
00613     "      16",
00614     "      Dialog...",
00615     "    Undo",
00616     "    Help",
00617     "    Dismiss",
00618     "",
00619     "Choose a drawing primitive from the Element sub-menu.",
00620     "",
00621     "Choose a color from the Color sub-menu.  Additional",
00622     "colors can be specified with the color browser.",
00623     "",
00624     "If you choose the color browser and press Grab, you can",
00625     "select the color by moving the pointer to the desired",
00626     "color on the screen and press any button.  The transparent",
00627     "color updates the image matte channel and is useful for",
00628     "image compositing.",
00629     "",
00630     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
00631     "Additional stipples can be specified with the file browser.",
00632     "Stipples obtained from the file browser must be on disk in the",
00633     "X11 bitmap format.",
00634     "",
00635     "Choose a width, if appropriate, from the Width sub-menu.  To",
00636     "choose a specific width select the Dialog widget.",
00637     "",
00638     "Choose a point in the Image window and press button 1 and",
00639     "hold.  Next, move the pointer to another location in the",
00640     "image.  As you move, a line connects the initial location and",
00641     "the pointer.  When you release the button, the image is",
00642     "updated with the primitive you just drew.  For polygons, the",
00643     "image is updated when you press and release the button without",
00644     "moving the pointer.",
00645     "",
00646     "To cancel image drawing, move the pointer back to the",
00647     "starting point of the line and release the button.",
00648     (char *) NULL,
00649   },
00650   *DisplayHelp[] =
00651   {
00652     "BUTTONS",
00653     "  The effects of each button press is described below.  Three",
00654     "  buttons are required.  If you have a two button mouse,",
00655     "  button 1 and 3 are returned.  Press ALT and button 3 to",
00656     "  simulate button 2.",
00657     "",
00658     "  1    Press this button to map or unmap the Command widget.",
00659     "",
00660     "  2    Press and drag to define a region of the image to",
00661     "       magnify.",
00662     "",
00663     "  3    Press and drag to choose from a select set of commands.",
00664     "       This button behaves differently if the image being",
00665     "       displayed is a visual image directory.  Here, choose a",
00666     "       particular tile of the directory and press this button and",
00667     "       drag to select a command from a pop-up menu.  Choose from",
00668     "       these menu items:",
00669     "",
00670     "           Open",
00671     "           Next",
00672     "           Former",
00673     "           Delete",
00674     "           Update",
00675     "",
00676     "       If you choose Open, the image represented by the tile is",
00677     "       displayed.  To return to the visual image directory, choose",
00678     "       Next from the Command widget.  Next and Former moves to the",
00679     "       next or former image respectively.  Choose Delete to delete",
00680     "       a particular image tile.  Finally, choose Update to",
00681     "       synchronize all the image tiles with their respective",
00682     "       images.",
00683     "",
00684     "COMMAND WIDGET",
00685     "  The Command widget lists a number of sub-menus and commands.",
00686     "  They are",
00687     "",
00688     "      File",
00689     "        Open...",
00690     "        Next",
00691     "        Former",
00692     "        Select...",
00693     "        Save...",
00694     "        Print...",
00695     "        Delete...",
00696     "        New...",
00697     "        Visual Directory...",
00698     "        Quit",
00699     "      Edit",
00700     "        Undo",
00701     "        Redo",
00702     "        Cut",
00703     "        Copy",
00704     "        Paste",
00705     "      View",
00706     "        Half Size",
00707     "        Original Size",
00708     "        Double Size",
00709     "        Resize...",
00710     "        Apply",
00711     "        Refresh",
00712     "        Restore",
00713     "      Transform",
00714     "        Crop",
00715     "        Chop",
00716     "        Flop",
00717     "        Flip",
00718     "        Rotate Right",
00719     "        Rotate Left",
00720     "        Rotate...",
00721     "        Shear...",
00722     "        Roll...",
00723     "        Trim Edges",
00724     "      Enhance",
00725     "        Brightness...",
00726     "        Saturation...",
00727     "        Hue...",
00728     "        Gamma...",
00729     "        Sharpen...",
00730     "        Dull",
00731     "        Contrast Stretch...",
00732     "        Sigmoidal Contrast...",
00733     "        Normalize",
00734     "        Equalize",
00735     "        Negate",
00736     "        Grayscale",
00737     "        Map...",
00738     "        Quantize...",
00739     "      Effects",
00740     "        Despeckle",
00741     "        Emboss",
00742     "        Reduce Noise",
00743     "        Add Noise",
00744     "        Sharpen...",
00745     "        Blur...",
00746     "        Threshold...",
00747     "        Edge Detect...",
00748     "        Spread...",
00749     "        Shade...",
00750     "        Painting...",
00751     "        Segment...",
00752     "      F/X",
00753     "        Solarize...",
00754     "        Sepia Tone...",
00755     "        Swirl...",
00756     "        Implode...",
00757     "        Vignette...",
00758     "        Wave...",
00759     "        Oil Painting...",
00760     "        Charcoal Drawing...",
00761     "      Image Edit",
00762     "        Annotate...",
00763     "        Draw...",
00764     "        Color...",
00765     "        Matte...",
00766     "        Composite...",
00767     "        Add Border...",
00768     "        Add Frame...",
00769     "        Comment...",
00770     "        Launch...",
00771     "        Region of Interest...",
00772     "      Miscellany",
00773     "        Image Info",
00774     "        Zoom Image",
00775     "        Show Preview...",
00776     "        Show Histogram",
00777     "        Show Matte",
00778     "        Background...",
00779     "        Slide Show",
00780     "        Preferences...",
00781     "      Help",
00782     "        Overview",
00783     "        Browse Documentation",
00784     "        About Display",
00785     "",
00786     "  Menu items with a indented triangle have a sub-menu.  They",
00787     "  are represented above as the indented items.  To access a",
00788     "  sub-menu item, move the pointer to the appropriate menu and",
00789     "  press a button and drag.  When you find the desired sub-menu",
00790     "  item, release the button and the command is executed.  Move",
00791     "  the pointer away from the sub-menu if you decide not to",
00792     "  execute a particular command.",
00793     "",
00794     "KEYBOARD ACCELERATORS",
00795     "  Accelerators are one or two key presses that effect a",
00796     "  particular command.  The keyboard accelerators that",
00797     "  display(1) understands is:",
00798     "",
00799     "  Ctl+O     Press to open an image from a file.",
00800     "",
00801     "  space     Press to display the next image.",
00802     "",
00803     "            If the image is a multi-paged document such as a Postscript",
00804     "            document, you can skip ahead several pages by preceeding",
00805     "            this command with a number.  For example to display the",
00806     "            third page beyond the current page, press 3<space>.",
00807     "",
00808     "  backspace Press to display the former image.",
00809     "",
00810     "            If the image is a multi-paged document such as a Postscript",
00811     "            document, you can skip behind several pages by preceeding",
00812     "            this command with a number.  For example to display the",
00813     "            third page preceeding the current page, press 3<backspace>.",
00814     "",
00815     "  Ctl+S     Press to write the image to a file.",
00816     "",
00817     "  Ctl+P     Press to print the image to a Postscript printer.",
00818     "",
00819     "  Ctl+D     Press to delete an image file.",
00820     "",
00821     "  Ctl+N     Press to create a blank canvas.",
00822     "",
00823     "  Ctl+Q     Press to discard all images and exit program.",
00824     "",
00825     "  Ctl+Z     Press to undo last image transformation.",
00826     "",
00827     "  Ctl+R     Press to redo last image transformation.",
00828     "",
00829     "  Ctl+X     Press to cut a region of the image.",
00830     "",
00831     "  Ctl+C     Press to copy a region of the image.",
00832     "",
00833     "  Ctl+V     Press to paste a region to the image.",
00834     "",
00835     "  <         Press to half the image size.",
00836     "",
00837     "  -         Press to return to the original image size.",
00838     "",
00839     "  >         Press to double the image size.",
00840     "",
00841     "  %         Press to resize the image to a width and height you",
00842     "            specify.",
00843     "",
00844     "Cmd-A       Press to make any image transformations permanent."
00845     "",
00846     "            By default, any image size transformations are applied",
00847     "            to the original image to create the image displayed on",
00848     "            the X server.  However, the transformations are not",
00849     "            permanent (i.e. the original image does not change",
00850     "            size only the X image does).  For example, if you",
00851     "            press > the X image will appear to double in size,",
00852     "            but the original image will in fact remain the same size.",
00853     "            To force the original image to double in size, press >",
00854     "            followed by Cmd-A.",
00855     "",
00856     "  @         Press to refresh the image window.",
00857     "",
00858     "  C         Press to cut out a rectangular region of the image.",
00859     "",
00860     "  [         Press to chop the image.",
00861     "",
00862     "  H         Press to flop image in the horizontal direction.",
00863     "",
00864     "  V         Press to flip image in the vertical direction.",
00865     "",
00866     "  /         Press to rotate the image 90 degrees clockwise.",
00867     "",
00868     " \\         Press to rotate the image 90 degrees counter-clockwise.",
00869     "",
00870     "  *         Press to rotate the image the number of degrees you",
00871     "            specify.",
00872     "",
00873     "  S         Press to shear the image the number of degrees you",
00874     "            specify.",
00875     "",
00876     "  R         Press to roll the image.",
00877     "",
00878     "  T         Press to trim the image edges.",
00879     "",
00880     "  Shft-H    Press to vary the image hue.",
00881     "",
00882     "  Shft-S    Press to vary the color saturation.",
00883     "",
00884     "  Shft-L    Press to vary the color brightness.",
00885     "",
00886     "  Shft-G    Press to gamma correct the image.",
00887     "",
00888     "  Shft-C    Press to sharpen the image contrast.",
00889     "",
00890     "  Shft-Z    Press to dull the image contrast.",
00891     "",
00892     "  =         Press to perform histogram equalization on the image.",
00893     "",
00894     "  Shft-N    Press to perform histogram normalization on the image.",
00895     "",
00896     "  Shft-~    Press to negate the colors of the image.",
00897     "",
00898     "  .         Press to convert the image colors to gray.",
00899     "",
00900     "  Shft-#    Press to set the maximum number of unique colors in the",
00901     "            image.",
00902     "",
00903     "  F2        Press to reduce the speckles in an image.",
00904     "",
00905     "  F3        Press to eliminate peak noise from an image.",
00906     "",
00907     "  F4        Press to add noise to an image.",
00908     "",
00909     "  F5        Press to sharpen an image.",
00910     "",
00911     "  F6        Press to delete an image file.",
00912     "",
00913     "  F7        Press to threshold the image.",
00914     "",
00915     "  F8        Press to detect edges within an image.",
00916     "",
00917     "  F9        Press to emboss an image.",
00918     "",
00919     "  F10       Press to displace pixels by a random amount.",
00920     "",
00921     "  F11       Press to negate all pixels above the threshold level.",
00922     "",
00923     "  F12       Press to shade the image using a distant light source.",
00924     "",
00925     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
00926     "",
00927     "  F14       Press to segment the image by color.",
00928     "",
00929     "  Meta-S    Press to swirl image pixels about the center.",
00930     "",
00931     "  Meta-I    Press to implode image pixels about the center.",
00932     "",
00933     "  Meta-W    Press to alter an image along a sine wave.",
00934     "",
00935     "  Meta-P    Press to simulate an oil painting.",
00936     "",
00937     "  Meta-C    Press to simulate a charcoal drawing.",
00938     "",
00939     "  Alt-A     Press to annotate the image with text.",
00940     "",
00941     "  Alt-D     Press to draw on an image.",
00942     "",
00943     "  Alt-P     Press to edit an image pixel color.",
00944     "",
00945     "  Alt-M     Press to edit the image matte information.",
00946     "",
00947     "  Alt-V     Press to composite the image with another.",
00948     "",
00949     "  Alt-B     Press to add a border to the image.",
00950     "",
00951     "  Alt-F     Press to add an ornamental border to the image.",
00952     "",
00953     "  Alt-Shft-!",
00954     "            Press to add an image comment.",
00955     "",
00956     "  Ctl-A     Press to apply image processing techniques to a region",
00957     "            of interest.",
00958     "",
00959     "  Shft-?    Press to display information about the image.",
00960     "",
00961     "  Shft-+    Press to map the zoom image window.",
00962     "",
00963     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
00964     "",
00965     "  F1        Press to display helpful information about display(1).",
00966     "",
00967     "  Find      Press to browse documentation about ImageMagick.",
00968     "",
00969     "  1-9       Press to change the level of magnification.",
00970     "",
00971     "  Use the arrow keys to move the image one pixel up, down,",
00972     "  left, or right within the magnify window.  Be sure to first",
00973     "  map the magnify window by pressing button 2.",
00974     "",
00975     "  Press ALT and one of the arrow keys to trim off one pixel",
00976     "  from any side of the image.",
00977     (char *) NULL,
00978   },
00979   *ImageMatteEditHelp[] =
00980   {
00981     "Matte information within an image is useful for some",
00982     "operations such as image compositing (See IMAGE",
00983     "COMPOSITING).  This extra channel usually defines a mask",
00984     "which represents a sort of a cookie-cutter for the image.",
00985     "This the case when matte is opaque (full coverage) for",
00986     "pixels inside the shape, zero outside, and between 0 and",
00987     "QuantumRange on the boundary.",
00988     "",
00989     "A small window appears showing the location of the cursor in",
00990     "the image window. You are now in matte edit mode.  To exit",
00991     "immediately, press Dismiss.  In matte edit mode, the Command",
00992     "widget has these options:",
00993     "",
00994     "    Method",
00995     "      point",
00996     "      replace",
00997     "      floodfill",
00998     "      filltoborder",
00999     "      reset",
01000     "    Border Color",
01001     "      black",
01002     "      blue",
01003     "      cyan",
01004     "      green",
01005     "      gray",
01006     "      red",
01007     "      magenta",
01008     "      yellow",
01009     "      white",
01010     "      Browser...",
01011     "    Fuzz",
01012     "      0%",
01013     "      2%",
01014     "      5%",
01015     "      10%",
01016     "      15%",
01017     "      Dialog...",
01018     "    Matte",
01019     "      Opaque",
01020     "      Transparent",
01021     "      Dialog...",
01022     "    Undo",
01023     "    Help",
01024     "    Dismiss",
01025     "",
01026     "Choose a matte editing method from the Method sub-menu of",
01027     "the Command widget.  The point method changes the matte value",
01028     "of any pixel selected with the pointer until the button is",
01029     "is released.  The replace method changes the matte value of",
01030     "any pixel that matches the color of the pixel you select with",
01031     "a button press.  Floodfill changes the matte value of any pixel",
01032     "that matches the color of the pixel you select with a button",
01033     "press and is a neighbor.  Whereas filltoborder changes the matte",
01034     "value any neighbor pixel that is not the border color.  Finally",
01035     "reset changes the entire image to the designated matte value.",
01036     "",
01037     "Choose Matte Value and pick Opaque or Transarent.  For other values",
01038     "select the Dialog entry.  Here a dialog appears requesting a matte",
01039     "value.  The value you select is assigned as the opacity value of the",
01040     "selected pixel or pixels.",
01041     "",
01042     "Now, press any button to select a pixel within the image",
01043     "window to change its matte value.",
01044     "",
01045     "If the Magnify widget is mapped, it can be helpful in positioning",
01046     "your pointer within the image (refer to button 2).",
01047     "",
01048     "Matte information is only valid in a DirectClass image.",
01049     "Therefore, any PseudoClass image is promoted to DirectClass",
01050     "(see miff(5)).  Note that matte information for PseudoClass",
01051     "is not retained for colormapped X server visuals (e.g.",
01052     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
01053     "immediately save your image to a file (refer to Write).",
01054     "Correct matte editing behavior may require a TrueColor or",
01055     "DirectColor visual or a Standard Colormap.",
01056     (char *) NULL,
01057   },
01058   *ImagePanHelp[] =
01059   {
01060     "When an image exceeds the width or height of the X server",
01061     "screen, display maps a small panning icon.  The rectangle",
01062     "within the panning icon shows the area that is currently",
01063     "displayed in the image window.  To pan about the image,",
01064     "press any button and drag the pointer within the panning",
01065     "icon.  The pan rectangle moves with the pointer and the",
01066     "image window is updated to reflect the location of the",
01067     "rectangle within the panning icon.  When you have selected",
01068     "the area of the image you wish to view, release the button.",
01069     "",
01070     "Use the arrow keys to pan the image one pixel up, down,",
01071     "left, or right within the image window.",
01072     "",
01073     "The panning icon is withdrawn if the image becomes smaller",
01074     "than the dimensions of the X server screen.",
01075     (char *) NULL,
01076   },
01077   *ImagePasteHelp[] =
01078   {
01079     "A small window appears showing the location of the cursor in",
01080     "the image window. You are now in paste mode.  To exit",
01081     "immediately, press Dismiss.  In paste mode, the Command",
01082     "widget has these options:",
01083     "",
01084     "    Operators",
01085     "      over",
01086     "      in",
01087     "      out",
01088     "      atop",
01089     "      xor",
01090     "      plus",
01091     "      minus",
01092     "      add",
01093     "      subtract",
01094     "      difference",
01095     "      replace",
01096     "    Help",
01097     "    Dismiss",
01098     "",
01099     "Choose a composite operation from the Operators sub-menu of",
01100     "the Command widget.  How each operator behaves is described",
01101     "below.  Image window is the image currently displayed on",
01102     "your X server and image is the image obtained with the File",
01103     "Browser widget.",
01104     "",
01105     "Over     The result is the union of the two image shapes,",
01106     "         with image obscuring image window in the region of",
01107     "         overlap.",
01108     "",
01109     "In       The result is simply image cut by the shape of",
01110     "         image window.  None of the image data of image",
01111     "         window is in the result.",
01112     "",
01113     "Out      The resulting image is image with the shape of",
01114     "         image window cut out.",
01115     "",
01116     "Atop     The result is the same shape as image image window,",
01117     "         with image obscuring image window where the image",
01118     "         shapes overlap.  Note this differs from over",
01119     "         because the portion of image outside image window's",
01120     "         shape does not appear in the result.",
01121     "",
01122     "Xor      The result is the image data from both image and",
01123     "         image window that is outside the overlap region.",
01124     "         The overlap region is blank.",
01125     "",
01126     "Plus     The result is just the sum of the image data.",
01127     "         Output values are cropped to QuantumRange (no overflow).",
01128     "         This operation is independent of the matte",
01129     "         channels.",
01130     "",
01131     "Minus    The result of image - image window, with underflow",
01132     "         cropped to zero.",
01133     "",
01134     "Add      The result of image + image window, with overflow",
01135     "         wrapping around (mod 256).",
01136     "",
01137     "Subtract The result of image - image window, with underflow",
01138     "         wrapping around (mod 256).  The add and subtract",
01139     "         operators can be used to perform reversible",
01140     "         transformations.",
01141     "",
01142     "Difference",
01143     "         The result of abs(image - image window).  This",
01144     "         useful for comparing two very similar images.",
01145     "",
01146     "Copy     The resulting image is image window replaced with",
01147     "         image.  Here the matte information is ignored.",
01148     "",
01149     "CopyRed  The red layer of the image window is replace with",
01150     "         the red layer of the image.  The other layers are",
01151     "         untouched.",
01152     "",
01153     "CopyGreen",
01154     "         The green layer of the image window is replace with",
01155     "         the green layer of the image.  The other layers are",
01156     "         untouched.",
01157     "",
01158     "CopyBlue The blue layer of the image window is replace with",
01159     "         the blue layer of the image.  The other layers are",
01160     "         untouched.",
01161     "",
01162     "CopyOpacity",
01163     "         The matte layer of the image window is replace with",
01164     "         the matte layer of the image.  The other layers are",
01165     "         untouched.",
01166     "",
01167     "The image compositor requires a matte, or alpha channel in",
01168     "the image for some operations.  This extra channel usually",
01169     "defines a mask which represents a sort of a cookie-cutter",
01170     "for the image.  This the case when matte is opaque (full",
01171     "coverage) for pixels inside the shape, zero outside, and",
01172     "between 0 and QuantumRange on the boundary.  If image does not",
01173     "have a matte channel, it is initialized with 0 for any pixel",
01174     "matching in color to pixel location (0,0), otherwise QuantumRange.",
01175     "",
01176     "Note that matte information for image window is not retained",
01177     "for colormapped X server visuals (e.g. StaticColor,",
01178     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
01179     "behavior may require a TrueColor or DirectColor visual or a",
01180     "Standard Colormap.",
01181     "",
01182     "Choosing a composite operator is optional.  The default",
01183     "operator is replace.  However, you must choose a location to",
01184     "paste your image and press button 1.  Press and hold the",
01185     "button before releasing and an outline of the image will",
01186     "appear to help you identify your location.",
01187     "",
01188     "The actual colors of the pasted image is saved.  However,",
01189     "the color that appears in image window may be different.",
01190     "For example, on a monochrome screen image window will appear",
01191     "black or white even though your pasted image may have",
01192     "many colors.  If the image is saved to a file it is written",
01193     "with the correct colors.  To assure the correct colors are",
01194     "saved in the final image, any PseudoClass image is promoted",
01195     "to DirectClass (see miff(5)).  To force a PseudoClass image",
01196     "to remain PseudoClass, use -colors.",
01197     (char *) NULL,
01198   },
01199   *ImageROIHelp[] =
01200   {
01201     "In region of interest mode, the Command widget has these",
01202     "options:",
01203     "",
01204     "    Help",
01205     "    Dismiss",
01206     "",
01207     "To define a region of interest, press button 1 and drag.",
01208     "The region of interest is defined by a highlighted rectangle",
01209     "that expands or contracts as it follows the pointer.  Once",
01210     "you are satisfied with the region of interest, release the",
01211     "button.  You are now in apply mode.  In apply mode the",
01212     "Command widget has these options:",
01213     "",
01214     "      File",
01215     "        Save...",
01216     "        Print...",
01217     "      Edit",
01218     "        Undo",
01219     "        Redo",
01220     "      Transform",
01221     "        Flop",
01222     "        Flip",
01223     "        Rotate Right",
01224     "        Rotate Left",
01225     "      Enhance",
01226     "        Hue...",
01227     "        Saturation...",
01228     "        Brightness...",
01229     "        Gamma...",
01230     "        Spiff",
01231     "        Dull",
01232     "        Contrast Stretch",
01233     "        Sigmoidal Contrast...",
01234     "        Normalize",
01235     "        Equalize",
01236     "        Negate",
01237     "        Grayscale",
01238     "        Map...",
01239     "        Quantize...",
01240     "      Effects",
01241     "        Despeckle",
01242     "        Emboss",
01243     "        Reduce Noise",
01244     "        Sharpen...",
01245     "        Blur...",
01246     "        Threshold...",
01247     "        Edge Detect...",
01248     "        Spread...",
01249     "        Shade...",
01250     "        Raise...",
01251     "        Segment...",
01252     "      F/X",
01253     "        Solarize...",
01254     "        Sepia Tone...",
01255     "        Swirl...",
01256     "        Implode...",
01257     "        Vignette...",
01258     "        Wave...",
01259     "        Oil Painting...",
01260     "        Charcoal Drawing...",
01261     "      Miscellany",
01262     "        Image Info",
01263     "        Zoom Image",
01264     "        Show Preview...",
01265     "        Show Histogram",
01266     "        Show Matte",
01267     "      Help",
01268     "      Dismiss",
01269     "",
01270     "You can make adjustments to the region of interest by moving",
01271     "the pointer to one of the rectangle corners, pressing a",
01272     "button, and dragging.  Finally, choose an image processing",
01273     "technique from the Command widget.  You can choose more than",
01274     "one image processing technique to apply to an area.",
01275     "Alternatively, you can move the region of interest before",
01276     "applying another image processing technique.  To exit, press",
01277     "Dismiss.",
01278     (char *) NULL,
01279   },
01280   *ImageRotateHelp[] =
01281   {
01282     "In rotate mode, the Command widget has these options:",
01283     "",
01284     "    Pixel Color",
01285     "      black",
01286     "      blue",
01287     "      cyan",
01288     "      green",
01289     "      gray",
01290     "      red",
01291     "      magenta",
01292     "      yellow",
01293     "      white",
01294     "      Browser...",
01295     "    Direction",
01296     "      horizontal",
01297     "      vertical",
01298     "    Help",
01299     "    Dismiss",
01300     "",
01301     "Choose a background color from the Pixel Color sub-menu.",
01302     "Additional background colors can be specified with the color",
01303     "browser.  You can change the menu colors by setting the X",
01304     "resources pen1 through pen9.",
01305     "",
01306     "If you choose the color browser and press Grab, you can",
01307     "select the background color by moving the pointer to the",
01308     "desired color on the screen and press any button.",
01309     "",
01310     "Choose a point in the image window and press this button and",
01311     "hold.  Next, move the pointer to another location in the",
01312     "image.  As you move a line connects the initial location and",
01313     "the pointer.  When you release the button, the degree of",
01314     "image rotation is determined by the slope of the line you",
01315     "just drew.  The slope is relative to the direction you",
01316     "choose from the Direction sub-menu of the Command widget.",
01317     "",
01318     "To cancel the image rotation, move the pointer back to the",
01319     "starting point of the line and release the button.",
01320     (char *) NULL,
01321   };
01322 
01323 /*
01324   Enumeration declarations.
01325 */
01326 typedef enum
01327 {
01328   CopyMode,
01329   CropMode,
01330   CutMode
01331 } ClipboardMode;
01332 
01333 typedef enum
01334 {
01335   OpenCommand,
01336   NextCommand,
01337   FormerCommand,
01338   SelectCommand,
01339   SaveCommand,
01340   PrintCommand,
01341   DeleteCommand,
01342   NewCommand,
01343   VisualDirectoryCommand,
01344   QuitCommand,
01345   UndoCommand,
01346   RedoCommand,
01347   CutCommand,
01348   CopyCommand,
01349   PasteCommand,
01350   HalfSizeCommand,
01351   OriginalSizeCommand,
01352   DoubleSizeCommand,
01353   ResizeCommand,
01354   ApplyCommand,
01355   RefreshCommand,
01356   RestoreCommand,
01357   CropCommand,
01358   ChopCommand,
01359   FlopCommand,
01360   FlipCommand,
01361   RotateRightCommand,
01362   RotateLeftCommand,
01363   RotateCommand,
01364   ShearCommand,
01365   RollCommand,
01366   TrimCommand,
01367   HueCommand,
01368   SaturationCommand,
01369   BrightnessCommand,
01370   GammaCommand,
01371   SpiffCommand,
01372   DullCommand,
01373   ContrastStretchCommand,
01374   SigmoidalContrastCommand,
01375   NormalizeCommand,
01376   EqualizeCommand,
01377   NegateCommand,
01378   GrayscaleCommand,
01379   MapCommand,
01380   QuantizeCommand,
01381   DespeckleCommand,
01382   EmbossCommand,
01383   ReduceNoiseCommand,
01384   AddNoiseCommand,
01385   SharpenCommand,
01386   BlurCommand,
01387   ThresholdCommand,
01388   EdgeDetectCommand,
01389   SpreadCommand,
01390   ShadeCommand,
01391   RaiseCommand,
01392   SegmentCommand,
01393   SolarizeCommand,
01394   SepiaToneCommand,
01395   SwirlCommand,
01396   ImplodeCommand,
01397   VignetteCommand,
01398   WaveCommand,
01399   OilPaintCommand,
01400   CharcoalDrawCommand,
01401   AnnotateCommand,
01402   DrawCommand,
01403   ColorCommand,
01404   MatteCommand,
01405   CompositeCommand,
01406   AddBorderCommand,
01407   AddFrameCommand,
01408   CommentCommand,
01409   LaunchCommand,
01410   RegionofInterestCommand,
01411   ROIHelpCommand,
01412   ROIDismissCommand,
01413   InfoCommand,
01414   ZoomCommand,
01415   ShowPreviewCommand,
01416   ShowHistogramCommand,
01417   ShowMatteCommand,
01418   BackgroundCommand,
01419   SlideShowCommand,
01420   PreferencesCommand,
01421   HelpCommand,
01422   BrowseDocumentationCommand,
01423   VersionCommand,
01424   SaveToUndoBufferCommand,
01425   FreeBuffersCommand,
01426   NullCommand
01427 } CommandType;
01428 
01429 typedef enum
01430 {
01431   AnnotateNameCommand,
01432   AnnotateFontColorCommand,
01433   AnnotateBackgroundColorCommand,
01434   AnnotateRotateCommand,
01435   AnnotateHelpCommand,
01436   AnnotateDismissCommand,
01437   TextHelpCommand,
01438   TextApplyCommand,
01439   ChopDirectionCommand,
01440   ChopHelpCommand,
01441   ChopDismissCommand,
01442   HorizontalChopCommand,
01443   VerticalChopCommand,
01444   ColorEditMethodCommand,
01445   ColorEditColorCommand,
01446   ColorEditBorderCommand,
01447   ColorEditFuzzCommand,
01448   ColorEditUndoCommand,
01449   ColorEditHelpCommand,
01450   ColorEditDismissCommand,
01451   CompositeOperatorsCommand,
01452   CompositeDissolveCommand,
01453   CompositeDisplaceCommand,
01454   CompositeHelpCommand,
01455   CompositeDismissCommand,
01456   CropHelpCommand,
01457   CropDismissCommand,
01458   RectifyCopyCommand,
01459   RectifyHelpCommand,
01460   RectifyDismissCommand,
01461   DrawElementCommand,
01462   DrawColorCommand,
01463   DrawStippleCommand,
01464   DrawWidthCommand,
01465   DrawUndoCommand,
01466   DrawHelpCommand,
01467   DrawDismissCommand,
01468   MatteEditMethod,
01469   MatteEditBorderCommand,
01470   MatteEditFuzzCommand,
01471   MatteEditValueCommand,
01472   MatteEditUndoCommand,
01473   MatteEditHelpCommand,
01474   MatteEditDismissCommand,
01475   PasteOperatorsCommand,
01476   PasteHelpCommand,
01477   PasteDismissCommand,
01478   RotateColorCommand,
01479   RotateDirectionCommand,
01480   RotateCropCommand,
01481   RotateSharpenCommand,
01482   RotateHelpCommand,
01483   RotateDismissCommand,
01484   HorizontalRotateCommand,
01485   VerticalRotateCommand,
01486   TileLoadCommand,
01487   TileNextCommand,
01488   TileFormerCommand,
01489   TileDeleteCommand,
01490   TileUpdateCommand
01491 } ModeType;
01492 
01493 /*
01494   Stipples.
01495 */
01496 #define BricksWidth  20
01497 #define BricksHeight  20
01498 #define DiagonalWidth  16
01499 #define DiagonalHeight  16
01500 #define HighlightWidth  8
01501 #define HighlightHeight  8
01502 #define ScalesWidth  16
01503 #define ScalesHeight  16
01504 #define ShadowWidth  8
01505 #define ShadowHeight  8
01506 #define VerticalWidth  16
01507 #define VerticalHeight  16
01508 #define WavyWidth  16
01509 #define WavyHeight  16
01510 
01511 /*
01512   Constant declaration.
01513 */
01514 static const int
01515   RoiDelta = 8;
01516 
01517 static const unsigned char
01518   BricksBitmap[] =
01519   {
01520     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
01521     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
01522     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
01523     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
01524     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
01525   },
01526   DiagonalBitmap[] =
01527   {
01528     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
01529     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
01530     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
01531   },
01532   ScalesBitmap[] =
01533   {
01534     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
01535     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
01536     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
01537   },
01538   VerticalBitmap[] =
01539   {
01540     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
01541     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
01542     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
01543   },
01544   WavyBitmap[] =
01545   {
01546     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
01547     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
01548     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
01549   };
01550 
01551 /*
01552   Function prototypes.
01553 */
01554 static CommandType
01555   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
01556     const MagickStatusType,KeySym,Image **);
01557 
01558 static Image
01559   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
01560     Image **),
01561   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
01562   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
01563   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
01564 
01565 static MagickBooleanType
01566   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
01567   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
01568   XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
01569   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
01570   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
01571   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
01572   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
01573   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
01574   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
01575   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
01576   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
01577   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
01578   XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
01579   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
01580   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
01581 
01582 static void
01583   XDrawPanRectangle(Display *,XWindows *),
01584   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
01585   XMagnifyImage(Display *,XWindows *,XEvent *),
01586   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
01587   XPanImage(Display *,XWindows *,XEvent *),
01588   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
01589     const KeySym),
01590   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
01591   XScreenEvent(Display *,XWindows *,XEvent *),
01592   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
01593 
01594 /*
01595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01596 %                                                                             %
01597 %                                                                             %
01598 %                                                                             %
01599 %   D i s p l a y I m a g e s                                                 %
01600 %                                                                             %
01601 %                                                                             %
01602 %                                                                             %
01603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01604 %
01605 %  DisplayImages() displays an image sequence to any X window screen.  It
01606 %  returns a value other than 0 if successful.  Check the exception member
01607 %  of image to determine the reason for any failure.
01608 %
01609 %  The format of the DisplayImages method is:
01610 %
01611 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
01612 %        Image *images)
01613 %
01614 %  A description of each parameter follows:
01615 %
01616 %    o image_info: the image info.
01617 %
01618 %    o image: the image.
01619 %
01620 */
01621 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
01622   Image *images)
01623 {
01624   char
01625     *argv[1];
01626 
01627   Display
01628     *display;
01629 
01630   Image
01631     *image;
01632 
01633   register long
01634     i;
01635 
01636   unsigned long
01637     state;
01638 
01639   XrmDatabase
01640     resource_database;
01641 
01642   XResourceInfo
01643     resource_info;
01644 
01645   assert(image_info != (const ImageInfo *) NULL);
01646   assert(image_info->signature == MagickSignature);
01647   assert(images != (Image *) NULL);
01648   assert(images->signature == MagickSignature);
01649   if (images->debug != MagickFalse)
01650     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
01651   display=XOpenDisplay(image_info->server_name);
01652   if (display == (Display *) NULL)
01653     {
01654       (void) ThrowMagickException(&images->exception,GetMagickModule(),
01655         XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
01656         image_info->server_name));
01657       return(MagickFalse);
01658     }
01659   if (images->exception.severity != UndefinedException)
01660     CatchException(&images->exception);
01661   (void) XSetErrorHandler(XError);
01662   resource_database=XGetResourceDatabase(display,GetClientName());
01663   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
01664   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
01665   if (image_info->page != (char *) NULL)
01666     resource_info.image_geometry=AcquireString(image_info->page);
01667   resource_info.immutable=MagickTrue;
01668   argv[0]=AcquireString(GetClientName());
01669   state=DefaultState;
01670   for (i=0; (state & ExitState) == 0; i++)
01671   {
01672     image=GetImageFromList(images,i % GetImageListLength(images));
01673     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
01674   }
01675   argv[0]=DestroyString(argv[0]);
01676   (void) XCloseDisplay(display);
01677   XDestroyResourceInfo(&resource_info);
01678   if (images->exception.severity != UndefinedException)
01679     return(MagickFalse);
01680   return(MagickTrue);
01681 }
01682 
01683 /*
01684 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01685 %                                                                             %
01686 %                                                                             %
01687 %                                                                             %
01688 %   R e m o t e D i s p l a y C o m m a n d                                   %
01689 %                                                                             %
01690 %                                                                             %
01691 %                                                                             %
01692 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01693 %
01694 %  RemoteDisplayCommand() encourages a remote display program to display the
01695 %  specified image filename.
01696 %
01697 %  The format of the RemoteDisplayCommand method is:
01698 %
01699 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
01700 %        const char *window,const char *filename,ExceptionInfo *exception)
01701 %
01702 %  A description of each parameter follows:
01703 %
01704 %    o image_info: the image info.
01705 %
01706 %    o window: Specifies the name or id of an X window.
01707 %
01708 %    o filename: the name of the image filename to display.
01709 %
01710 %    o exception: return any errors or warnings in this structure.
01711 %
01712 */
01713 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
01714   const char *window,const char *filename,ExceptionInfo *exception)
01715 {
01716   Display
01717     *display;
01718 
01719   MagickStatusType
01720     status;
01721 
01722   assert(image_info != (const ImageInfo *) NULL);
01723   assert(image_info->signature == MagickSignature);
01724   assert(filename != (char *) NULL);
01725   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
01726   display=XOpenDisplay(image_info->server_name);
01727   if (display == (Display *) NULL)
01728     {
01729       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
01730         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
01731       return(MagickFalse);
01732     }
01733   (void) XSetErrorHandler(XError);
01734   status=XRemoteCommand(display,window,filename);
01735   (void) XCloseDisplay(display);
01736   return(status != 0 ? MagickTrue : MagickFalse);
01737 }
01738 
01739 /*
01740 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01741 %                                                                             %
01742 %                                                                             %
01743 %                                                                             %
01744 +   X A n n o t a t e E d i t I m a g e                                       %
01745 %                                                                             %
01746 %                                                                             %
01747 %                                                                             %
01748 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01749 %
01750 %  XAnnotateEditImage() annotates the image with text.
01751 %
01752 %  The format of the XAnnotateEditImage method is:
01753 %
01754 %      MagickBooleanType XAnnotateEditImage(Display *display,
01755 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
01756 %
01757 %  A description of each parameter follows:
01758 %
01759 %    o display: Specifies a connection to an X server;  returned from
01760 %      XOpenDisplay.
01761 %
01762 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
01763 %
01764 %    o windows: Specifies a pointer to a XWindows structure.
01765 %
01766 %    o image: the image; returned from
01767 %      ReadImage.
01768 %
01769 */
01770 
01771 static inline long MagickMax(const long x,const long y)
01772 {
01773   if (x > y)
01774     return(x);
01775   return(y);
01776 }
01777 
01778 static inline long MagickMin(const long x,const long y)
01779 {
01780   if (x < y)
01781     return(x);
01782   return(y);
01783 }
01784 
01785 static MagickBooleanType XAnnotateEditImage(Display *display,
01786   XResourceInfo *resource_info,XWindows *windows,Image *image)
01787 {
01788   static const char
01789     *AnnotateMenu[] =
01790     {
01791       "Font Name",
01792       "Font Color",
01793       "Box Color",
01794       "Rotate Text",
01795       "Help",
01796       "Dismiss",
01797       (char *) NULL
01798     },
01799     *TextMenu[] =
01800     {
01801       "Help",
01802       "Apply",
01803       (char *) NULL
01804     };
01805 
01806   static const ModeType
01807     AnnotateCommands[] =
01808     {
01809       AnnotateNameCommand,
01810       AnnotateFontColorCommand,
01811       AnnotateBackgroundColorCommand,
01812       AnnotateRotateCommand,
01813       AnnotateHelpCommand,
01814       AnnotateDismissCommand
01815     },
01816     TextCommands[] =
01817     {
01818       TextHelpCommand,
01819       TextApplyCommand
01820     };
01821 
01822   static MagickBooleanType
01823     transparent_box = MagickTrue,
01824     transparent_pen = MagickFalse;
01825 
01826   static MagickRealType
01827     degrees = 0.0;
01828 
01829   static unsigned int
01830     box_id = MaxNumberPens-2,
01831     font_id = 0,
01832     pen_id = 0;
01833 
01834   char
01835     command[MaxTextExtent],
01836     text[MaxTextExtent];
01837 
01838   const char
01839     *ColorMenu[MaxNumberPens+1];
01840 
01841   Cursor
01842     cursor;
01843 
01844   GC
01845     annotate_context;
01846 
01847   int
01848     id,
01849     pen_number,
01850     status,
01851     x,
01852     y;
01853 
01854   KeySym
01855     key_symbol;
01856 
01857   register char
01858     *p;
01859 
01860   register long
01861     i;
01862 
01863   unsigned int
01864     height,
01865     width;
01866 
01867   unsigned long
01868     state;
01869 
01870   XAnnotateInfo
01871     *annotate_info,
01872     *previous_info;
01873 
01874   XColor
01875     color;
01876 
01877   XFontStruct
01878     *font_info;
01879 
01880   XEvent
01881     event,
01882     text_event;
01883 
01884   /*
01885     Map Command widget.
01886   */
01887   (void) CloneString(&windows->command.name,"Annotate");
01888   windows->command.data=4;
01889   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
01890   (void) XMapRaised(display,windows->command.id);
01891   XClientMessage(display,windows->image.id,windows->im_protocols,
01892     windows->im_update_widget,CurrentTime);
01893   /*
01894     Track pointer until button 1 is pressed.
01895   */
01896   XQueryPosition(display,windows->image.id,&x,&y);
01897   (void) XSelectInput(display,windows->image.id,
01898     windows->image.attributes.event_mask | PointerMotionMask);
01899   cursor=XCreateFontCursor(display,XC_left_side);
01900   (void) XCheckDefineCursor(display,windows->image.id,cursor);
01901   state=DefaultState;
01902   do
01903   {
01904     if (windows->info.mapped != MagickFalse)
01905       {
01906         /*
01907           Display pointer position.
01908         */
01909         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
01910           x+windows->image.x,y+windows->image.y);
01911         XInfoWidget(display,windows,text);
01912       }
01913     /*
01914       Wait for next event.
01915     */
01916     XScreenEvent(display,windows,&event);
01917     if (event.xany.window == windows->command.id)
01918       {
01919         /*
01920           Select a command from the Command widget.
01921         */
01922         id=XCommandWidget(display,windows,AnnotateMenu,&event);
01923         (void) XCheckDefineCursor(display,windows->image.id,cursor);
01924         if (id < 0)
01925           continue;
01926         switch (AnnotateCommands[id])
01927         {
01928           case AnnotateNameCommand:
01929           {
01930             const char
01931               *FontMenu[MaxNumberFonts];
01932 
01933             int
01934               font_number;
01935 
01936             /*
01937               Initialize menu selections.
01938             */
01939             for (i=0; i < MaxNumberFonts; i++)
01940               FontMenu[i]=resource_info->font_name[i];
01941             FontMenu[MaxNumberFonts-2]="Browser...";
01942             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
01943             /*
01944               Select a font name from the pop-up menu.
01945             */
01946             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
01947               (const char **) FontMenu,command);
01948             if (font_number < 0)
01949               break;
01950             if (font_number == (MaxNumberFonts-2))
01951               {
01952                 static char
01953                   font_name[MaxTextExtent] = "fixed";
01954 
01955                 /*
01956                   Select a font name from a browser.
01957                 */
01958                 resource_info->font_name[font_number]=font_name;
01959                 XFontBrowserWidget(display,windows,"Select",font_name);
01960                 if (*font_name == '\0')
01961                   break;
01962               }
01963             /*
01964               Initialize font info.
01965             */
01966             font_info=XLoadQueryFont(display,resource_info->font_name[
01967               font_number]);
01968             if (font_info == (XFontStruct *) NULL)
01969               {
01970                 XNoticeWidget(display,windows,"Unable to load font:",
01971                   resource_info->font_name[font_number]);
01972                 break;
01973               }
01974             font_id=(unsigned int) font_number;
01975             (void) XFreeFont(display,font_info);
01976             break;
01977           }
01978           case AnnotateFontColorCommand:
01979           {
01980             /*
01981               Initialize menu selections.
01982             */
01983             for (i=0; i < (int) (MaxNumberPens-2); i++)
01984               ColorMenu[i]=resource_info->pen_colors[i];
01985             ColorMenu[MaxNumberPens-2]="transparent";
01986             ColorMenu[MaxNumberPens-1]="Browser...";
01987             ColorMenu[MaxNumberPens]=(const char *) NULL;
01988             /*
01989               Select a pen color from the pop-up menu.
01990             */
01991             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
01992               (const char **) ColorMenu,command);
01993             if (pen_number < 0)
01994               break;
01995             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
01996               MagickFalse;
01997             if (transparent_pen != MagickFalse)
01998               break;
01999             if (pen_number == (MaxNumberPens-1))
02000               {
02001                 static char
02002                   color_name[MaxTextExtent] = "gray";
02003 
02004                 /*
02005                   Select a pen color from a dialog.
02006                 */
02007                 resource_info->pen_colors[pen_number]=color_name;
02008                 XColorBrowserWidget(display,windows,"Select",color_name);
02009                 if (*color_name == '\0')
02010                   break;
02011               }
02012             /*
02013               Set pen color.
02014             */
02015             (void) XParseColor(display,windows->map_info->colormap,
02016               resource_info->pen_colors[pen_number],&color);
02017             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
02018               (unsigned int) MaxColors,&color);
02019             windows->pixel_info->pen_colors[pen_number]=color;
02020             pen_id=(unsigned int) pen_number;
02021             break;
02022           }
02023           case AnnotateBackgroundColorCommand:
02024           {
02025             /*
02026               Initialize menu selections.
02027             */
02028             for (i=0; i < (int) (MaxNumberPens-2); i++)
02029               ColorMenu[i]=resource_info->pen_colors[i];
02030             ColorMenu[MaxNumberPens-2]="transparent";
02031             ColorMenu[MaxNumberPens-1]="Browser...";
02032             ColorMenu[MaxNumberPens]=(const char *) NULL;
02033             /*
02034               Select a pen color from the pop-up menu.
02035             */
02036             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
02037               (const char **) ColorMenu,command);
02038             if (pen_number < 0)
02039               break;
02040             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
02041               MagickFalse;
02042             if (transparent_box != MagickFalse)
02043               break;
02044             if (pen_number == (MaxNumberPens-1))
02045               {
02046                 static char
02047                   color_name[MaxTextExtent] = "gray";
02048 
02049                 /*
02050                   Select a pen color from a dialog.
02051                 */
02052                 resource_info->pen_colors[pen_number]=color_name;
02053                 XColorBrowserWidget(display,windows,"Select",color_name);
02054                 if (*color_name == '\0')
02055                   break;
02056               }
02057             /*
02058               Set pen color.
02059             */
02060             (void) XParseColor(display,windows->map_info->colormap,
02061               resource_info->pen_colors[pen_number],&color);
02062             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
02063               (unsigned int) MaxColors,&color);
02064             windows->pixel_info->pen_colors[pen_number]=color;
02065             box_id=(unsigned int) pen_number;
02066             break;
02067           }
02068           case AnnotateRotateCommand:
02069           {
02070             int
02071               entry;
02072 
02073             static char
02074               angle[MaxTextExtent] = "30.0";
02075 
02076             static const char
02077               *RotateMenu[] =
02078               {
02079                 "-90",
02080                 "-45",
02081                 "-30",
02082                 "0",
02083                 "30",
02084                 "45",
02085                 "90",
02086                 "180",
02087                 "Dialog...",
02088                 (char *) NULL,
02089               };
02090 
02091             /*
02092               Select a command from the pop-up menu.
02093             */
02094             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
02095               command);
02096             if (entry < 0)
02097               break;
02098             if (entry != 8)
02099               {
02100                 degrees=atof(RotateMenu[entry]);
02101                 break;
02102               }
02103             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
02104               angle);
02105             if (*angle == '\0')
02106               break;
02107             degrees=atof(angle);
02108             break;
02109           }
02110           case AnnotateHelpCommand:
02111           {
02112             XTextViewWidget(display,resource_info,windows,MagickFalse,
02113               "Help Viewer - Image Annotation",ImageAnnotateHelp);
02114             break;
02115           }
02116           case AnnotateDismissCommand:
02117           {
02118             /*
02119               Prematurely exit.
02120             */
02121             state|=EscapeState;
02122             state|=ExitState;
02123             break;
02124           }
02125           default:
02126             break;
02127         }
02128         continue;
02129       }
02130     switch (event.type)
02131     {
02132       case ButtonPress:
02133       {
02134         if (event.xbutton.button != Button1)
02135           break;
02136         if (event.xbutton.window != windows->image.id)
02137           break;
02138         /*
02139           Change to text entering mode.
02140         */
02141         x=event.xbutton.x;
02142         y=event.xbutton.y;
02143         state|=ExitState;
02144         break;
02145       }
02146       case ButtonRelease:
02147         break;
02148       case Expose:
02149         break;
02150       case KeyPress:
02151       {
02152         if (event.xkey.window != windows->image.id)
02153           break;
02154         /*
02155           Respond to a user key press.
02156         */
02157         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
02158           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
02159         switch ((int) key_symbol)
02160         {
02161           case XK_Escape:
02162           case XK_F20:
02163           {
02164             /*
02165               Prematurely exit.
02166             */
02167             state|=EscapeState;
02168             state|=ExitState;
02169             break;
02170           }
02171           case XK_F1:
02172           case XK_Help:
02173           {
02174             XTextViewWidget(display,resource_info,windows,MagickFalse,
02175               "Help Viewer - Image Annotation",ImageAnnotateHelp);
02176             break;
02177           }
02178           default:
02179           {
02180             (void) XBell(display,0);
02181             break;
02182           }
02183         }
02184         break;
02185       }
02186       case MotionNotify:
02187       {
02188         /*
02189           Map and unmap Info widget as cursor crosses its boundaries.
02190         */
02191         x=event.xmotion.x;
02192         y=event.xmotion.y;
02193         if (windows->info.mapped != MagickFalse)
02194           {
02195             if ((x < (int) (windows->info.x+windows->info.width)) &&
02196                 (y < (int) (windows->info.y+windows->info.height)))
02197               (void) XWithdrawWindow(display,windows->info.id,
02198                 windows->info.screen);
02199           }
02200         else
02201           if ((x > (int) (windows->info.x+windows->info.width)) ||
02202               (y > (int) (windows->info.y+windows->info.height)))
02203             (void) XMapWindow(display,windows->info.id);
02204         break;
02205       }
02206       default:
02207         break;
02208     }
02209   } while ((state & ExitState) == 0);
02210   (void) XSelectInput(display,windows->image.id,
02211     windows->image.attributes.event_mask);
02212   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
02213   if ((state & EscapeState) != 0)
02214     return(MagickTrue);
02215   /*
02216     Set font info and check boundary conditions.
02217   */
02218   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
02219   if (font_info == (XFontStruct *) NULL)
02220     {
02221       XNoticeWidget(display,windows,"Unable to load font:",
02222         resource_info->font_name[font_id]);
02223       font_info=windows->font_info;
02224     }
02225   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
02226     x=(int) windows->image.width-font_info->max_bounds.width;
02227   if (y < (int) (font_info->ascent+font_info->descent))
02228     y=(int) font_info->ascent+font_info->descent;
02229   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
02230       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
02231     return(MagickFalse);
02232   /*
02233     Initialize annotate structure.
02234   */
02235   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
02236   if (annotate_info == (XAnnotateInfo *) NULL)
02237     return(MagickFalse);
02238   XGetAnnotateInfo(annotate_info);
02239   annotate_info->x=x;
02240   annotate_info->y=y;
02241   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
02242     annotate_info->stencil=OpaqueStencil;
02243   else
02244     if (transparent_box == MagickFalse)
02245       annotate_info->stencil=BackgroundStencil;
02246     else
02247       annotate_info->stencil=ForegroundStencil;
02248   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
02249   annotate_info->degrees=degrees;
02250   annotate_info->font_info=font_info;
02251   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
02252     windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
02253     sizeof(*annotate_info->text));
02254   if (annotate_info->text == (char *) NULL)
02255     return(MagickFalse);
02256   /*
02257     Create cursor and set graphic context.
02258   */
02259   cursor=XCreateFontCursor(display,XC_pencil);
02260   (void) XCheckDefineCursor(display,windows->image.id,cursor);
02261   annotate_context=windows->image.annotate_context;
02262   (void) XSetFont(display,annotate_context,font_info->fid);
02263   (void) XSetBackground(display,annotate_context,
02264     windows->pixel_info->pen_colors[box_id].pixel);
02265   (void) XSetForeground(display,annotate_context,
02266     windows->pixel_info->pen_colors[pen_id].pixel);
02267   /*
02268     Begin annotating the image with text.
02269   */
02270   (void) CloneString(&windows->command.name,"Text");
02271   windows->command.data=0;
02272   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
02273   state=DefaultState;
02274   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
02275   text_event.xexpose.width=(int) font_info->max_bounds.width;
02276   text_event.xexpose.height=font_info->max_bounds.ascent+
02277     font_info->max_bounds.descent;
02278   p=annotate_info->text;
02279   do
02280   {
02281     /*
02282       Display text cursor.
02283     */
02284     *p='\0';
02285     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
02286     /*
02287       Wait for next event.
02288     */
02289     XScreenEvent(display,windows,&event);
02290     if (event.xany.window == windows->command.id)
02291       {
02292         /*
02293           Select a command from the Command widget.
02294         */
02295         (void) XSetBackground(display,annotate_context,
02296           windows->pixel_info->background_color.pixel);
02297         (void) XSetForeground(display,annotate_context,
02298           windows->pixel_info->foreground_color.pixel);
02299         id=XCommandWidget(display,windows,AnnotateMenu,&event);
02300         (void) XSetBackground(display,annotate_context,
02301           windows->pixel_info->pen_colors[box_id].pixel);
02302         (void) XSetForeground(display,annotate_context,
02303           windows->pixel_info->pen_colors[pen_id].pixel);
02304         if (id < 0)
02305           continue;
02306         switch (TextCommands[id])
02307         {
02308           case TextHelpCommand:
02309           {
02310             XTextViewWidget(display,resource_info,windows,MagickFalse,
02311               "Help Viewer - Image Annotation",ImageAnnotateHelp);
02312             (void) XCheckDefineCursor(display,windows->image.id,cursor);
02313             break;
02314           }
02315           case TextApplyCommand:
02316           {
02317             /*
02318               Finished annotating.
02319             */
02320             annotate_info->width=(unsigned int) XTextWidth(font_info,
02321               annotate_info->text,(int) strlen(annotate_info->text));
02322             XRefreshWindow(display,&windows->image,&text_event);
02323             state|=ExitState;
02324             break;
02325           }
02326           default:
02327             break;
02328         }
02329         continue;
02330       }
02331     /*
02332       Erase text cursor.
02333     */
02334     text_event.xexpose.x=x;
02335     text_event.xexpose.y=y-font_info->max_bounds.ascent;
02336     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
02337       (unsigned int) text_event.xexpose.width,(unsigned int)
02338       text_event.xexpose.height,MagickFalse);
02339     XRefreshWindow(display,&windows->image,&text_event);
02340     switch (event.type)
02341     {
02342       case ButtonPress:
02343       {
02344         if (event.xbutton.window != windows->image.id)
02345           break;
02346         if (event.xbutton.button == Button2)
02347           {
02348             /*
02349               Request primary selection.
02350             */
02351             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
02352               windows->image.id,CurrentTime);
02353             break;
02354           }
02355         break;
02356       }
02357       case Expose:
02358       {
02359         if (event.xexpose.count == 0)
02360           {
02361             XAnnotateInfo
02362               *text_info;
02363 
02364             /*
02365               Refresh Image window.
02366             */
02367             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
02368             text_info=annotate_info;
02369             while (text_info != (XAnnotateInfo *) NULL)
02370             {
02371               if (annotate_info->stencil == ForegroundStencil)
02372                 (void) XDrawString(display,windows->image.id,annotate_context,
02373                   text_info->x,text_info->y,text_info->text,
02374                   (int) strlen(text_info->text));
02375               else
02376                 (void) XDrawImageString(display,windows->image.id,
02377                   annotate_context,text_info->x,text_info->y,text_info->text,
02378                   (int) strlen(text_info->text));
02379               text_info=text_info->previous;
02380             }
02381             (void) XDrawString(display,windows->image.id,annotate_context,
02382               x,y,"_",1);
02383           }
02384         break;
02385       }
02386       case KeyPress:
02387       {
02388         int
02389           length;
02390 
02391         if (event.xkey.window != windows->image.id)
02392           break;
02393         /*
02394           Respond to a user key press.
02395         */
02396         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
02397           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
02398         *(command+length)='\0';
02399         if (((event.xkey.state & ControlMask) != 0) ||
02400             ((event.xkey.state & Mod1Mask) != 0))
02401           state|=ModifierState;
02402         if ((state & ModifierState) != 0)
02403           switch ((int) key_symbol)
02404           {
02405             case XK_u:
02406             case XK_U:
02407             {
02408               key_symbol=DeleteCommand;
02409               break;
02410             }
02411             default:
02412               break;
02413           }
02414         switch ((int) key_symbol)
02415         {
02416           case XK_BackSpace:
02417           {
02418             /*
02419               Erase one character.
02420             */
02421             if (p == annotate_info->text)
02422               {
02423                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
02424                   break;
02425                 else
02426                   {
02427                     /*
02428                       Go to end of the previous line of text.
02429                     */
02430                     annotate_info=annotate_info->previous;
02431                     p=annotate_info->text;
02432                     x=annotate_info->x+annotate_info->width;
02433                     y=annotate_info->y;
02434                     if (annotate_info->width != 0)
02435                       p+=strlen(annotate_info->text);
02436                     break;
02437                   }
02438               }
02439             p--;
02440             x-=XTextWidth(font_info,p,1);
02441             text_event.xexpose.x=x;
02442             text_event.xexpose.y=y-font_info->max_bounds.ascent;
02443             XRefreshWindow(display,&windows->image,&text_event);
02444             break;
02445           }
02446           case XK_bracketleft:
02447           {
02448             key_symbol=XK_Escape;
02449             break;
02450           }
02451           case DeleteCommand:
02452           {
02453             /*
02454               Erase the entire line of text.
02455             */
02456             while (p != annotate_info->text)
02457             {
02458               p--;
02459               x-=XTextWidth(font_info,p,1);
02460               text_event.xexpose.x=x;
02461               XRefreshWindow(display,&windows->image,&text_event);
02462             }
02463             break;
02464           }
02465           case XK_Escape:
02466           case XK_F20:
02467           {
02468             /*
02469               Finished annotating.
02470             */
02471             annotate_info->width=(unsigned int) XTextWidth(font_info,
02472               annotate_info->text,(int) strlen(annotate_info->text));
02473             XRefreshWindow(display,&windows->image,&text_event);
02474             state|=ExitState;
02475             break;
02476           }
02477           default:
02478           {
02479             /*
02480               Draw a single character on the Image window.
02481             */
02482             if ((state & ModifierState) != 0)
02483               break;
02484             if (*command == '\0')
02485               break;
02486             *p=(*command);
02487             if (annotate_info->stencil == ForegroundStencil)
02488               (void) XDrawString(display,windows->image.id,annotate_context,
02489                 x,y,p,1);
02490             else
02491               (void) XDrawImageString(display,windows->image.id,
02492                 annotate_context,x,y,p,1);
02493             x+=XTextWidth(font_info,p,1);
02494             p++;
02495             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
02496               break;
02497           }
02498           case XK_Return:
02499           case XK_KP_Enter:
02500           {
02501             /*
02502               Advance to the next line of text.
02503             */
02504             *p='\0';
02505             annotate_info->width=(unsigned int) XTextWidth(font_info,
02506               annotate_info->text,(int) strlen(annotate_info->text));
02507             if (annotate_info->next != (XAnnotateInfo *) NULL)
02508               {
02509                 /*
02510                   Line of text already exists.
02511                 */
02512                 annotate_info=annotate_info->next;
02513                 x=annotate_info->x;
02514                 y=annotate_info->y;
02515                 p=annotate_info->text;
02516                 break;
02517               }
02518             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
02519               sizeof(*annotate_info->next));
02520             if (annotate_info->next == (XAnnotateInfo *) NULL)
02521               return(MagickFalse);
02522             *annotate_info->next=(*annotate_info);
02523             annotate_info->next->previous=annotate_info;
02524             annotate_info=annotate_info->next;
02525             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
02526               windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
02527               sizeof(*annotate_info->text));
02528             if (annotate_info->text == (char *) NULL)
02529               return(MagickFalse);
02530             annotate_info->y+=annotate_info->height;
02531             if (annotate_info->y > (int) windows->image.height)
02532               annotate_info->y=(int) annotate_info->height;
02533             annotate_info->next=(XAnnotateInfo *) NULL;
02534             x=annotate_info->x;
02535             y=annotate_info->y;
02536             p=annotate_info->text;
02537             break;
02538           }
02539         }
02540         break;
02541       }
02542       case KeyRelease:
02543       {
02544         /*
02545           Respond to a user key release.
02546         */
02547         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
02548           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
02549         state&=(~ModifierState);
02550         break;
02551       }
02552       case SelectionNotify:
02553       {
02554         Atom
02555           type;
02556 
02557         int
02558           format;
02559 
02560         unsigned char
02561           *data;
02562 
02563         unsigned long
02564           after,
02565           length;
02566 
02567         /*
02568           Obtain response from primary selection.
02569         */
02570         if (event.xselection.property == (Atom) None)
02571           break;
02572         status=XGetWindowProperty(display,event.xselection.requestor,
02573           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
02574           &type,&format,&length,&after,&data);
02575         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
02576             (length == 0))
02577           break;
02578         /*
02579           Annotate Image window with primary selection.
02580         */
02581         for (i=0; i < (long) length; i++)
02582         {
02583           if ((char) data[i] != '\n')
02584             {
02585               /*
02586                 Draw a single character on the Image window.
02587               */
02588               *p=(char) data[i];
02589               (void) XDrawString(display,windows->image.id,annotate_context,
02590                 x,y,p,1);
02591               x+=XTextWidth(font_info,p,1);
02592               p++;
02593               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
02594                 continue;
02595             }
02596           /*
02597             Advance to the next line of text.
02598           */
02599           *p='\0';
02600           annotate_info->width=(unsigned int) XTextWidth(font_info,
02601             annotate_info->text,(int) strlen(annotate_info->text));
02602           if (annotate_info->next != (XAnnotateInfo *) NULL)
02603             {
02604               /*
02605                 Line of text already exists.
02606               */
02607               annotate_info=annotate_info->next;
02608               x=annotate_info->x;
02609               y=annotate_info->y;
02610               p=annotate_info->text;
02611               continue;
02612             }
02613           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
02614             sizeof(*annotate_info->next));
02615           if (annotate_info->next == (XAnnotateInfo *) NULL)
02616             return(MagickFalse);
02617           *annotate_info->next=(*annotate_info);
02618           annotate_info->next->previous=annotate_info;
02619           annotate_info=annotate_info->next;
02620           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
02621             windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
02622             sizeof(*annotate_info->text));
02623           if (annotate_info->text == (char *) NULL)
02624             return(MagickFalse);
02625           annotate_info->y+=annotate_info->height;
02626           if (annotate_info->y > (int) windows->image.height)
02627             annotate_info->y=(int) annotate_info->height;
02628           annotate_info->next=(XAnnotateInfo *) NULL;
02629           x=annotate_info->x;
02630           y=annotate_info->y;
02631           p=annotate_info->text;
02632         }
02633         (void) XFree((void *) data);
02634         break;
02635       }
02636       default:
02637         break;
02638     }
02639   } while ((state & ExitState) == 0);
02640   (void) XFreeCursor(display,cursor);
02641   /*
02642     Annotation is relative to image configuration.
02643   */
02644   width=(unsigned int) image->columns;
02645   height=(unsigned int) image->rows;
02646   x=0;
02647   y=0;
02648   if (windows->image.crop_geometry != (char *) NULL)
02649     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
02650   /*
02651     Initialize annotated image.
02652   */
02653   XSetCursorState(display,windows,MagickTrue);
02654   XCheckRefreshWindows(display,windows);
02655   while (annotate_info != (XAnnotateInfo *) NULL)
02656   {
02657     if (annotate_info->width == 0)
02658       {
02659         /*
02660           No text on this line--  go to the next line of text.
02661         */
02662         previous_info=annotate_info->previous;
02663         annotate_info->text=(char *)
02664           RelinquishMagickMemory(annotate_info->text);
02665         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
02666         annotate_info=previous_info;
02667         continue;
02668       }
02669     /*
02670       Determine pixel index for box and pen color.
02671     */
02672     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
02673     if (windows->pixel_info->colors != 0)
02674       for (i=0; i < (long) windows->pixel_info->colors; i++)
02675         if (windows->pixel_info->pixels[i] ==
02676             windows->pixel_info->pen_colors[box_id].pixel)
02677           {
02678             windows->pixel_info->box_index=(unsigned short) i;
02679             break;
02680           }
02681     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
02682     if (windows->pixel_info->colors != 0)
02683       for (i=0; i < (long) windows->pixel_info->colors; i++)
02684         if (windows->pixel_info->pixels[i] ==
02685             windows->pixel_info->pen_colors[pen_id].pixel)
02686           {
02687             windows->pixel_info->pen_index=(unsigned short) i;
02688             break;
02689           }
02690     /*
02691       Define the annotate geometry string.
02692     */
02693     annotate_info->x=(int)
02694       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
02695     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
02696       windows->image.y)/windows->image.ximage->height;
02697     (void) FormatMagickString(annotate_info->geometry,MaxTextExtent,
02698       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
02699       height*annotate_info->height/windows->image.ximage->height,
02700       annotate_info->x+x,annotate_info->y+y);
02701     /*
02702       Annotate image with text.
02703     */
02704     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
02705     if (status == 0)
02706       return(MagickFalse);
02707     /*
02708       Free up memory.
02709     */
02710     previous_info=annotate_info->previous;
02711     annotate_info->text=DestroyString(annotate_info->text);
02712     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
02713     annotate_info=previous_info;
02714   }
02715   (void) XSetForeground(display,annotate_context,
02716     windows->pixel_info->foreground_color.pixel);
02717   (void) XSetBackground(display,annotate_context,
02718     windows->pixel_info->background_color.pixel);
02719   (void) XSetFont(display,annotate_context,windows->font_info->fid);
02720   XSetCursorState(display,windows,MagickFalse);
02721   (void) XFreeFont(display,font_info);
02722   /*
02723     Update image configuration.
02724   */
02725   XConfigureImageColormap(display,resource_info,windows,image);
02726   (void) XConfigureImage(display,resource_info,windows,image);
02727   return(MagickTrue);
02728 }
02729 
02730 /*
02731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02732 %                                                                             %
02733 %                                                                             %
02734 %                                                                             %
02735 +   X B a c k g r o u n d I m a g e                                           %
02736 %                                                                             %
02737 %                                                                             %
02738 %                                                                             %
02739 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02740 %
02741 %  XBackgroundImage() displays the image in the background of a window.
02742 %
02743 %  The format of the XBackgroundImage method is:
02744 %
02745 %      MagickBooleanType XBackgroundImage(Display *display,
02746 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
02747 %
02748 %  A description of each parameter follows:
02749 %
02750 %    o display: Specifies a connection to an X server; returned from
02751 %      XOpenDisplay.
02752 %
02753 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
02754 %
02755 %    o windows: Specifies a pointer to a XWindows structure.
02756 %
02757 %    o image: the image;  returned from
02758 %      ReadImage.
02759 %
02760 */
02761 static MagickBooleanType XBackgroundImage(Display *display,
02762   XResourceInfo *resource_info,XWindows *windows,Image **image)
02763 {
02764 #define BackgroundImageTag  "Background/Image"
02765 
02766   int
02767     status;
02768 
02769   static char
02770     window_id[MaxTextExtent] = "root";
02771 
02772   XResourceInfo
02773     background_resources;
02774 
02775   /*
02776     Put image in background.
02777   */
02778   status=XDialogWidget(display,windows,"Background",
02779     "Enter window id (id 0x00 selects window with pointer):",window_id);
02780   if (*window_id == '\0')
02781     return(MagickFalse);
02782   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
02783   XInfoWidget(display,windows,BackgroundImageTag);
02784   XSetCursorState(display,windows,MagickTrue);
02785   XCheckRefreshWindows(display,windows);
02786   background_resources=(*resource_info);
02787   background_resources.window_id=window_id;
02788   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
02789   status=XDisplayBackgroundImage(display,&background_resources,*image);
02790   if (status != MagickFalse)
02791     XClientMessage(display,windows->image.id,windows->im_protocols,
02792       windows->im_retain_colors,CurrentTime);
02793   XSetCursorState(display,windows,MagickFalse);
02794   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
02795   return(MagickTrue);
02796 }
02797 
02798 /*
02799 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02800 %                                                                             %
02801 %                                                                             %
02802 %                                                                             %
02803 +   X C h o p I m a g e                                                       %
02804 %                                                                             %
02805 %                                                                             %
02806 %                                                                             %
02807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02808 %
02809 %  XChopImage() chops the X image.
02810 %
02811 %  The format of the XChopImage method is:
02812 %
02813 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
02814 %      XWindows *windows,Image **image)
02815 %
02816 %  A description of each parameter follows:
02817 %
02818 %    o display: Specifies a connection to an X server; returned from
02819 %      XOpenDisplay.
02820 %
02821 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
02822 %
02823 %    o windows: Specifies a pointer to a XWindows structure.
02824 %
02825 %    o image: the image;  returned from
02826 %      ReadImage.
02827 %
02828 */
02829 static MagickBooleanType XChopImage(Display *display,
02830   XResourceInfo *resource_info,XWindows *windows,Image **image)
02831 {
02832   static const char
02833     *ChopMenu[] =
02834     {
02835       "Direction",
02836       "Help",
02837       "Dismiss",
02838       (char *) NULL
02839     };
02840 
02841   static ModeType
02842     direction = HorizontalChopCommand;
02843 
02844   static const ModeType
02845     ChopCommands[] =
02846     {
02847       ChopDirectionCommand,
02848       ChopHelpCommand,
02849       ChopDismissCommand
02850     },
02851     DirectionCommands[] =
02852     {
02853       HorizontalChopCommand,
02854       VerticalChopCommand
02855     };
02856 
02857   char
02858     text[MaxTextExtent];
02859 
02860   Image
02861     *chop_image;
02862 
02863   int
02864     id,
02865     x,
02866     y;
02867 
02868   MagickRealType
02869     scale_factor;
02870 
02871   RectangleInfo
02872     chop_info;
02873 
02874   unsigned int
02875     distance,
02876     height,
02877     width;
02878 
02879   unsigned long
02880     state;
02881 
02882   XEvent
02883     event;
02884 
02885   XSegment
02886     segment_info;
02887 
02888   /*
02889     Map Command widget.
02890   */
02891   (void) CloneString(&windows->command.name,"Chop");
02892   windows->command.data=1;
02893   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
02894   (void) XMapRaised(display,windows->command.id);
02895   XClientMessage(display,windows->image.id,windows->im_protocols,
02896     windows->im_update_widget,CurrentTime);
02897   /*
02898     Track pointer until button 1 is pressed.
02899   */
02900   XQueryPosition(display,windows->image.id,&x,&y);
02901   (void) XSelectInput(display,windows->image.id,
02902     windows->image.attributes.event_mask | PointerMotionMask);
02903   state=DefaultState;
02904   do
02905   {
02906     if (windows->info.mapped != MagickFalse)
02907       {
02908         /*
02909           Display pointer position.
02910         */
02911         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
02912           x+windows->image.x,y+windows->image.y);
02913         XInfoWidget(display,windows,text);
02914       }
02915     /*
02916       Wait for next event.
02917     */
02918     XScreenEvent(display,windows,&event);
02919     if (event.xany.window == windows->command.id)
02920       {
02921         /*
02922           Select a command from the Command widget.
02923         */
02924         id=XCommandWidget(display,windows,ChopMenu,&event);
02925         if (id < 0)
02926           continue;
02927         switch (ChopCommands[id])
02928         {
02929           case ChopDirectionCommand:
02930           {
02931             char
02932               command[MaxTextExtent];
02933 
02934             static const char
02935               *Directions[] =
02936               {
02937                 "horizontal",
02938                 "vertical",
02939                 (char *) NULL,
02940               };
02941 
02942             /*
02943               Select a command from the pop-up menu.
02944             */
02945             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
02946             if (id >= 0)
02947               direction=DirectionCommands[id];
02948             break;
02949           }
02950           case ChopHelpCommand:
02951           {
02952             XTextViewWidget(display,resource_info,windows,MagickFalse,
02953               "Help Viewer - Image Chop",ImageChopHelp);
02954             break;
02955           }
02956           case ChopDismissCommand:
02957           {
02958             /*
02959               Prematurely exit.
02960             */
02961             state|=EscapeState;
02962             state|=ExitState;
02963             break;
02964           }
02965           default:
02966             break;
02967         }
02968         continue;
02969       }
02970     switch (event.type)
02971     {
02972       case ButtonPress:
02973       {
02974         if (event.xbutton.button != Button1)
02975           break;
02976         if (event.xbutton.window != windows->image.id)
02977           break;
02978         /*
02979           User has committed to start point of chopping line.
02980         */
02981         segment_info.x1=(short int) event.xbutton.x;
02982         segment_info.x2=(short int) event.xbutton.x;
02983         segment_info.y1=(short int) event.xbutton.y;
02984         segment_info.y2=(short int) event.xbutton.y;
02985         state|=ExitState;
02986         break;
02987       }
02988       case ButtonRelease:
02989         break;
02990       case Expose:
02991         break;
02992       case KeyPress:
02993       {
02994         char
02995           command[MaxTextExtent];
02996 
02997         KeySym
02998           key_symbol;
02999 
03000         if (event.xkey.window != windows->image.id)
03001           break;
03002         /*
03003           Respond to a user key press.
03004         */
03005         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
03006           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
03007         switch ((int) key_symbol)
03008         {
03009           case XK_Escape:
03010           case XK_F20:
03011           {
03012             /*
03013               Prematurely exit.
03014             */
03015             state|=EscapeState;
03016             state|=ExitState;
03017             break;
03018           }
03019           case XK_F1:
03020           case XK_Help:
03021           {
03022             (void) XSetFunction(display,windows->image.highlight_context,
03023               GXcopy);
03024             XTextViewWidget(display,resource_info,windows,MagickFalse,
03025               "Help Viewer - Image Chop",ImageChopHelp);
03026             (void) XSetFunction(display,windows->image.highlight_context,
03027               GXinvert);
03028             break;
03029           }
03030           default:
03031           {
03032             (void) XBell(display,0);
03033             break;
03034           }
03035         }
03036         break;
03037       }
03038       case MotionNotify:
03039       {
03040         /*
03041           Map and unmap Info widget as text cursor crosses its boundaries.
03042         */
03043         x=event.xmotion.x;
03044         y=event.xmotion.y;
03045         if (windows->info.mapped != MagickFalse)
03046           {
03047             if ((x < (int) (windows->info.x+windows->info.width)) &&
03048                 (y < (int) (windows->info.y+windows->info.height)))
03049               (void) XWithdrawWindow(display,windows->info.id,
03050                 windows->info.screen);
03051           }
03052         else
03053           if ((x > (int) (windows->info.x+windows->info.width)) ||
03054               (y > (int) (windows->info.y+windows->info.height)))
03055             (void) XMapWindow(display,windows->info.id);
03056       }
03057     }
03058   } while ((state & ExitState) == 0);
03059   (void) XSelectInput(display,windows->image.id,
03060     windows->image.attributes.event_mask);
03061   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
03062   if ((state & EscapeState) != 0)
03063     return(MagickTrue);
03064   /*
03065     Draw line as pointer moves until the mouse button is released.
03066   */
03067   chop_info.width=0;
03068   chop_info.height=0;
03069   chop_info.x=0;
03070   chop_info.y=0;
03071   distance=0;
03072   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
03073   state=DefaultState;
03074   do
03075   {
03076     if (distance > 9)
03077       {
03078         /*
03079           Display info and draw chopping line.
03080         */
03081         if (windows->info.mapped == MagickFalse)
03082           (void) XMapWindow(display,windows->info.id);
03083         (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
03084           chop_info.width,chop_info.height,chop_info.x,chop_info.y);
03085         XInfoWidget(display,windows,text);
03086         XHighlightLine(display,windows->image.id,
03087           windows->image.highlight_context,&segment_info);
03088       }
03089     else
03090       if (windows->info.mapped != MagickFalse)
03091         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
03092     /*
03093       Wait for next event.
03094     */
03095     XScreenEvent(display,windows,&event);
03096     if (distance > 9)
03097       XHighlightLine(display,windows->image.id,
03098         windows->image.highlight_context,&segment_info);
03099     switch (event.type)
03100     {
03101       case ButtonPress:
03102       {
03103         segment_info.x2=(short int) event.xmotion.x;
03104         segment_info.y2=(short int) event.xmotion.y;
03105         break;
03106       }
03107       case ButtonRelease:
03108       {
03109         /*
03110           User has committed to chopping line.
03111         */
03112         segment_info.x2=(short int) event.xbutton.x;
03113         segment_info.y2=(short int) event.xbutton.y;
03114         state|=ExitState;
03115         break;
03116       }
03117       case Expose:
03118         break;
03119       case MotionNotify:
03120       {
03121         segment_info.x2=(short int) event.xmotion.x;
03122         segment_info.y2=(short int) event.xmotion.y;
03123       }
03124       default:
03125         break;
03126     }
03127     /*
03128       Check boundary conditions.
03129     */
03130     if (segment_info.x2 < 0)
03131       segment_info.x2=0;
03132     else
03133       if (segment_info.x2 > windows->image.ximage->width)
03134         segment_info.x2=windows->image.ximage->width;
03135     if (segment_info.y2 < 0)
03136       segment_info.y2=0;
03137     else
03138       if (segment_info.y2 > windows->image.ximage->height)
03139         segment_info.y2=windows->image.ximage->height;
03140     distance=(unsigned int)
03141       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
03142        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
03143     /*
03144       Compute chopping geometry.
03145     */
03146     if (direction == HorizontalChopCommand)
03147       {
03148         chop_info.width=(unsigned long) (segment_info.x2-segment_info.x1+1);
03149         chop_info.x=windows->image.x+segment_info.x1;
03150         chop_info.height=0;
03151         chop_info.y=0;
03152         if (segment_info.x1 > (int) segment_info.x2)
03153           {
03154             chop_info.width=(unsigned long) (segment_info.x1-segment_info.x2+1);
03155             chop_info.x=windows->image.x+segment_info.x2;
03156           }
03157       }
03158     else
03159       {
03160         chop_info.width=0;
03161         chop_info.height=(unsigned long) (segment_info.y2-segment_info.y1+1);
03162         chop_info.x=0;
03163         chop_info.y=windows->image.y+segment_info.y1;
03164         if (segment_info.y1 > segment_info.y2)
03165           {
03166             chop_info.height=(unsigned long)
03167               (segment_info.y1-segment_info.y2+1);
03168             chop_info.y=windows->image.y+segment_info.y2;
03169           }
03170       }
03171   } while ((state & ExitState) == 0);
03172   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
03173   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
03174   if (distance <= 9)
03175     return(MagickTrue);
03176   /*
03177     Image chopping is relative to image configuration.
03178   */
03179   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
03180   XSetCursorState(display,windows,MagickTrue);
03181   XCheckRefreshWindows(display,windows);
03182   windows->image.window_changes.width=windows->image.ximage->width-
03183     (unsigned int) chop_info.width;
03184   windows->image.window_changes.height=windows->image.ximage->height-
03185     (unsigned int) chop_info.height;
03186   width=(unsigned int) (*image)->columns;
03187   height=(unsigned int) (*image)->rows;
03188   x=0;
03189   y=0;
03190   if (windows->image.crop_geometry != (char *) NULL)
03191     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
03192   scale_factor=(MagickRealType) width/windows->image.ximage->width;
03193   chop_info.x+=x;
03194   chop_info.x=(int) (scale_factor*chop_info.x+0.5);
03195   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
03196   scale_factor=(MagickRealType) height/windows->image.ximage->height;
03197   chop_info.y+=y;
03198   chop_info.y=(int) (scale_factor*chop_info.y+0.5);
03199   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
03200   /*
03201     Chop image.
03202   */
03203   chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
03204   XSetCursorState(display,windows,MagickFalse);
03205   if (chop_image == (Image *) NULL)
03206     return(MagickFalse);
03207   *image=DestroyImage(*image);
03208   *image=chop_image;
03209   /*
03210     Update image configuration.
03211   */
03212   XConfigureImageColormap(display,resource_info,windows,*image);
03213   (void) XConfigureImage(display,resource_info,windows,*image);
03214   return(MagickTrue);
03215 }
03216 
03217 /*
03218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03219 %                                                                             %
03220 %                                                                             %
03221 %                                                                             %
03222 +   X C o l o r E d i t I m a g e                                             %
03223 %                                                                             %
03224 %                                                                             %
03225 %                                                                             %
03226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03227 %
03228 %  XColorEditImage() allows the user to interactively change the color of one
03229 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
03230 %
03231 %  The format of the XColorEditImage method is:
03232 %
03233 %      MagickBooleanType XColorEditImage(Display *display,
03234 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
03235 %
03236 %  A description of each parameter follows:
03237 %
03238 %    o display: Specifies a connection to an X server;  returned from
03239 %      XOpenDisplay.
03240 %
03241 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
03242 %
03243 %    o windows: Specifies a pointer to a XWindows structure.
03244 %
03245 %    o image: the image; returned from
03246 %      ReadImage.
03247 %
03248 */
03249 
03250 
03251 static MagickBooleanType XColorEditImage(Display *display,
03252   XResourceInfo *resource_info,XWindows *windows,Image **image)
03253 {
03254   static const char
03255     *ColorEditMenu[] =
03256     {
03257       "Method",
03258       "Pixel Color",
03259       "Border Color",
03260       "Fuzz",
03261       "Undo",
03262       "Help",
03263       "Dismiss",
03264       (char *) NULL
03265     };
03266 
03267   static const ModeType
03268     ColorEditCommands[] =
03269     {
03270       ColorEditMethodCommand,
03271       ColorEditColorCommand,
03272       ColorEditBorderCommand,
03273       ColorEditFuzzCommand,
03274       ColorEditUndoCommand,
03275       ColorEditHelpCommand,
03276       ColorEditDismissCommand
03277     };
03278 
03279   static PaintMethod
03280     method = PointMethod;
03281 
03282   static unsigned int
03283     pen_id = 0;
03284 
03285   static XColor
03286     border_color = { 0, 0, 0, 0, 0, 0 };
03287 
03288   char
03289     command[MaxTextExtent],
03290     text[MaxTextExtent];
03291 
03292   Cursor
03293     cursor;
03294 
03295   ExceptionInfo
03296     *exception;
03297 
03298   int
03299     entry,
03300     id,
03301     x,
03302     x_offset,
03303     y,
03304     y_offset;
03305 
03306   register PixelPacket
03307     *q;
03308 
03309   register long
03310     i;
03311 
03312   unsigned int
03313     height,
03314     width;
03315 
03316   unsigned long
03317     state;
03318 
03319   XColor
03320     color;
03321 
03322   XEvent
03323     event;
03324 
03325   /*
03326     Map Command widget.
03327   */
03328   (void) CloneString(&windows->command.name,"Color Edit");
03329   windows->command.data=4;
03330   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
03331   (void) XMapRaised(display,windows->command.id);
03332   XClientMessage(display,windows->image.id,windows->im_protocols,
03333     windows->im_update_widget,CurrentTime);
03334   /*
03335     Make cursor.
03336   */
03337   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
03338     resource_info->background_color,resource_info->foreground_color);
03339   (void) XCheckDefineCursor(display,windows->image.id,cursor);
03340   /*
03341     Track pointer until button 1 is pressed.
03342   */
03343   XQueryPosition(display,windows->image.id,&x,&y);
03344   (void) XSelectInput(display,windows->image.id,
03345     windows->image.attributes.event_mask | PointerMotionMask);
03346   state=DefaultState;
03347   do
03348   {
03349     if (windows->info.mapped != MagickFalse)
03350       {
03351         /*
03352           Display pointer position.
03353         */
03354         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
03355           x+windows->image.x,y+windows->image.y);
03356         XInfoWidget(display,windows,text);
03357       }
03358     /*
03359       Wait for next event.
03360     */
03361     XScreenEvent(display,windows,&event);
03362     if (event.xany.window == windows->command.id)
03363       {
03364         /*
03365           Select a command from the Command widget.
03366         */
03367         id=XCommandWidget(display,windows,ColorEditMenu,&event);
03368         if (id < 0)
03369           {
03370             (void) XCheckDefineCursor(display,windows->image.id,cursor);
03371             continue;
03372           }
03373         switch (ColorEditCommands[id])
03374         {
03375           case ColorEditMethodCommand:
03376           {
03377             char
03378               **methods;
03379 
03380             /*
03381               Select a method from the pop-up menu.
03382             */
03383             methods=(char **) GetMagickOptions(MagickMethodOptions);
03384             if (methods == (char **) NULL)
03385               break;
03386             entry=XMenuWidget(display,windows,ColorEditMenu[id],
03387               (const char **) methods,command);
03388             if (entry >= 0)
03389               method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
03390                 MagickFalse,methods[entry]);
03391             methods=DestroyStringList(methods);
03392             break;
03393           }
03394           case ColorEditColorCommand:
03395           {
03396             const char
03397               *ColorMenu[MaxNumberPens];
03398 
03399             int
03400               pen_number;
03401 
03402             /*
03403               Initialize menu selections.
03404             */
03405             for (i=0; i < (int) (MaxNumberPens-2); i++)
03406               ColorMenu[i]=resource_info->pen_colors[i];
03407             ColorMenu[MaxNumberPens-2]="Browser...";
03408             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
03409             /*
03410               Select a pen color from the pop-up menu.
03411             */
03412             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
03413               (const char **) ColorMenu,command);
03414             if (pen_number < 0)
03415               break;
03416             if (pen_number == (MaxNumberPens-2))
03417               {
03418                 static char
03419                   color_name[MaxTextExtent] = "gray";
03420 
03421                 /*
03422                   Select a pen color from a dialog.
03423                 */
03424                 resource_info->pen_colors[pen_number]=color_name;
03425                 XColorBrowserWidget(display,windows,"Select",color_name);
03426                 if (*color_name == '\0')
03427                   break;
03428               }
03429             /*
03430               Set pen color.
03431             */
03432             (void) XParseColor(display,windows->map_info->colormap,
03433               resource_info->pen_colors[pen_number],&color);
03434             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
03435               (unsigned int) MaxColors,&color);
03436             windows->pixel_info->pen_colors[pen_number]=color;
03437             pen_id=(unsigned int) pen_number;
03438             break;
03439           }
03440           case ColorEditBorderCommand:
03441           {
03442             const char
03443               *ColorMenu[MaxNumberPens];
03444 
03445             int
03446               pen_number;
03447 
03448             /*
03449               Initialize menu selections.
03450             */
03451             for (i=0; i < (int) (MaxNumberPens-2); i++)
03452               ColorMenu[i]=resource_info->pen_colors[i];
03453             ColorMenu[MaxNumberPens-2]="Browser...";
03454             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
03455             /*
03456               Select a pen color from the pop-up menu.
03457             */
03458             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
03459               (const char **) ColorMenu,command);
03460             if (pen_number < 0)
03461               break;
03462             if (pen_number == (MaxNumberPens-2))
03463               {
03464                 static char
03465                   color_name[MaxTextExtent] = "gray";
03466 
03467                 /*
03468                   Select a pen color from a dialog.
03469                 */
03470                 resource_info->pen_colors[pen_number]=color_name;
03471                 XColorBrowserWidget(display,windows,"Select",color_name);
03472                 if (*color_name == '\0')
03473                   break;
03474               }
03475             /*
03476               Set border color.
03477             */
03478             (void) XParseColor(display,windows->map_info->colormap,
03479               resource_info->pen_colors[pen_number],&border_color);
03480             break;
03481           }
03482           case ColorEditFuzzCommand:
03483           {
03484             static char
03485               fuzz[MaxTextExtent];
03486 
03487             static const char
03488               *FuzzMenu[] =
03489               {
03490                 "0%",
03491                 "2%",
03492                 "5%",
03493                 "10%",
03494                 "15%",
03495                 "Dialog...",
03496                 (char *) NULL,
03497               };
03498 
03499             /*
03500               Select a command from the pop-up menu.
03501             */
03502             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
03503               command);
03504             if (entry < 0)
03505               break;
03506             if (entry != 5)
03507               {
03508                 (*image)->fuzz=StringToDouble(FuzzMenu[entry],1.0*QuantumRange+
03509                   1.0);
03510                 break;
03511               }
03512             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
03513             (void) XDialogWidget(display,windows,"Ok",
03514               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
03515             if (*fuzz == '\0')
03516               break;
03517             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
03518             (*image)->fuzz=StringToDouble(fuzz,1.0*QuantumRange+1.0);
03519             break;
03520           }
03521           case ColorEditUndoCommand:
03522           {
03523             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
03524               image);
03525             break;
03526           }
03527           case ColorEditHelpCommand:
03528           default:
03529           {
03530             XTextViewWidget(display,resource_info,windows,MagickFalse,
03531               "Help Viewer - Image Annotation",ImageColorEditHelp);
03532             break;
03533           }
03534           case ColorEditDismissCommand:
03535           {
03536             /*
03537               Prematurely exit.
03538             */
03539             state|=EscapeState;
03540             state|=ExitState;
03541             break;
03542           }
03543         }
03544         (void) XCheckDefineCursor(display,windows->image.id,cursor);
03545         continue;
03546       }
03547     switch (event.type)
03548     {
03549       case ButtonPress:
03550       {
03551         if (event.xbutton.button != Button1)
03552           break;
03553         if ((event.xbutton.window != windows->image.id) &&
03554             (event.xbutton.window != windows->magnify.id))
03555           break;
03556         /*
03557           exit loop.
03558         */
03559         x=event.xbutton.x;
03560         y=event.xbutton.y;
03561         (void) XMagickCommand(display,resource_info,windows,
03562           SaveToUndoBufferCommand,image);
03563         state|=UpdateConfigurationState;
03564         break;
03565       }
03566       case ButtonRelease:
03567       {
03568         if (event.xbutton.button != Button1)
03569           break;
03570         if ((event.xbutton.window != windows->image.id) &&
03571             (event.xbutton.window != windows->magnify.id))
03572           break;
03573         /*
03574           Update colormap information.
03575         */
03576         x=event.xbutton.x;
03577         y=event.xbutton.y;
03578         XConfigureImageColormap(display,resource_info,windows,*image);
03579         (void) XConfigureImage(display,resource_info,windows,*image);
03580         XInfoWidget(display,windows,text);
03581         (void) XCheckDefineCursor(display,windows->image.id,cursor);
03582         state&=(~UpdateConfigurationState);
03583         break;
03584       }
03585       case Expose:
03586         break;
03587       case KeyPress:
03588       {
03589         KeySym
03590           key_symbol;
03591 
03592         if (event.xkey.window == windows->magnify.id)
03593           {
03594             Window
03595               window;
03596 
03597             window=windows->magnify.id;
03598             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
03599           }
03600         if (event.xkey.window != windows->image.id)
03601           break;
03602         /*
03603           Respond to a user key press.
03604         */
03605         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
03606           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
03607         switch ((int) key_symbol)
03608         {
03609           case XK_Escape:
03610           case XK_F20:
03611           {
03612             /*
03613               Prematurely exit.
03614             */
03615             state|=ExitState;
03616             break;
03617           }
03618           case XK_F1:
03619           case XK_Help:
03620           {
03621             XTextViewWidget(display,resource_info,windows,MagickFalse,
03622               "Help Viewer - Image Annotation",ImageColorEditHelp);
03623             break;
03624           }
03625           default:
03626           {
03627             (void) XBell(display,0);
03628             break;
03629           }
03630         }
03631         break;
03632       }
03633       case MotionNotify:
03634       {
03635         /*
03636           Map and unmap Info widget as cursor crosses its boundaries.
03637         */
03638         x=event.xmotion.x;
03639         y=event.xmotion.y;
03640         if (windows->info.mapped != MagickFalse)
03641           {
03642             if ((x < (int) (windows->info.x+windows->info.width)) &&
03643                 (y < (int) (windows->info.y+windows->info.height)))
03644               (void) XWithdrawWindow(display,windows->info.id,
03645                 windows->info.screen);
03646           }
03647         else
03648           if ((x > (int) (windows->info.x+windows->info.width)) ||
03649               (y > (int) (windows->info.y+windows->info.height)))
03650             (void) XMapWindow(display,windows->info.id);
03651         break;
03652       }
03653       default:
03654         break;
03655     }
03656     if (event.xany.window == windows->magnify.id)
03657       {
03658         x=windows->magnify.x-windows->image.x;
03659         y=windows->magnify.y-windows->image.y;
03660       }
03661     x_offset=x;
03662     y_offset=y;
03663     if ((state & UpdateConfigurationState) != 0)
03664       {
03665         int
03666           x,
03667           y;
03668 
03669         /*
03670           Pixel edit is relative to image configuration.
03671         */
03672         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
03673           MagickTrue);
03674         color=windows->pixel_info->pen_colors[pen_id];
03675         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
03676         width=(unsigned int) (*image)->columns;
03677         height=(unsigned int) (*image)->rows;
03678         x=0;
03679         y=0;
03680         if (windows->image.crop_geometry != (char *) NULL)
03681           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
03682             &width,&height);
03683         x_offset=(int)
03684           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
03685         y_offset=(int)
03686           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
03687         if ((x_offset < 0) || (y_offset < 0))
03688           continue;
03689         if ((x_offset >= (long) (*image)->columns) ||
03690             (y_offset >= (long) (*image)->rows))
03691           continue;
03692         exception=(&(*image)->exception);
03693         switch (method)
03694         {
03695           case PointMethod:
03696           default:
03697           {
03698             /*
03699               Update color information using point algorithm.
03700             */
03701             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
03702               return(MagickFalse);
03703             q=GetAuthenticPixels(*image,x_offset,y_offset,1,1,exception);
03704             if (q == (PixelPacket *) NULL)
03705               break;
03706             q->red=ScaleShortToQuantum(color.red);
03707             q->green=ScaleShortToQuantum(color.green);
03708             q->blue=ScaleShortToQuantum(color.blue);
03709             (void) SyncAuthenticPixels(*image,&(*image)->exception);
03710             break;
03711           }
03712           case ReplaceMethod:
03713           {
03714             PixelPacket
03715               target;
03716 
03717             /*
03718               Update color information using replace algorithm.
03719             */
03720             (void) GetOneVirtualPixel(*image,x_offset,y_offset,&target,
03721               &(*image)->exception);
03722             if ((*image)->storage_class == DirectClass)
03723               {
03724                 for (y=0; y < (long) (*image)->rows; y++)
03725                 {
03726                   q=GetAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
03727                   if (q == (PixelPacket *) NULL)
03728                     break;
03729                   for (x=0; x < (int) (*image)->columns; x++)
03730                   {
03731                     if (IsColorSimilar(*image,q,&target))
03732                       {
03733                         q->red=ScaleShortToQuantum(color.red);
03734                         q->green=ScaleShortToQuantum(color.green);
03735                         q->blue=ScaleShortToQuantum(color.blue);
03736                       }
03737                     q++;
03738                   }
03739                   if (SyncAuthenticPixels(*image,exception) == MagickFalse)
03740                     break;
03741                 }
03742               }
03743             else
03744               {
03745                 for (i=0; i < (int) (*image)->colors; i++)
03746                   if (IsColorSimilar(*image,(*image)->colormap+i,&target))
03747                     {
03748                       (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
03749                       (*image)->colormap[i].green=ScaleShortToQuantum(
03750                         color.green);
03751                       (*image)->colormap[i].blue=ScaleShortToQuantum(
03752                         color.blue);
03753                     }
03754                 (void) SyncImage(*image);
03755               }
03756             break;
03757           }
03758           case FloodfillMethod:
03759           case FillToBorderMethod:
03760           {
03761             DrawInfo
03762               *draw_info;
03763 
03764             MagickPixelPacket
03765               target;
03766 
03767             /*
03768               Update color information using floodfill algorithm.
03769             */
03770             (void) GetOneVirtualMagickPixel(*image,x_offset,y_offset,&target,
03771               exception);
03772             if (method == FillToBorderMethod)
03773               {
03774                 target.red=(MagickRealType)
03775                   ScaleShortToQuantum(border_color.red);
03776                 target.green=(MagickRealType)
03777                   ScaleShortToQuantum(border_color.green);
03778                 target.blue=(MagickRealType)
03779                   ScaleShortToQuantum(border_color.blue);
03780               }
03781             draw_info=CloneDrawInfo(resource_info->image_info,
03782               (DrawInfo *) NULL);
03783             (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
03784               &draw_info->fill,exception);
03785             (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
03786               x_offset,y_offset,method == FloodfillMethod ? MagickFalse :
03787               MagickTrue);
03788             draw_info=DestroyDrawInfo(draw_info);
03789             break;
03790           }
03791           case ResetMethod:
03792           {
03793             /*
03794               Update color information using reset algorithm.
03795             */
03796             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
03797               return(MagickFalse);
03798             for (y=0; y < (long) (*image)->rows; y++)
03799             {
03800               q=QueueAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
03801               if (q == (PixelPacket *) NULL)
03802                 break;
03803               for (x=0; x < (int) (*image)->columns; x++)
03804               {
03805                 q->red=ScaleShortToQuantum(color.red);
03806                 q->green=ScaleShortToQuantum(color.green);
03807                 q->blue=ScaleShortToQuantum(color.blue);
03808                 q++;
03809               }
03810               if (SyncAuthenticPixels(*image,exception) == MagickFalse)
03811                 break;
03812             }
03813             break;
03814           }
03815         }
03816         state&=(~UpdateConfigurationState);
03817       }
03818   } while ((state & ExitState) == 0);
03819   (void) XSelectInput(display,windows->image.id,
03820     windows->image.attributes.event_mask);
03821   XSetCursorState(display,windows,MagickFalse);
03822   (void) XFreeCursor(display,cursor);
03823   return(MagickTrue);
03824 }
03825 
03826 /*
03827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03828 %                                                                             %
03829 %                                                                             %
03830 %                                                                             %
03831 +   X C o m p o s i t e I m a g e                                             %
03832 %                                                                             %
03833 %                                                                             %
03834 %                                                                             %
03835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03836 %
03837 %  XCompositeImage() requests an image name from the user, reads the image and
03838 %  composites it with the X window image at a location the user chooses with
03839 %  the pointer.
03840 %
03841 %  The format of the XCompositeImage method is:
03842 %
03843 %      MagickBooleanType XCompositeImage(Display *display,
03844 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
03845 %
03846 %  A description of each parameter follows:
03847 %
03848 %    o display: Specifies a connection to an X server;  returned from
03849 %      XOpenDisplay.
03850 %
03851 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
03852 %
03853 %    o windows: Specifies a pointer to a XWindows structure.
03854 %
03855 %    o image: the image; returned from
03856 %      ReadImage.
03857 %
03858 */
03859 static MagickBooleanType XCompositeImage(Display *display,
03860   XResourceInfo *resource_info,XWindows *windows,Image *image)
03861 {
03862   static char
03863     displacement_geometry[MaxTextExtent] = "30x30",
03864     filename[MaxTextExtent] = "\0";
03865 
03866   static const char
03867     *CompositeMenu[] =
03868     {
03869       "Operators",
03870       "Dissolve",
03871       "Displace",
03872       "Help",
03873       "Dismiss",
03874       (char *) NULL
03875     };
03876 
03877   static CompositeOperator
03878     compose = CopyCompositeOp;
03879 
03880   static const ModeType
03881     CompositeCommands[] =
03882     {
03883       CompositeOperatorsCommand,
03884       CompositeDissolveCommand,
03885       CompositeDisplaceCommand,
03886       CompositeHelpCommand,
03887       CompositeDismissCommand
03888     };
03889 
03890   char
03891     text[MaxTextExtent];
03892 
03893   Cursor
03894     cursor;
03895 
03896   Image
03897     *composite_image;
03898 
03899   int
03900     entry,
03901     id,
03902     x,
03903     y;
03904 
03905   MagickRealType
03906     blend,
03907     scale_factor;
03908 
03909   RectangleInfo
03910     highlight_info,
03911     composite_info;
03912 
03913   unsigned int
03914     height,
03915     width;
03916 
03917   unsigned long
03918     state;
03919 
03920   XEvent
03921     event;
03922 
03923   /*
03924     Request image file name from user.
03925   */
03926   XFileBrowserWidget(display,windows,"Composite",filename);
03927   if (*filename == '\0')
03928     return(MagickTrue);
03929   /*
03930     Read image.
03931   */
03932   XSetCursorState(display,windows,MagickTrue);
03933   XCheckRefreshWindows(display,windows);
03934   (void) CopyMagickString(resource_info->image_info->filename,filename,
03935     MaxTextExtent);
03936   composite_image=ReadImage(resource_info->image_info,&image->exception);
03937   CatchException(&image->exception);
03938   XSetCursorState(display,windows,MagickFalse);
03939   if (composite_image == (Image *) NULL)
03940     return(MagickFalse);
03941   /*
03942     Map Command widget.
03943   */
03944   (void) CloneString(&windows->command.name,"Composite");
03945   windows->command.data=1;
03946   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
03947   (void) XMapRaised(display,windows->command.id);
03948   XClientMessage(display,windows->image.id,windows->im_protocols,
03949     windows->im_update_widget,CurrentTime);
03950   /*
03951     Track pointer until button 1 is pressed.
03952   */
03953   XQueryPosition(display,windows->image.id,&x,&y);
03954   (void) XSelectInput(display,windows->image.id,
03955     windows->image.attributes.event_mask | PointerMotionMask);
03956   composite_info.x=windows->image.x+x;
03957   composite_info.y=windows->image.y+y;
03958   composite_info.width=0;
03959   composite_info.height=0;
03960   cursor=XCreateFontCursor(display,XC_ul_angle);
03961   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
03962   blend=0.0;
03963   state=DefaultState;
03964   do
03965   {
03966     if (windows->info.mapped != MagickFalse)
03967       {
03968         /*
03969           Display pointer position.
03970         */
03971         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
03972           composite_info.x,composite_info.y);
03973         XInfoWidget(display,windows,text);
03974       }
03975     highlight_info=composite_info;
03976     highlight_info.x=composite_info.x-windows->image.x;
03977     highlight_info.y=composite_info.y-windows->image.y;
03978     XHighlightRectangle(display,windows->image.id,
03979       windows->image.highlight_context,&highlight_info);
03980     /*
03981       Wait for next event.
03982     */
03983     XScreenEvent(display,windows,&event);
03984     XHighlightRectangle(display,windows->image.id,
03985       windows->image.highlight_context,&highlight_info);
03986     if (event.xany.window == windows->command.id)
03987       {
03988         /*
03989           Select a command from the Command widget.
03990         */
03991         id=XCommandWidget(display,windows,CompositeMenu,&event);
03992         if (id < 0)
03993           continue;
03994         switch (CompositeCommands[id])
03995         {
03996           case CompositeOperatorsCommand:
03997           {
03998             char
03999               command[MaxTextExtent],
04000               **operators;
04001 
04002             /*
04003               Select a command from the pop-up menu.
04004             */
04005             operators=GetMagickOptions(MagickComposeOptions);
04006             if (operators == (char **) NULL)
04007               break;
04008             entry=XMenuWidget(display,windows,CompositeMenu[id],
04009               (const char **) operators,command);
04010             if (entry >= 0)
04011               compose=(CompositeOperator) ParseMagickOption(
04012                 MagickComposeOptions,MagickFalse,operators[entry]);
04013             operators=DestroyStringList(operators);
04014             break;
04015           }
04016           case CompositeDissolveCommand:
04017           {
04018             static char
04019               factor[MaxTextExtent] = "20.0";
04020 
04021             /*
04022               Dissolve the two images a given percent.
04023             */
04024             (void) XSetFunction(display,windows->image.highlight_context,
04025               GXcopy);
04026             (void) XDialogWidget(display,windows,"Dissolve",
04027               "Enter the blend factor (0.0 - 99.9%):",factor);
04028             (void) XSetFunction(display,windows->image.highlight_context,
04029               GXinvert);
04030             if (*factor == '\0')
04031               break;
04032             blend=atof(factor);
04033             compose=DissolveCompositeOp;
04034             break;
04035           }
04036           case CompositeDisplaceCommand:
04037           {
04038             /*
04039               Get horizontal and vertical scale displacement geometry.
04040             */
04041             (void) XSetFunction(display,windows->image.highlight_context,
04042               GXcopy);
04043             (void) XDialogWidget(display,windows,"Displace",
04044               "Enter the horizontal and vertical scale:",displacement_geometry);
04045             (void) XSetFunction(display,windows->image.highlight_context,
04046               GXinvert);
04047             if (*displacement_geometry == '\0')
04048               break;
04049             compose=DisplaceCompositeOp;
04050             break;
04051           }
04052           case CompositeHelpCommand:
04053           {
04054             (void) XSetFunction(display,windows->image.highlight_context,
04055               GXcopy);
04056             XTextViewWidget(display,resource_info,windows,MagickFalse,
04057               "Help Viewer - Image Composite",ImageCompositeHelp);
04058             (void) XSetFunction(display,windows->image.highlight_context,
04059               GXinvert);
04060             break;
04061           }
04062           case CompositeDismissCommand:
04063           {
04064             /*
04065               Prematurely exit.
04066             */
04067             state|=EscapeState;
04068             state|=ExitState;
04069             break;
04070           }
04071           default:
04072             break;
04073         }
04074         continue;
04075       }
04076     switch (event.type)
04077     {
04078       case ButtonPress:
04079       {
04080         if (image->debug != MagickFalse)
04081           (void) LogMagickEvent(X11Event,GetMagickModule(),
04082             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
04083             event.xbutton.button,event.xbutton.x,event.xbutton.y);
04084         if (event.xbutton.button != Button1)
04085           break;
04086         if (event.xbutton.window != windows->image.id)
04087           break;
04088         /*
04089           Change cursor.
04090         */
04091         composite_info.width=composite_image->columns;
04092         composite_info.height=composite_image->rows;
04093         (void) XCheckDefineCursor(display,windows->image.id,cursor);
04094         composite_info.x=windows->image.x+event.xbutton.x;
04095         composite_info.y=windows->image.y+event.xbutton.y;
04096         break;
04097       }
04098       case ButtonRelease:
04099       {
04100         if (image->debug != MagickFalse)
04101           (void) LogMagickEvent(X11Event,GetMagickModule(),
04102             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
04103             event.xbutton.button,event.xbutton.x,event.xbutton.y);
04104         if (event.xbutton.button != Button1)
04105           break;
04106         if (event.xbutton.window != windows->image.id)
04107           break;
04108         if ((composite_info.width != 0) && (composite_info.height != 0))
04109           {
04110             /*
04111               User has selected the location of the composite image.
04112             */
04113             composite_info.x=windows->image.x+event.xbutton.x;
04114             composite_info.y=windows->image.y+event.xbutton.y;
04115             state|=ExitState;
04116           }
04117         break;
04118       }
04119       case Expose:
04120         break;
04121       case KeyPress:
04122       {
04123         char
04124           command[MaxTextExtent];
04125 
04126         KeySym
04127           key_symbol;
04128 
04129         int
04130           length;
04131 
04132         if (event.xkey.window != windows->image.id)
04133           break;
04134         /*
04135           Respond to a user key press.
04136         */
04137         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
04138           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
04139         *(command+length)='\0';
04140         if (image->debug != MagickFalse)
04141           (void) LogMagickEvent(X11Event,GetMagickModule(),
04142             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
04143         switch ((int) key_symbol)
04144         {
04145           case XK_Escape:
04146           case XK_F20:
04147           {
04148             /*
04149               Prematurely exit.
04150             */
04151             composite_image=DestroyImage(composite_image);
04152             state|=EscapeState;
04153             state|=ExitState;
04154             break;
04155           }
04156           case XK_F1:
04157           case XK_Help:
04158           {
04159             (void) XSetFunction(display,windows->image.highlight_context,
04160               GXcopy);
04161             XTextViewWidget(display,resource_info,windows,MagickFalse,
04162               "Help Viewer - Image Composite",ImageCompositeHelp);
04163             (void) XSetFunction(display,windows->image.highlight_context,
04164               GXinvert);
04165             break;
04166           }
04167           default:
04168           {
04169             (void) XBell(display,0);
04170             break;
04171           }
04172         }
04173         break;
04174       }
04175       case MotionNotify:
04176       {
04177         /*
04178           Map and unmap Info widget as text cursor crosses its boundaries.
04179         */
04180         x=event.xmotion.x;
04181         y=event.xmotion.y;
04182         if (windows->info.mapped != MagickFalse)
04183           {
04184             if ((x < (int) (windows->info.x+windows->info.width)) &&
04185                 (y < (int) (windows->info.y+windows->info.height)))
04186               (void) XWithdrawWindow(display,windows->info.id,
04187                 windows->info.screen);
04188           }
04189         else
04190           if ((x > (int) (windows->info.x+windows->info.width)) ||
04191               (y > (int) (windows->info.y+windows->info.height)))
04192             (void) XMapWindow(display,windows->info.id);
04193         composite_info.x=windows->image.x+x;
04194         composite_info.y=windows->image.y+y;
04195         break;
04196       }
04197       default:
04198       {
04199         if (image->debug != MagickFalse)
04200           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
04201             event.type);
04202         break;
04203       }
04204     }
04205   } while ((state & ExitState) == 0);
04206   (void) XSelectInput(display,windows->image.id,
04207     windows->image.attributes.event_mask);
04208   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
04209   XSetCursorState(display,windows,MagickFalse);
04210   (void) XFreeCursor(display,cursor);
04211   if ((state & EscapeState) != 0)
04212     return(MagickTrue);
04213   /*
04214     Image compositing is relative to image configuration.
04215   */
04216   XSetCursorState(display,windows,MagickTrue);
04217   XCheckRefreshWindows(display,windows);
04218   width=(unsigned int) image->columns;
04219   height=(unsigned int) image->rows;
04220   x=0;
04221   y=0;
04222   if (windows->image.crop_geometry != (char *) NULL)
04223     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
04224   scale_factor=(MagickRealType) width/windows->image.ximage->width;
04225   composite_info.x+=x;
04226   composite_info.x=(int) (scale_factor*composite_info.x+0.5);
04227   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
04228   scale_factor=(MagickRealType) height/windows->image.ximage->height;
04229   composite_info.y+=y;
04230   composite_info.y=(int) (scale_factor*composite_info.y+0.5);
04231   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
04232   if ((composite_info.width != composite_image->columns) ||
04233       (composite_info.height != composite_image->rows))
04234     {
04235       Image
04236         *resize_image;
04237 
04238       /*
04239         Scale composite image.
04240       */
04241       resize_image=ZoomImage(composite_image,composite_info.width,
04242         composite_info.height,&image->exception);
04243       composite_image=DestroyImage(composite_image);
04244       if (resize_image == (Image *) NULL)
04245         {
04246           XSetCursorState(display,windows,MagickFalse);
04247           return(MagickFalse);
04248         }
04249       composite_image=resize_image;
04250     }
04251   if (compose == DisplaceCompositeOp)
04252     (void) SetImageArtifact(composite_image,"compose:args",
04253       displacement_geometry);
04254   if (blend != 0.0)
04255     {
04256       ExceptionInfo
04257         *exception;
04258 
04259       int
04260         y;
04261 
04262       Quantum
04263         opacity;
04264 
04265       register int
04266         x;
04267 
04268       register PixelPacket
04269         *q;
04270 
04271       /*
04272         Create mattes for blending.
04273       */
04274       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
04275       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
04276         ((long) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
04277       if (SetImageStorageClass(image,DirectClass) == MagickFalse)
04278         return(MagickFalse);
04279       image->matte=MagickTrue;
04280       exception=(&image->exception);
04281       for (y=0; y < (long) image->rows; y++)
04282       {
04283         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
04284         if (q == (PixelPacket *) NULL)
04285           break;
04286         for (x=0; x < (int) image->columns; x++)
04287         {
04288           q->opacity=opacity;
04289           q++;
04290         }
04291         if (SyncAuthenticPixels(image,exception) == MagickFalse)
04292           break;
04293       }
04294     }
04295   /*
04296     Composite image with X Image window.
04297   */
04298   (void) CompositeImage(image,compose,composite_image,composite_info.x,
04299     composite_info.y);
04300   composite_image=DestroyImage(composite_image);
04301   XSetCursorState(display,windows,MagickFalse);
04302   /*
04303     Update image configuration.
04304   */
04305   XConfigureImageColormap(display,resource_info,windows,image);
04306   (void) XConfigureImage(display,resource_info,windows,image);
04307   return(MagickTrue);
04308 }
04309 
04310 /*
04311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04312 %                                                                             %
04313 %                                                                             %
04314 %                                                                             %
04315 +   X C o n f i g u r e I m a g e                                             %
04316 %                                                                             %
04317 %                                                                             %
04318 %                                                                             %
04319 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04320 %
04321 %  XConfigureImage() creates a new X image.  It also notifies the window
04322 %  manager of the new image size and configures the transient widows.
04323 %
04324 %  The format of the XConfigureImage method is:
04325 %
04326 %      MagickBooleanType XConfigureImage(Display *display,
04327 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
04328 %
04329 %  A description of each parameter follows:
04330 %
04331 %    o display: Specifies a connection to an X server; returned from
04332 %      XOpenDisplay.
04333 %
04334 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
04335 %
04336 %    o windows: Specifies a pointer to a XWindows structure.
04337 %
04338 %    o image: the image;  returned from
04339 %      ReadImage.
04340 %
04341 %
04342 */
04343 static MagickBooleanType XConfigureImage(Display *display,
04344   XResourceInfo *resource_info,XWindows *windows,Image *image)
04345 {
04346   char
04347     geometry[MaxTextExtent];
04348 
04349   long
04350     x,
04351     y;
04352 
04353   MagickStatusType
04354     status;
04355 
04356   unsigned long
04357     mask,
04358     height,
04359     width;
04360 
04361   XSizeHints
04362     *size_hints;
04363 
04364   XWindowChanges
04365     window_changes;
04366 
04367   /*
04368     Dismiss if window dimensions are zero.
04369   */
04370   width=(unsigned int) windows->image.window_changes.width;
04371   height=(unsigned int) windows->image.window_changes.height;
04372   if (image->debug != MagickFalse)
04373     (void) LogMagickEvent(X11Event,GetMagickModule(),
04374       "Configure Image: %dx%d=>%lux%lu",windows->image.ximage->width,
04375       windows->image.ximage->height,width,height);
04376   if ((width*height) == 0)
04377     return(MagickTrue);
04378   x=0;
04379   y=0;
04380   /*
04381     Resize image to fit Image window dimensions.
04382   */
04383   XSetCursorState(display,windows,MagickTrue);
04384   (void) XFlush(display);
04385   if (((int) width != windows->image.ximage->width) ||
04386       ((int) height != windows->image.ximage->height))
04387     image->taint=MagickTrue;
04388   windows->magnify.x=(int)
04389     width*windows->magnify.x/windows->image.ximage->width;
04390   windows->magnify.y=(int)
04391     height*windows->magnify.y/windows->image.ximage->height;
04392   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
04393   windows->image.y=(int)
04394     (height*windows->image.y/windows->image.ximage->height);
04395   status=XMakeImage(display,resource_info,&windows->image,image,
04396     (unsigned int) width,(unsigned int) height);
04397   if (status == MagickFalse)
04398     XNoticeWidget(display,windows,"Unable to configure X image:",
04399       windows->image.name);
04400   /*
04401     Notify window manager of the new configuration.
04402   */
04403   if (resource_info->image_geometry != (char *) NULL)
04404     (void) FormatMagickString(geometry,MaxTextExtent,"%s>!",
04405       resource_info->image_geometry);
04406   else
04407     (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
04408       XDisplayWidth(display,windows->image.screen),
04409       XDisplayHeight(display,windows->image.screen));
04410   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
04411   window_changes.width=(int) width;
04412   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
04413     window_changes.width=XDisplayWidth(display,windows->image.screen);
04414   window_changes.height=(int) height;
04415   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
04416     window_changes.height=XDisplayHeight(display,windows->image.screen);
04417   mask=(unsigned long) (CWWidth | CWHeight);
04418   if (resource_info->backdrop)
04419     {
04420       mask|=CWX | CWY;
04421       window_changes.x=(int)
04422         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
04423       window_changes.y=(int)
04424         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
04425     }
04426   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
04427     (unsigned int) mask,&window_changes);
04428   (void) XClearWindow(display,windows->image.id);
04429   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
04430   /*
04431     Update Magnify window configuration.
04432   */
04433   if (windows->magnify.mapped != MagickFalse)
04434     XMakeMagnifyImage(display,windows);
04435   windows->pan.crop_geometry=windows->image.crop_geometry;
04436   XBestIconSize(display,&windows->pan,image);
04437   while (((windows->pan.width << 1) < MaxIconSize) &&
04438          ((windows->pan.height << 1) < MaxIconSize))
04439   {
04440     windows->pan.width<<=1;
04441     windows->pan.height<<=1;
04442   }
04443   if (windows->pan.geometry != (char *) NULL)
04444     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
04445       &windows->pan.width,&windows->pan.height);
04446   window_changes.width=(int) windows->pan.width;
04447   window_changes.height=(int) windows->pan.height;
04448   size_hints=XAllocSizeHints();
04449   if (size_hints != (XSizeHints *) NULL)
04450     {
04451       /*
04452         Set new size hints.
04453       */
04454       size_hints->flags=PSize | PMinSize | PMaxSize;
04455       size_hints->width=window_changes.width;
04456       size_hints->height=window_changes.height;
04457       size_hints->min_width=size_hints->width;
04458       size_hints->min_height=size_hints->height;
04459       size_hints->max_width=size_hints->width;
04460       size_hints->max_height=size_hints->height;
04461       (void) XSetNormalHints(display,windows->pan.id,size_hints);
04462       (void) XFree((void *) size_hints);
04463     }
04464   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
04465     (unsigned int) (CWWidth | CWHeight),&window_changes);
04466   /*
04467     Update icon window configuration.
04468   */
04469   windows->icon.crop_geometry=windows->image.crop_geometry;
04470   XBestIconSize(display,&windows->icon,image);
04471   window_changes.width=(int) windows->icon.width;
04472   window_changes.height=(int) windows->icon.height;
04473   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
04474     (unsigned int) (CWWidth | CWHeight),&window_changes);
04475   XSetCursorState(display,windows,MagickFalse);
04476   return(status != 0 ? MagickTrue : MagickFalse);
04477 }
04478 
04479 /*
04480 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04481 %                                                                             %
04482 %                                                                             %
04483 %                                                                             %
04484 +   X C r o p I m a g e                                                       %
04485 %                                                                             %
04486 %                                                                             %
04487 %                                                                             %
04488 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04489 %
04490 %  XCropImage() allows the user to select a region of the image and crop, copy,
04491 %  or cut it.  For copy or cut, the image can subsequently be composited onto
04492 %  the image with XPasteImage.
04493 %
04494 %  The format of the XCropImage method is:
04495 %
04496 %      MagickBooleanType XCropImage(Display *display,
04497 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
04498 %        const ClipboardMode mode)
04499 %
04500 %  A description of each parameter follows:
04501 %
04502 %    o display: Specifies a connection to an X server; returned from
04503 %      XOpenDisplay.
04504 %
04505 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
04506 %
04507 %    o windows: Specifies a pointer to a XWindows structure.
04508 %
04509 %    o image: the image; returned from
04510 %      ReadImage.
04511 %
04512 %    o mode: This unsigned value specified whether the image should be
04513 %      cropped, copied, or cut.
04514 %
04515 */
04516 static MagickBooleanType XCropImage(Display *display,
04517   XResourceInfo *resource_info,XWindows *windows,Image *image,
04518   const ClipboardMode mode)
04519 {
04520   static const char
04521     *CropModeMenu[] =
04522     {
04523       "Help",
04524       "Dismiss",
04525       (char *) NULL
04526     },
04527     *RectifyModeMenu[] =
04528     {
04529       "Crop",
04530       "Help",
04531       "Dismiss",
04532       (char *) NULL
04533     };
04534 
04535   static const ModeType
04536     CropCommands[] =
04537     {
04538       CropHelpCommand,
04539       CropDismissCommand
04540     },
04541     RectifyCommands[] =
04542     {
04543       RectifyCopyCommand,
04544       RectifyHelpCommand,
04545       RectifyDismissCommand
04546     };
04547 
04548   char
04549     command[MaxTextExtent],
04550     text[MaxTextExtent];
04551 
04552   Cursor
04553     cursor;
04554 
04555   ExceptionInfo
04556     *exception;
04557 
04558   int
04559     id,
04560     x,
04561     y;
04562 
04563   KeySym
04564     key_symbol;
04565 
04566   Image
04567     *crop_image;
04568 
04569   MagickRealType
04570     scale_factor;
04571 
04572   RectangleInfo
04573     crop_info,
04574     highlight_info;
04575 
04576   register PixelPacket
04577     *q;
04578 
04579   unsigned int
04580     height,
04581     width;
04582 
04583   unsigned long
04584     state;
04585 
04586   XEvent
04587     event;
04588 
04589   /*
04590     Map Command widget.
04591   */
04592   switch (mode)
04593   {
04594     case CopyMode:
04595     {
04596       (void) CloneString(&windows->command.name,"Copy");
04597       break;
04598     }
04599     case CropMode:
04600     {
04601       (void) CloneString(&windows->command.name,"Crop");
04602       break;
04603     }
04604     case CutMode:
04605     {
04606       (void) CloneString(&windows->command.name,"Cut");
04607       break;
04608     }
04609   }
04610   RectifyModeMenu[0]=windows->command.name;
04611   windows->command.data=0;
04612   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
04613   (void) XMapRaised(display,windows->command.id);
04614   XClientMessage(display,windows->image.id,windows->im_protocols,
04615     windows->im_update_widget,CurrentTime);
04616   /*
04617     Track pointer until button 1 is pressed.
04618   */
04619   XQueryPosition(display,windows->image.id,&x,&y);
04620   (void) XSelectInput(display,windows->image.id,
04621     windows->image.attributes.event_mask | PointerMotionMask);
04622   crop_info.x=windows->image.x+x;
04623   crop_info.y=windows->image.y+y;
04624   crop_info.width=0;
04625   crop_info.height=0;
04626   cursor=XCreateFontCursor(display,XC_fleur);
04627   state=DefaultState;
04628   do
04629   {
04630     if (windows->info.mapped != MagickFalse)
04631       {
04632         /*
04633           Display pointer position.
04634         */
04635         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
04636           crop_info.x,crop_info.y);
04637         XInfoWidget(display,windows,text);
04638       }
04639     /*
04640       Wait for next event.
04641     */
04642     XScreenEvent(display,windows,&event);
04643     if (event.xany.window == windows->command.id)
04644       {
04645         /*
04646           Select a command from the Command widget.
04647         */
04648         id=XCommandWidget(display,windows,CropModeMenu,&event);
04649         if (id < 0)
04650           continue;
04651         switch (CropCommands[id])
04652         {
04653           case CropHelpCommand:
04654           {
04655             switch (mode)
04656             {
04657               case CopyMode:
04658               {
04659                 XTextViewWidget(display,resource_info,windows,MagickFalse,
04660                   "Help Viewer - Image Copy",ImageCopyHelp);
04661                 break;
04662               }
04663               case CropMode:
04664               {
04665                 XTextViewWidget(display,resource_info,windows,MagickFalse,
04666                   "Help Viewer - Image Crop",ImageCropHelp);
04667                 break;
04668               }
04669               case CutMode:
04670               {
04671                 XTextViewWidget(display,resource_info,windows,MagickFalse,
04672                   "Help Viewer - Image Cut",ImageCutHelp);
04673                 break;
04674               }
04675             }
04676             break;
04677           }
04678           case CropDismissCommand:
04679           {
04680             /*
04681               Prematurely exit.
04682             */
04683             state|=EscapeState;
04684             state|=ExitState;
04685             break;
04686           }
04687           default:
04688             break;
04689         }
04690         continue;
04691       }
04692     switch (event.type)
04693     {
04694       case ButtonPress:
04695       {
04696         if (event.xbutton.button != Button1)
04697           break;
04698         if (event.xbutton.window != windows->image.id)
04699           break;
04700         /*
04701           Note first corner of cropping rectangle-- exit loop.
04702         */
04703         (void) XCheckDefineCursor(display,windows->image.id,cursor);
04704         crop_info.x=windows->image.x+event.xbutton.x;
04705         crop_info.y=windows->image.y+event.xbutton.y;
04706         state|=ExitState;
04707         break;
04708       }
04709       case ButtonRelease:
04710         break;
04711       case Expose:
04712         break;
04713       case KeyPress:
04714       {
04715         if (event.xkey.window != windows->image.id)
04716           break;
04717         /*
04718           Respond to a user key press.
04719         */
04720         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
04721           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
04722         switch ((int) key_symbol)
04723         {
04724           case XK_Escape:
04725           case XK_F20:
04726           {
04727             /*
04728               Prematurely exit.
04729             */
04730             state|=EscapeState;
04731             state|=ExitState;
04732             break;
04733           }
04734           case XK_F1:
04735           case XK_Help:
04736           {
04737             switch (mode)
04738             {
04739               case CopyMode:
04740               {
04741                 XTextViewWidget(display,resource_info,windows,MagickFalse,
04742                   "Help Viewer - Image Copy",ImageCopyHelp);
04743                 break;
04744               }
04745               case CropMode:
04746               {
04747                 XTextViewWidget(display,resource_info,windows,MagickFalse,
04748                   "Help Viewer - Image Crop",ImageCropHelp);
04749                 break;
04750               }
04751               case CutMode:
04752               {
04753                 XTextViewWidget(display,resource_info,windows,MagickFalse,
04754                   "Help Viewer - Image Cut",ImageCutHelp);
04755                 break;
04756               }
04757             }
04758             break;
04759           }
04760           default:
04761           {
04762             (void) XBell(display,0);
04763             break;
04764           }
04765         }
04766         break;
04767       }
04768       case MotionNotify:
04769       {
04770         if (event.xmotion.window != windows->image.id)
04771           break;
04772         /*
04773           Map and unmap Info widget as text cursor crosses its boundaries.
04774         */
04775         x=event.xmotion.x;
04776         y=event.xmotion.y;
04777         if (windows->info.mapped != MagickFalse)
04778           {
04779             if ((x < (int) (windows->info.x+windows->info.width)) &&
04780                 (y < (int) (windows->info.y+windows->info.height)))
04781               (void) XWithdrawWindow(display,windows->info.id,
04782                 windows->info.screen);
04783           }
04784         else
04785           if ((x > (int) (windows->info.x+windows->info.width)) ||
04786               (y > (int) (windows->info.y+windows->info.height)))
04787             (void) XMapWindow(display,windows->info.id);
04788         crop_info.x=windows->image.x+x;
04789         crop_info.y=windows->image.y+y;
04790         break;
04791       }
04792       default:
04793         break;
04794     }
04795   } while ((state & ExitState) == 0);
04796   (void) XSelectInput(display,windows->image.id,
04797     windows->image.attributes.event_mask);
04798   if ((state & EscapeState) != 0)
04799     {
04800       /*
04801         User want to exit without cropping.
04802       */
04803       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
04804       (void) XFreeCursor(display,cursor);
04805       return(MagickTrue);
04806     }
04807   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
04808   do
04809   {
04810     /*
04811       Size rectangle as pointer moves until the mouse button is released.
04812     */
04813     x=(int) crop_info.x;
04814     y=(int) crop_info.y;
04815     crop_info.width=0;
04816     crop_info.height=0;
04817     state=DefaultState;
04818     do
04819     {
04820       highlight_info=crop_info;
04821       highlight_info.x=crop_info.x-windows->image.x;
04822       highlight_info.y=crop_info.y-windows->image.y;
04823       if ((highlight_info.width > 3) && (highlight_info.height > 3))
04824         {
04825           /*
04826             Display info and draw cropping rectangle.
04827           */
04828           if (windows->info.mapped == MagickFalse)
04829             (void) XMapWindow(display,windows->info.id);
04830           (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
04831             crop_info.width,crop_info.height,crop_info.x,crop_info.y);
04832           XInfoWidget(display,windows,text);
04833           XHighlightRectangle(display,windows->image.id,
04834             windows->image.highlight_context,&highlight_info);
04835         }
04836       else
04837         if (windows->info.mapped != MagickFalse)
04838           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
04839       /*
04840         Wait for next event.
04841       */
04842       XScreenEvent(display,windows,&event);
04843       if ((highlight_info.width > 3) && (highlight_info.height > 3))
04844         XHighlightRectangle(display,windows->image.id,
04845           windows->image.highlight_context,&highlight_info);
04846       switch (event.type)
04847       {
04848         case ButtonPress:
04849         {
04850           crop_info.x=windows->image.x+event.xbutton.x;
04851           crop_info.y=windows->image.y+event.xbutton.y;
04852           break;
04853         }
04854         case ButtonRelease:
04855         {
04856           /*
04857             User has committed to cropping rectangle.
04858           */
04859           crop_info.x=windows->image.x+event.xbutton.x;
04860           crop_info.y=windows->image.y+event.xbutton.y;
04861           XSetCursorState(display,windows,MagickFalse);
04862           state|=ExitState;
04863           windows->command.data=0;
04864           (void) XCommandWidget(display,windows,RectifyModeMenu,
04865             (XEvent *) NULL);
04866           break;
04867         }
04868         case Expose:
04869           break;
04870         case MotionNotify:
04871         {
04872           crop_info.x=windows->image.x+event.xmotion.x;
04873           crop_info.y=windows->image.y+event.xmotion.y;
04874         }
04875         default:
04876           break;
04877       }
04878       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
04879           ((state & ExitState) != 0))
04880         {
04881           /*
04882             Check boundary conditions.
04883           */
04884           if (crop_info.x < 0)
04885             crop_info.x=0;
04886           else
04887             if (crop_info.x > (int) windows->image.ximage->width)
04888               crop_info.x=windows->image.ximage->width;
04889           if ((int) crop_info.x < x)
04890             crop_info.width=(unsigned int) (x-crop_info.x);
04891           else
04892             {
04893               crop_info.width=(unsigned int) (crop_info.x-x);
04894               crop_info.x=x;
04895             }
04896           if (crop_info.y < 0)
04897             crop_info.y=0;
04898           else
04899             if (crop_info.y > (int) windows->image.ximage->height)
04900               crop_info.y=windows->image.ximage->height;
04901           if ((int) crop_info.y < y)
04902             crop_info.height=(unsigned int) (y-crop_info.y);
04903           else
04904             {
04905               crop_info.height=(unsigned int) (crop_info.y-y);
04906               crop_info.y=y;
04907             }
04908         }
04909     } while ((state & ExitState) == 0);
04910     /*
04911       Wait for user to grab a corner of the rectangle or press return.
04912     */
04913     state=DefaultState;
04914     (void) XMapWindow(display,windows->info.id);
04915     do
04916     {
04917       if (windows->info.mapped != MagickFalse)
04918         {
04919           /*
04920             Display pointer position.
04921           */
04922           (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
04923             crop_info.width,crop_info.height,crop_info.x,crop_info.y);
04924           XInfoWidget(display,windows,text);
04925         }
04926       highlight_info=crop_info;
04927       highlight_info.x=crop_info.x-windows->image.x;
04928       highlight_info.y=crop_info.y-windows->image.y;
04929       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
04930         {
04931           state|=EscapeState;
04932           state|=ExitState;
04933           break;
04934         }
04935       XHighlightRectangle(display,windows->image.id,
04936         windows->image.highlight_context,&highlight_info);
04937       XScreenEvent(display,windows,&event);
04938       if (event.xany.window == windows->command.id)
04939         {
04940           /*
04941             Select a command from the Command widget.
04942           */
04943           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
04944           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
04945           (void) XSetFunction(display,windows->image.highlight_context,
04946             GXinvert);
04947           XHighlightRectangle(display,windows->image.id,
04948             windows->image.highlight_context,&highlight_info);
04949           if (id >= 0)
04950             switch (RectifyCommands[id])
04951             {
04952               case RectifyCopyCommand:
04953               {
04954                 state|=ExitState;
04955                 break;
04956               }
04957               case RectifyHelpCommand:
04958               {
04959                 (void) XSetFunction(display,windows->image.highlight_context,
04960                   GXcopy);
04961                 switch (mode)
04962                 {
04963                   case CopyMode:
04964                   {
04965                     XTextViewWidget(display,resource_info,windows,MagickFalse,
04966                       "Help Viewer - Image Copy",ImageCopyHelp);
04967                     break;
04968                   }
04969                   case CropMode:
04970                   {
04971                     XTextViewWidget(display,resource_info,windows,MagickFalse,
04972                       "Help Viewer - Image Crop",ImageCropHelp);
04973                     break;
04974                   }
04975                   case CutMode:
04976                   {
04977                     XTextViewWidget(display,resource_info,windows,MagickFalse,
04978                       "Help Viewer - Image Cut",ImageCutHelp);
04979                     break;
04980                   }
04981                 }
04982                 (void) XSetFunction(display,windows->image.highlight_context,
04983                   GXinvert);
04984                 break;
04985               }
04986               case RectifyDismissCommand:
04987               {
04988                 /*
04989                   Prematurely exit.
04990                 */
04991                 state|=EscapeState;
04992                 state|=ExitState;
04993                 break;
04994               }
04995               default:
04996                 break;
04997             }
04998           continue;
04999         }
05000       XHighlightRectangle(display,windows->image.id,
05001         windows->image.highlight_context,&highlight_info);
05002       switch (event.type)
05003       {
05004         case ButtonPress:
05005         {
05006           if (event.xbutton.button != Button1)
05007             break;
05008           if (event.xbutton.window != windows->image.id)
05009             break;
05010           x=windows->image.x+event.xbutton.x;
05011           y=windows->image.y+event.xbutton.y;
05012           if ((x < (int) (crop_info.x+RoiDelta)) &&
05013               (x > (int) (crop_info.x-RoiDelta)) &&
05014               (y < (int) (crop_info.y+RoiDelta)) &&
05015               (y > (int) (crop_info.y-RoiDelta)))
05016             {
05017               crop_info.x=(long) (crop_info.x+crop_info.width);
05018               crop_info.y=(long) (crop_info.y+crop_info.height);
05019               state|=UpdateConfigurationState;
05020               break;
05021             }
05022           if ((x < (int) (crop_info.x+RoiDelta)) &&
05023               (x > (int) (crop_info.x-RoiDelta)) &&
05024               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
05025               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
05026             {
05027               crop_info.x=(long) (crop_info.x+crop_info.width);
05028               state|=UpdateConfigurationState;
05029               break;
05030             }
05031           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
05032               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
05033               (y < (int) (crop_info.y+RoiDelta)) &&
05034               (y > (int) (crop_info.y-RoiDelta)))
05035             {
05036               crop_info.y=(long) (crop_info.y+crop_info.height);
05037               state|=UpdateConfigurationState;
05038               break;
05039             }
05040           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
05041               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
05042               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
05043               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
05044             {
05045               state|=UpdateConfigurationState;
05046               break;
05047             }
05048         }
05049         case ButtonRelease:
05050         {
05051           if (event.xbutton.window == windows->pan.id)
05052             if ((highlight_info.x != crop_info.x-windows->image.x) ||
05053                 (highlight_info.y != crop_info.y-windows->image.y))
05054               XHighlightRectangle(display,windows->image.id,
05055                 windows->image.highlight_context,&highlight_info);
05056           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
05057             event.xbutton.time);
05058           break;
05059         }
05060         case Expose:
05061         {
05062           if (event.xexpose.window == windows->image.id)
05063             if (event.xexpose.count == 0)
05064               {
05065                 event.xexpose.x=(int) highlight_info.x;
05066                 event.xexpose.y=(int) highlight_info.y;
05067                 event.xexpose.width=(int) highlight_info.width;
05068                 event.xexpose.height=(int) highlight_info.height;
05069                 XRefreshWindow(display,&windows->image,&event);
05070               }
05071           if (event.xexpose.window == windows->info.id)
05072             if (event.xexpose.count == 0)
05073               XInfoWidget(display,windows,text);
05074           break;
05075         }
05076         case KeyPress:
05077         {
05078           if (event.xkey.window != windows->image.id)
05079             break;
05080           /*
05081             Respond to a user key press.
05082           */
05083           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
05084             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
05085           switch ((int) key_symbol)
05086           {
05087             case XK_Escape:
05088             case XK_F20:
05089               state|=EscapeState;
05090             case XK_Return:
05091             {
05092               state|=ExitState;
05093               break;
05094             }
05095             case XK_Home:
05096             case XK_KP_Home:
05097             {
05098               crop_info.x=(long) (windows->image.width/2L-crop_info.width/2L);
05099               crop_info.y=(long) (windows->image.height/2L-crop_info.height/2L);
05100               break;
05101             }
05102             case XK_Left:
05103             case XK_KP_Left:
05104             {
05105               crop_info.x--;
05106               break;
05107             }
05108             case XK_Up:
05109             case XK_KP_Up:
05110             case XK_Next:
05111             {
05112               crop_info.y--;
05113               break;
05114             }
05115             case XK_Right:
05116             case XK_KP_Right:
05117             {
05118               crop_info.x++;
05119               break;
05120             }
05121             case XK_Prior:
05122             case XK_Down:
05123             case XK_KP_Down:
05124             {
05125               crop_info.y++;
05126               break;
05127             }
05128             case XK_F1:
05129             case XK_Help:
05130             {
05131               (void) XSetFunction(display,windows->image.highlight_context,
05132                 GXcopy);
05133               switch (mode)
05134               {
05135                 case CopyMode:
05136                 {
05137                   XTextViewWidget(display,resource_info,windows,MagickFalse,
05138                     "Help Viewer - Image Copy",ImageCopyHelp);
05139                   break;
05140                 }
05141                 case CropMode:
05142                 {
05143                   XTextViewWidget(display,resource_info,windows,MagickFalse,
05144                     "Help Viewer - Image Cropg",ImageCropHelp);
05145                   break;
05146                 }
05147                 case CutMode:
05148                 {
05149                   XTextViewWidget(display,resource_info,windows,MagickFalse,
05150                     "Help Viewer - Image Cutg",ImageCutHelp);
05151                   break;
05152                 }
05153               }
05154               (void) XSetFunction(display,windows->image.highlight_context,
05155                 GXinvert);
05156               break;
05157             }
05158             default:
05159             {
05160               (void) XBell(display,0);
05161               break;
05162             }
05163           }
05164           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
05165             event.xkey.time);
05166           break;
05167         }
05168         case KeyRelease:
05169           break;
05170         case MotionNotify:
05171         {
05172           if (event.xmotion.window != windows->image.id)
05173             break;
05174           /*
05175             Map and unmap Info widget as text cursor crosses its boundaries.
05176           */
05177           x=event.xmotion.x;
05178           y=event.xmotion.y;
05179           if (windows->info.mapped != MagickFalse)
05180             {
05181               if ((x < (int) (windows->info.x+windows->info.width)) &&
05182                   (y < (int) (windows->info.y+windows->info.height)))
05183                 (void) XWithdrawWindow(display,windows->info.id,
05184                   windows->info.screen);
05185             }
05186           else
05187             if ((x > (int) (windows->info.x+windows->info.width)) ||
05188                 (y > (int) (windows->info.y+windows->info.height)))
05189               (void) XMapWindow(display,windows->info.id);
05190           crop_info.x=windows->image.x+event.xmotion.x;
05191           crop_info.y=windows->image.y+event.xmotion.y;
05192           break;
05193         }
05194         case SelectionRequest:
05195         {
05196           XSelectionEvent
05197             notify;
05198 
05199           XSelectionRequestEvent
05200             *request;
05201 
05202           /*
05203             Set primary selection.
05204           */
05205           (void) FormatMagickString(text,MaxTextExtent,"%lux%lu%+ld%+ld",
05206             crop_info.width,crop_info.height,crop_info.x,crop_info.y);
05207           request=(&(event.xselectionrequest));
05208           (void) XChangeProperty(request->display,request->requestor,
05209             request->property,request->target,8,PropModeReplace,
05210             (unsigned char *) text,(int) strlen(text));
05211           notify.type=SelectionNotify;
05212           notify.display=request->display;
05213           notify.requestor=request->requestor;
05214           notify.selection=request->selection;
05215           notify.target=request->target;
05216           notify.time=request->time;
05217           if (request->property == None)
05218             notify.property=request->target;
05219           else
05220             notify.property=request->property;
05221           (void) XSendEvent(request->display,request->requestor,False,0,
05222             (XEvent *) &notify);
05223         }
05224         default:
05225           break;
05226       }
05227       if ((state & UpdateConfigurationState) != 0)
05228         {
05229           (void) XPutBackEvent(display,&event);
05230           (void) XCheckDefineCursor(display,windows->image.id,cursor);
05231           break;
05232         }
05233     } while ((state & ExitState) == 0);
05234   } while ((state & ExitState) == 0);
05235   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
05236   XSetCursorState(display,windows,MagickFalse);
05237   if ((state & EscapeState) != 0)
05238     return(MagickTrue);
05239   if (mode == CropMode)
05240     if (((int) crop_info.width != windows->image.ximage->width) ||
05241         ((int) crop_info.height != windows->image.ximage->height))
05242       {
05243         /*
05244           Reconfigure Image window as defined by cropping rectangle.
05245         */
05246         XSetCropGeometry(display,windows,&crop_info,image);
05247         windows->image.window_changes.width=(int) crop_info.width;
05248         windows->image.window_changes.height=(int) crop_info.height;
05249         (void) XConfigureImage(display,resource_info,windows,image);
05250         return(MagickTrue);
05251       }
05252   /*
05253     Copy image before applying image transforms.
05254   */
05255   XSetCursorState(display,windows,MagickTrue);
05256   XCheckRefreshWindows(display,windows);
05257   width=(unsigned int) image->columns;
05258   height=(unsigned int) image->rows;
05259   x=0;
05260   y=0;
05261   if (windows->image.crop_geometry != (char *) NULL)
05262     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
05263   scale_factor=(MagickRealType) width/windows->image.ximage->width;
05264   crop_info.x+=x;
05265   crop_info.x=(int) (scale_factor*crop_info.x+0.5);
05266   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
05267   scale_factor=(MagickRealType) height/windows->image.ximage->height;
05268   crop_info.y+=y;
05269   crop_info.y=(int) (scale_factor*crop_info.y+0.5);
05270   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
05271   crop_image=CropImage(image,&crop_info,&image->exception);
05272   XSetCursorState(display,windows,MagickFalse);
05273   if (crop_image == (Image *) NULL)
05274     return(MagickFalse);
05275   if (resource_info->copy_image != (Image *) NULL)
05276     resource_info->copy_image=DestroyImage(resource_info->copy_image);
05277   resource_info->copy_image=crop_image;
05278   if (mode == CopyMode)
05279     {
05280       (void) XConfigureImage(display,resource_info,windows,image);
05281       return(MagickTrue);
05282     }
05283   /*
05284     Cut image.
05285   */
05286   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
05287     return(MagickFalse);
05288   image->matte=MagickTrue;
05289   exception=(&image->exception);
05290   for (y=0; y < (long) crop_info.height; y++)
05291   {
05292     q=GetAuthenticPixels(image,crop_info.x,y+crop_info.y,crop_info.width,1,
05293       exception);
05294     if (q == (PixelPacket *) NULL)
05295       break;
05296     for (x=0; x < (int) crop_info.width; x++)
05297     {
05298       q->opacity=(Quantum) TransparentOpacity;
05299       q++;
05300     }
05301     if (SyncAuthenticPixels(image,exception) == MagickFalse)
05302       break;
05303   }
05304   /*
05305     Update image configuration.
05306   */
05307   XConfigureImageColormap(display,resource_info,windows,image);
05308   (void) XConfigureImage(display,resource_info,windows,image);
05309   return(MagickTrue);
05310 }
05311 
05312 /*
05313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
05314 %                                                                             %
05315 %                                                                             %
05316 %                                                                             %
05317 +   X D r a w I m a g e                                                       %
05318 %                                                                             %
05319 %                                                                             %
05320 %                                                                             %
05321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
05322 %
05323 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
05324 %  the image.
05325 %
05326 %  The format of the XDrawEditImage method is:
05327 %
05328 %      MagickBooleanType XDrawEditImage(Display *display,
05329 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
05330 %
05331 %  A description of each parameter follows:
05332 %
05333 %    o display: Specifies a connection to an X server; returned from
05334 %      XOpenDisplay.
05335 %
05336 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
05337 %
05338 %    o windows: Specifies a pointer to a XWindows structure.
05339 %
05340 %    o image: the image;  returned from
05341 %      ReadImage.
05342 %
05343 */
05344 static MagickBooleanType XDrawEditImage(Display *display,
05345   XResourceInfo *resource_info,XWindows *windows,Image **image)
05346 {
05347   static const char
05348     *DrawMenu[] =
05349     {
05350       "Element",
05351       "Color",
05352       "Stipple",
05353       "Width",
05354       "Undo",
05355       "Help",
05356       "Dismiss",
05357       (char *) NULL
05358     };
05359 
05360   static ElementType
05361     element = PointElement;
05362 
05363   static const ModeType
05364     DrawCommands[] =
05365     {
05366       DrawElementCommand,
05367       DrawColorCommand,
05368       DrawStippleCommand,
05369       DrawWidthCommand,
05370       DrawUndoCommand,
05371       DrawHelpCommand,
05372       DrawDismissCommand
05373     };
05374 
05375   static Pixmap
05376     stipple = (Pixmap) NULL;
05377 
05378   static unsigned int
05379     pen_id = 0,
05380     line_width = 1;
05381 
05382   char
05383     command[MaxTextExtent],
05384     text[MaxTextExtent];
05385 
05386   Cursor
05387     cursor;
05388 
05389   int
05390     entry,
05391     id,
05392     number_coordinates,
05393     x,
05394     y;
05395 
05396   MagickRealType
05397     degrees;
05398 
05399   MagickStatusType
05400     status;
05401 
05402   RectangleInfo
05403     rectangle_info;
05404 
05405   register int
05406     i;
05407 
05408   unsigned int
05409     distance,
05410     height,
05411     max_coordinates,
05412     width;
05413 
05414   unsigned long
05415     state;
05416 
05417   Window
05418     root_window;
05419 
05420   XDrawInfo
05421     draw_info;
05422 
05423   XEvent
05424     event;
05425 
05426   XPoint
05427     *coordinate_info;
05428 
05429   XSegment
05430     line_info;
05431 
05432   /*
05433     Allocate polygon info.
05434   */
05435   max_coordinates=2048;
05436   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
05437     sizeof(*coordinate_info));
05438   if (coordinate_info == (XPoint *) NULL)
05439     {
05440       (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
05441         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
05442       return(MagickFalse);
05443     }
05444   /*
05445     Map Command widget.
05446   */
05447   (void) CloneString(&windows->command.name,"Draw");
05448   windows->command.data=4;
05449   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
05450   (void) XMapRaised(display,windows->command.id);
05451   XClientMessage(display,windows->image.id,windows->im_protocols,
05452     windows->im_update_widget,CurrentTime);
05453   /*
05454     Wait for first button press.
05455   */
05456   root_window=XRootWindow(display,XDefaultScreen(display));
05457   draw_info.stencil=OpaqueStencil;
05458   status=MagickTrue;
05459   cursor=XCreateFontCursor(display,XC_tcross);
05460   for ( ; ; )
05461   {
05462     XQueryPosition(display,windows->image.id,&x,&y);
05463     (void) XSelectInput(display,windows->image.id,
05464       windows->image.attributes.event_mask | PointerMotionMask);
05465     (void) XCheckDefineCursor(display,windows->image.id,cursor);
05466     state=DefaultState;
05467     do
05468     {
05469       if (windows->info.mapped != MagickFalse)
05470         {
05471           /*
05472             Display pointer position.
05473           */
05474           (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
05475             x+windows->image.x,y+windows->image.y);
05476           XInfoWidget(display,windows,text);
05477         }
05478       /*
05479         Wait for next event.
05480       */
05481       XScreenEvent(display,windows,&event);
05482       if (event.xany.window == windows->command.id)
05483         {
05484           /*
05485             Select a command from the Command widget.
05486           */
05487           id=XCommandWidget(display,windows,DrawMenu,&event);
05488           if (id < 0)
05489             continue;
05490           switch (DrawCommands[id])
05491           {
05492             case DrawElementCommand:
05493             {
05494               static const char
05495                 *Elements[] =
05496                 {
05497                   "point",
05498                   "line",
05499                   "rectangle",
05500                   "fill rectangle",
05501                   "circle",
05502                   "fill circle",
05503                   "ellipse",
05504                   "fill ellipse",
05505                   "polygon",
05506                   "fill polygon",
05507                   (char *) NULL,
05508                 };
05509 
05510               /*
05511                 Select a command from the pop-up menu.
05512               */
05513               element=(ElementType) (XMenuWidget(display,windows,
05514                 DrawMenu[id],Elements,command)+1);
05515               break;
05516             }
05517             case DrawColorCommand:
05518             {
05519               const char
05520                 *ColorMenu[MaxNumberPens+1];
05521 
05522               int
05523                 pen_number;
05524 
05525               MagickBooleanType
05526                 transparent;
05527 
05528               XColor
05529                 color;
05530 
05531               /*
05532                 Initialize menu selections.
05533               */
05534               for (i=0; i < (int) (MaxNumberPens-2); i++)
05535                 ColorMenu[i]=resource_info->pen_colors[i];
05536               ColorMenu[MaxNumberPens-2]="transparent";
05537               ColorMenu[MaxNumberPens-1]="Browser...";
05538               ColorMenu[MaxNumberPens]=(char *) NULL;
05539               /*
05540                 Select a pen color from the pop-up menu.
05541               */
05542               pen_number=XMenuWidget(display,windows,DrawMenu[id],
05543                 (const char **) ColorMenu,command);
05544               if (pen_number < 0)
05545                 break;
05546               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
05547                 MagickFalse;
05548               if (transparent != MagickFalse)
05549                 {
05550                   draw_info.stencil=TransparentStencil;
05551                   break;
05552                 }
05553               if (pen_number == (MaxNumberPens-1))
05554                 {
05555                   static char
05556                     color_name[MaxTextExtent] = "gray";
05557 
05558                   /*
05559                     Select a pen color from a dialog.
05560                   */
05561                   resource_info->pen_colors[pen_number]=color_name;
05562                   XColorBrowserWidget(display,windows,"Select",color_name);
05563                   if (*color_name == '\0')
05564                     break;
05565                 }
05566               /*
05567                 Set pen color.
05568               */
05569               (void) XParseColor(display,windows->map_info->colormap,
05570                 resource_info->pen_colors[pen_number],&color);
05571               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
05572                 (unsigned int) MaxColors,&color);
05573               windows->pixel_info->pen_colors[pen_number]=color;
05574               pen_id=(unsigned int) pen_number;
05575               draw_info.stencil=OpaqueStencil;
05576               break;
05577             }
05578             case DrawStippleCommand:
05579             {
05580               Image
05581                 *stipple_image;
05582 
05583               ImageInfo
05584                 *image_info;
05585 
05586               int
05587                 status;
05588 
05589               static char
05590                 filename[MaxTextExtent] = "\0";
05591 
05592               static const char
05593                 *StipplesMenu[] =
05594                 {
05595                   "Brick",
05596                   "Diagonal",
05597                   "Scales",
05598                   "Vertical",
05599                   "Wavy",
05600                   "Translucent",
05601                   "Opaque",
05602                   (char *) NULL,
05603                   (char *) NULL,
05604                 };
05605 
05606               /*
05607                 Select a command from the pop-up menu.
05608               */
05609               StipplesMenu[7]="Open...";
05610               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
05611                 command);
05612               if (entry < 0)
05613                 break;
05614               if (stipple != (Pixmap) NULL)
05615                 (void) XFreePixmap(display,stipple);
05616               stipple=(Pixmap) NULL;
05617               if (entry == 6)
05618                 break;
05619               if (entry != 7)
05620                 {
05621                   switch (entry)
05622                   {
05623                     case 0:
05624                     {
05625                       stipple=XCreateBitmapFromData(display,root_window,
05626                         (char *) BricksBitmap,BricksWidth,BricksHeight);
05627                       break;
05628                     }
05629                     case 1:
05630                     {
05631                       stipple=XCreateBitmapFromData(display,root_window,
05632                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
05633                       break;
05634                     }
05635                     case 2:
05636                     {
05637                       stipple=XCreateBitmapFromData(display,root_window,
05638                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
05639                       break;
05640                     }
05641                     case 3:
05642                     {
05643                       stipple=XCreateBitmapFromData(display,root_window,
05644                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
05645                       break;
05646                     }
05647                     case 4:
05648                     {
05649                       stipple=XCreateBitmapFromData(display,root_window,
05650                         (char *) WavyBitmap,WavyWidth,WavyHeight);
05651                       break;
05652                     }
05653                     case 5:
05654                     default:
05655                     {
05656                       stipple=XCreateBitmapFromData(display,root_window,
05657                         (char *) HighlightBitmap,HighlightWidth,
05658                         HighlightHeight);
05659                       break;
05660                     }
05661                   }
05662                   break;
05663                 }
05664               XFileBrowserWidget(display,windows,"Stipple",filename);
05665               if (*filename == '\0')
05666                 break;
05667               /*
05668                 Read image.
05669               */
05670               XSetCursorState(display,windows,MagickTrue);
05671               XCheckRefreshWindows(display,windows);
05672               image_info=AcquireImageInfo();
05673               (void) CopyMagickString(image_info->filename,filename,
05674                 MaxTextExtent);
05675               stipple_image=ReadImage(image_info,&(*image)->exception);
05676               CatchException(&(*image)->exception);
05677               XSetCursorState(display,windows,MagickFalse);
05678               if (stipple_image == (Image *) NULL)
05679                 break;
05680               (void) AcquireUniqueFileResource(filename);
05681               (void) FormatMagickString(stipple_image->filename,MaxTextExtent,
05682                 "xbm:%s",filename);
05683               (void) WriteImage(image_info,stipple_image);
05684               stipple_image=DestroyImage(stipple_image);
05685               image_info=DestroyImageInfo(image_info);
05686               status=XReadBitmapFile(display,root_window,filename,&width,
05687                 &height,&stipple,&x,&y);
05688               (void) RelinquishUniqueFileResource(filename);
05689               if ((status != BitmapSuccess) != 0)
05690                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
05691                   filename);
05692               break;
05693             }
05694             case DrawWidthCommand:
05695             {
05696               static char
05697                 width[MaxTextExtent] = "0";
05698 
05699               static const char
05700                 *WidthsMenu[] =
05701                 {
05702                   "1",
05703                   "2",
05704                   "4",
05705                   "8",
05706                   "16",
05707                   "Dialog...",
05708                   (char *) NULL,
05709                 };
05710 
05711               /*
05712                 Select a command from the pop-up menu.
05713               */
05714               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
05715                 command);
05716               if (entry < 0)
05717                 break;
05718               if (entry != 5)
05719                 {
05720                   line_width=(unsigned int) atoi(WidthsMenu[entry]);
05721                   break;
05722                 }
05723               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
05724                 width);
05725               if (*width == '\0')
05726                 break;
05727               line_width=(unsigned int) atoi(width);
05728               break;
05729             }
05730             case DrawUndoCommand:
05731             {
05732               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
05733                 image);
05734               break;
05735             }
05736             case DrawHelpCommand:
05737             {
05738               XTextViewWidget(display,resource_info,windows,MagickFalse,
05739                 "Help Viewer - Image Rotation",ImageDrawHelp);
05740               (void) XCheckDefineCursor(display,windows->image.id,cursor);
05741               break;
05742             }
05743             case DrawDismissCommand:
05744             {
05745               /*
05746                 Prematurely exit.
05747               */
05748               state|=EscapeState;
05749               state|=ExitState;
05750               break;
05751             }
05752             default:
05753               break;
05754           }
05755           (void) XCheckDefineCursor(display,windows->image.id,cursor);
05756           continue;
05757         }
05758       switch (event.type)
05759       {
05760         case ButtonPress:
05761         {
05762           if (event.xbutton.button != Button1)
05763             break;
05764           if (event.xbutton.window != windows->image.id)
05765             break;
05766           /*
05767             exit loop.
05768           */
05769           x=event.xbutton.x;
05770           y=event.xbutton.y;
05771           state|=ExitState;
05772           break;
05773         }
05774         case ButtonRelease:
05775           break;
05776         case Expose:
05777           break;
05778         case KeyPress:
05779         {
05780           KeySym
05781             key_symbol;
05782 
05783           if (event.xkey.window != windows->image.id)
05784             break;
05785           /*
05786             Respond to a user key press.
05787           */
05788           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
05789             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
05790           switch ((int) key_symbol)
05791           {
05792             case XK_Escape:
05793             case XK_F20:
05794             {
05795               /*
05796                 Prematurely exit.
05797               */
05798               state|=EscapeState;
05799               state|=ExitState;
05800               break;
05801             }
05802             case XK_F1:
05803             case XK_Help:
05804             {
05805               XTextViewWidget(display,resource_info,windows,MagickFalse,
05806                 "Help Viewer - Image Rotation",ImageDrawHelp);
05807               break;
05808             }
05809             default:
05810             {
05811               (void) XBell(display,0);
05812               break;
05813             }
05814           }
05815           break;
05816         }
05817         case MotionNotify:
05818         {
05819           /*
05820             Map and unmap Info widget as text cursor crosses its boundaries.
05821           */
05822           x=event.xmotion.x;
05823           y=event.xmotion.y;
05824           if (windows->info.mapped != MagickFalse)
05825             {
05826               if ((x < (int) (windows->info.x+windows->info.width)) &&
05827                   (y < (int) (windows->info.y+windows->info.height)))
05828                 (void) XWithdrawWindow(display,windows->info.id,
05829                   windows->info.screen);
05830             }
05831           else
05832             if ((x > (int) (windows->info.x+windows->info.width)) ||
05833                 (y > (int) (windows->info.y+windows->info.height)))
05834               (void) XMapWindow(display,windows->info.id);
05835           break;
05836         }
05837       }
05838     } while ((state & ExitState) == 0);
05839     (void) XSelectInput(display,windows->image.id,
05840       windows->image.attributes.event_mask);
05841     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
05842     if ((state & EscapeState) != 0)
05843       break;
05844     /*
05845       Draw element as pointer moves until the button is released.
05846     */
05847     distance=0;
05848     degrees=0.0;
05849     line_info.x1=x;
05850     line_info.y1=y;
05851     line_info.x2=x;
05852     line_info.y2=y;
05853     rectangle_info.x=x;
05854     rectangle_info.y=y;
05855     rectangle_info.width=0;
05856     rectangle_info.height=0;
05857     number_coordinates=1;
05858     coordinate_info->x=x;
05859     coordinate_info->y=y;
05860     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
05861     state=DefaultState;
05862     do
05863     {
05864       switch (element)
05865       {
05866         case PointElement:
05867         default:
05868         {
05869           if (number_coordinates > 1)
05870             {
05871               (void) XDrawLines(display,windows->image.id,
05872                 windows->image.highlight_context,coordinate_info,
05873                 number_coordinates,CoordModeOrigin);
05874               (void) FormatMagickString(text,MaxTextExtent," %+d%+d",
05875                 coordinate_info[number_coordinates-1].x,
05876                 coordinate_info[number_coordinates-1].y);
05877               XInfoWidget(display,windows,text);
05878             }
05879           break;
05880         }
05881         case LineElement:
05882         {
05883           if (distance > 9)
05884             {
05885               /*
05886                 Display angle of the line.
05887               */
05888               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
05889                 line_info.y1),(double) (line_info.x2-line_info.x1)));
05890               (void) FormatMagickString(text,MaxTextExtent," %g",
05891                 (double) degrees);
05892               XInfoWidget(display,windows,text);
05893               XHighlightLine(display,windows->image.id,
05894                 windows->image.highlight_context,&line_info);
05895             }
05896           else
05897             if (windows->info.mapped != MagickFalse)
05898               (void) XWithdrawWindow(display,windows->info.id,
05899                 windows->info.screen);
05900           break;
05901         }
05902         case RectangleElement:
05903         case FillRectangleElement:
05904         {
05905           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
05906             {
05907               /*
05908                 Display info and draw drawing rectangle.
05909               */
05910               (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
05911                 rectangle_info.width,rectangle_info.height,rectangle_info.x,
05912                 rectangle_info.y);
05913               XInfoWidget(display,windows,text);
05914               XHighlightRectangle(display,windows->image.id,
05915                 windows->image.highlight_context,&rectangle_info);
05916             }
05917           else
05918             if (windows->info.mapped != MagickFalse)
05919               (void) XWithdrawWindow(display,windows->info.id,
05920                 windows->info.screen);
05921           break;
05922         }
05923         case CircleElement:
05924         case FillCircleElement:
05925         case EllipseElement:
05926         case FillEllipseElement:
05927         {
05928           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
05929             {
05930               /*
05931                 Display info and draw drawing rectangle.
05932               */
05933               (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
05934                 rectangle_info.width,rectangle_info.height,rectangle_info.x,
05935                 rectangle_info.y);
05936               XInfoWidget(display,windows,text);
05937               XHighlightEllipse(display,windows->image.id,
05938                 windows->image.highlight_context,&rectangle_info);
05939             }
05940           else
05941             if (windows->info.mapped != MagickFalse)
05942               (void) XWithdrawWindow(display,windows->info.id,
05943                 windows->info.screen);
05944           break;
05945         }
05946         case PolygonElement:
05947         case FillPolygonElement:
05948         {
05949           if (number_coordinates > 1)
05950             (void) XDrawLines(display,windows->image.id,
05951               windows->image.highlight_context,coordinate_info,
05952               number_coordinates,CoordModeOrigin);
05953           if (distance > 9)
05954             {
05955               /*
05956                 Display angle of the line.
05957               */
05958               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
05959                 line_info.y1),(double) (line_info.x2-line_info.x1)));
05960               (void) FormatMagickString(text,MaxTextExtent," %g",
05961                 (double) degrees);
05962               XInfoWidget(display,windows,text);
05963               XHighlightLine(display,windows->image.id,
05964                 windows->image.highlight_context,&line_info);
05965             }
05966           else
05967             if (windows->info.mapped != MagickFalse)
05968               (void) XWithdrawWindow(display,windows->info.id,
05969                 windows->info.screen);
05970           break;
05971         }
05972       }
05973       /*
05974         Wait for next event.
05975       */
05976       XScreenEvent(display,windows,&event);
05977       switch (element)
05978       {
05979         case PointElement:
05980         default:
05981         {
05982           if (number_coordinates > 1)
05983             (void) XDrawLines(display,windows->image.id,
05984               windows->image.highlight_context,coordinate_info,
05985               number_coordinates,CoordModeOrigin);
05986           break;
05987         }
05988         case LineElement:
05989         {
05990           if (distance > 9)
05991             XHighlightLine(display,windows->image.id,
05992               windows->image.highlight_context,&line_info);
05993           break;
05994         }
05995         case RectangleElement:
05996         case FillRectangleElement:
05997         {
05998           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
05999             XHighlightRectangle(display,windows->image.id,
06000               windows->image.highlight_context,&rectangle_info);
06001           break;
06002         }
06003         case CircleElement:
06004         case FillCircleElement:
06005         case EllipseElement:
06006         case FillEllipseElement:
06007         {
06008           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
06009             XHighlightEllipse(display,windows->image.id,
06010               windows->image.highlight_context,&rectangle_info);
06011           break;
06012         }
06013         case PolygonElement:
06014         case FillPolygonElement:
06015         {
06016           if (number_coordinates > 1)
06017             (void) XDrawLines(display,windows->image.id,
06018               windows->image.highlight_context,coordinate_info,
06019               number_coordinates,CoordModeOrigin);
06020           if (distance > 9)
06021             XHighlightLine(display,windows->image.id,
06022               windows->image.highlight_context,&line_info);
06023           break;
06024         }
06025       }
06026       switch (event.type)
06027       {
06028         case ButtonPress:
06029           break;
06030         case ButtonRelease:
06031         {
06032           /*
06033             User has committed to element.
06034           */
06035           line_info.x2=event.xbutton.x;
06036           line_info.y2=event.xbutton.y;
06037           rectangle_info.x=event.xbutton.x;
06038           rectangle_info.y=event.xbutton.y;
06039           coordinate_info[number_coordinates].x=event.xbutton.x;
06040           coordinate_info[number_coordinates].y=event.xbutton.y;
06041           if (((element != PolygonElement) &&
06042                (element != FillPolygonElement)) || (distance <= 9))
06043             {
06044               state|=ExitState;
06045               break;
06046             }
06047           number_coordinates++;
06048           if (number_coordinates < (int) max_coordinates)
06049             {
06050               line_info.x1=event.xbutton.x;
06051               line_info.y1=event.xbutton.y;
06052               break;
06053             }
06054           max_coordinates<<=1;
06055           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
06056             max_coordinates,sizeof(*coordinate_info));
06057           if (coordinate_info == (XPoint *) NULL)
06058             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
06059               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
06060           break;
06061         }
06062         case Expose:
06063           break;
06064         case MotionNotify:
06065         {
06066           if (event.xmotion.window != windows->image.id)
06067             break;
06068           if (element != PointElement)
06069             {
06070               line_info.x2=event.xmotion.x;
06071               line_info.y2=event.xmotion.y;
06072               rectangle_info.x=event.xmotion.x;
06073               rectangle_info.y=event.xmotion.y;
06074               break;
06075             }
06076           coordinate_info[number_coordinates].x=event.xbutton.x;
06077           coordinate_info[number_coordinates].y=event.xbutton.y;
06078           number_coordinates++;
06079           if (number_coordinates < (int) max_coordinates)
06080             break;
06081           max_coordinates<<=1;
06082           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
06083             max_coordinates,sizeof(*coordinate_info));
06084           if (coordinate_info == (XPoint *) NULL)
06085             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
06086               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
06087           break;
06088         }
06089         default:
06090           break;
06091       }
06092       /*
06093         Check boundary conditions.
06094       */
06095       if (line_info.x2 < 0)
06096         line_info.x2=0;
06097       else
06098         if (line_info.x2 > (int) windows->image.width)
06099           line_info.x2=(short) windows->image.width;
06100       if (line_info.y2 < 0)
06101         line_info.y2=0;
06102       else
06103         if (line_info.y2 > (int) windows->image.height)
06104           line_info.y2=(short) windows->image.height;
06105       distance=(unsigned int)
06106         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
06107          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
06108       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
06109           ((state & ExitState) != 0))
06110         {
06111           if (rectangle_info.x < 0)
06112             rectangle_info.x=0;
06113           else
06114             if (rectangle_info.x > (int) windows->image.width)
06115               rectangle_info.x=(long) windows->image.width;
06116           if ((int) rectangle_info.x < x)
06117             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
06118           else
06119             {
06120               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
06121               rectangle_info.x=x;
06122             }
06123           if (rectangle_info.y < 0)
06124             rectangle_info.y=0;
06125           else
06126             if (rectangle_info.y > (int) windows->image.height)
06127               rectangle_info.y=(long) windows->image.height;
06128           if ((int) rectangle_info.y < y)
06129             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
06130           else
06131             {
06132               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
06133               rectangle_info.y=y;
06134             }
06135         }
06136     } while ((state & ExitState) == 0);
06137     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
06138     if ((element == PointElement) || (element == PolygonElement) ||
06139         (element == FillPolygonElement))
06140       {
06141         /*
06142           Determine polygon bounding box.
06143         */
06144         rectangle_info.x=coordinate_info->x;
06145         rectangle_info.y=coordinate_info->y;
06146         x=coordinate_info->x;
06147         y=coordinate_info->y;
06148         for (i=1; i < number_coordinates; i++)
06149         {
06150           if (coordinate_info[i].x > x)
06151             x=coordinate_info[i].x;
06152           if (coordinate_info[i].y > y)
06153             y=coordinate_info[i].y;
06154           if (coordinate_info[i].x < rectangle_info.x)
06155             rectangle_info.x=MagickMax(coordinate_info[i].x,0);
06156           if (coordinate_info[i].y < rectangle_info.y)
06157             rectangle_info.y=MagickMax(coordinate_info[i].y,0);
06158         }
06159         rectangle_info.width=(unsigned long) (x-rectangle_info.x);
06160         rectangle_info.height=(unsigned long) (y-rectangle_info.y);
06161         for (i=0; i < number_coordinates; i++)
06162         {
06163           coordinate_info[i].x-=rectangle_info.x;
06164           coordinate_info[i].y-=rectangle_info.y;
06165         }
06166       }
06167     else
06168       if (distance <= 9)
06169         continue;
06170       else
06171         if ((element == RectangleElement) ||
06172             (element == CircleElement) || (element == EllipseElement))
06173           {
06174             rectangle_info.width--;
06175             rectangle_info.height--;
06176           }
06177     /*
06178       Drawing is relative to image configuration.
06179     */
06180     draw_info.x=(int) rectangle_info.x;
06181     draw_info.y=(int) rectangle_info.y;
06182     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
06183       image);
06184     width=(unsigned int) (*image)->columns;
06185     height=(unsigned int) (*image)->rows;
06186     x=0;
06187     y=0;
06188     if (windows->image.crop_geometry != (char *) NULL)
06189       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
06190     draw_info.x+=windows->image.x-(line_width/2);
06191     if (draw_info.x < 0)
06192       draw_info.x=0;
06193     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
06194     draw_info.y+=windows->image.y-(line_width/2);
06195     if (draw_info.y < 0)
06196       draw_info.y=0;
06197     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
06198     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
06199     if (draw_info.width > (unsigned int) (*image)->columns)
06200       draw_info.width=(unsigned int) (*image)->columns;
06201     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
06202     if (draw_info.height > (unsigned int) (*image)->rows)
06203       draw_info.height=(unsigned int) (*image)->rows;
06204     (void) FormatMagickString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
06205       width*draw_info.width/windows->image.ximage->width,
06206       height*draw_info.height/windows->image.ximage->height,
06207       draw_info.x+x,draw_info.y+y);
06208     /*
06209       Initialize drawing attributes.
06210     */
06211     draw_info.degrees=0.0;
06212     draw_info.element=element;
06213     draw_info.stipple=stipple;
06214     draw_info.line_width=line_width;
06215     draw_info.line_info=line_info;
06216     if (line_info.x1 > (int) (line_width/2))
06217       draw_info.line_info.x1=(short) line_width/2;
06218     if (line_info.y1 > (int) (line_width/2))
06219       draw_info.line_info.y1=(short) line_width/2;
06220     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
06221     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
06222     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
06223       {
06224         draw_info.line_info.x2=(-draw_info.line_info.x2);
06225         draw_info.line_info.y2=(-draw_info.line_info.y2);
06226       }
06227     if (draw_info.line_info.x2 < 0)
06228       {
06229         draw_info.line_info.x2=(-draw_info.line_info.x2);
06230         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
06231       }
06232     if (draw_info.line_info.y2 < 0)
06233       {
06234         draw_info.line_info.y2=(-draw_info.line_info.y2);
06235         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
06236       }
06237     draw_info.rectangle_info=rectangle_info;
06238     if (draw_info.rectangle_info.x > (int) (line_width/2))
06239       draw_info.rectangle_info.x=(long) line_width/2;
06240     if (draw_info.rectangle_info.y > (int) (line_width/2))
06241       draw_info.rectangle_info.y=(long) line_width/2;
06242     draw_info.number_coordinates=(unsigned int) number_coordinates;
06243     draw_info.coordinate_info=coordinate_info;
06244     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
06245     /*
06246       Draw element on image.
06247     */
06248     XSetCursorState(display,windows,MagickTrue);
06249     XCheckRefreshWindows(display,windows);
06250     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
06251     XSetCursorState(display,windows,MagickFalse);
06252     /*
06253       Update image colormap and return to image drawing.
06254     */
06255     XConfigureImageColormap(display,resource_info,windows,*image);
06256     (void) XConfigureImage(display,resource_info,windows,*image);
06257   }
06258   XSetCursorState(display,windows,MagickFalse);
06259   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
06260   return(status != 0 ? MagickTrue : MagickFalse);
06261 }
06262 
06263 /*
06264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
06265 %                                                                             %
06266 %                                                                             %
06267 %                                                                             %
06268 +   X D r a w P a n R e c t a n g l e                                         %
06269 %                                                                             %
06270 %                                                                             %
06271 %                                                                             %
06272 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
06273 %
06274 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
06275 %  displays a zoom image and the rectangle shows which portion of the image is
06276 %  displayed in the Image window.
06277 %
06278 %  The format of the XDrawPanRectangle method is:
06279 %
06280 %      XDrawPanRectangle(Display *display,XWindows *windows)
06281 %
06282 %  A description of each parameter follows:
06283 %
06284 %    o display: Specifies a connection to an X server;  returned from
06285 %      XOpenDisplay.
06286 %
06287 %    o windows: Specifies a pointer to a XWindows structure.
06288 %
06289 */
06290 static void XDrawPanRectangle(Display *display,XWindows *windows)
06291 {
06292   MagickRealType
06293     scale_factor;
06294 
06295   RectangleInfo
06296     highlight_info;
06297 
06298   /*
06299     Determine dimensions of the panning rectangle.
06300   */
06301   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
06302   highlight_info.x=(int) (scale_factor*windows->image.x+0.5);
06303   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
06304   scale_factor=(MagickRealType)
06305     windows->pan.height/windows->image.ximage->height;
06306   highlight_info.y=(int) (scale_factor*windows->image.y+0.5);
06307   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
06308   /*
06309     Display the panning rectangle.
06310   */
06311   (void) XClearWindow(display,windows->pan.id);
06312   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
06313     &highlight_info);
06314 }
06315 
06316 /*
06317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
06318 %                                                                             %
06319 %                                                                             %
06320 %                                                                             %
06321 +   X I m a g e C a c h e                                                     %
06322 %                                                                             %
06323 %                                                                             %
06324 %                                                                             %
06325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
06326 %
06327 %  XImageCache() handles the creation, manipulation, and destruction of the
06328 %  image cache (undo and redo buffers).
06329 %
06330 %  The format of the XImageCache method is:
06331 %
06332 %      void XImageCache(Display *display,XResourceInfo *resource_info,
06333 %        XWindows *windows,const CommandType command,Image **image)
06334 %
06335 %  A description of each parameter follows:
06336 %
06337 %    o display: Specifies a connection to an X server; returned from
06338 %      XOpenDisplay.
06339 %
06340 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
06341 %
06342 %    o windows: Specifies a pointer to a XWindows structure.
06343 %
06344 %    o command: Specifies a command to perform.
06345 %
06346 %    o image: the image;  XImageCache
06347 %      may transform the image and return a new image pointer.
06348 %
06349 */
06350 static void XImageCache(Display *display,XResourceInfo *resource_info,
06351   XWindows *windows,const CommandType command,Image **image)
06352 {
06353   Image
06354     *cache_image;
06355 
06356   static Image
06357     *redo_image = (Image *) NULL,
06358     *undo_image = (Image *) NULL;
06359 
06360   switch (command)
06361   {
06362     case FreeBuffersCommand:
06363     {
06364       /*
06365         Free memory from the undo and redo cache.
06366       */
06367       while (undo_image != (Image *) NULL)
06368       {
06369         cache_image=undo_image;
06370         undo_image=GetPreviousImageInList(undo_image);
06371         cache_image->list=DestroyImage(cache_image->list);
06372         cache_image=DestroyImage(cache_image);
06373       }
06374       undo_image=NewImageList();
06375       if (redo_image != (Image *) NULL)
06376         redo_image=DestroyImage(redo_image);
06377       redo_image=NewImageList();
06378       return;
06379     }
06380     case UndoCommand:
06381     {
06382       /*
06383         Undo the last image transformation.
06384       */
06385       if (undo_image == (Image *) NULL)
06386         {
06387           (void) XBell(display,0);
06388           return;
06389         }
06390       cache_image=undo_image;
06391       undo_image=GetPreviousImageInList(undo_image);
06392       windows->image.window_changes.width=(int) cache_image->columns;
06393       windows->image.window_changes.height=(int) cache_image->rows;
06394       if (windows->image.crop_geometry != (char *) NULL)
06395         windows->image.crop_geometry=(char *)
06396           RelinquishMagickMemory(windows->image.crop_geometry);
06397       windows->image.crop_geometry=cache_image->geometry;
06398       if (redo_image != (Image *) NULL)
06399         redo_image=DestroyImage(redo_image);
06400       redo_image=(*image);
06401       *image=cache_image->list;
06402       cache_image=DestroyImage(cache_image);
06403       if (windows->image.orphan != MagickFalse)
06404         return;
06405       XConfigureImageColormap(display,resource_info,windows,*image);
06406       (void) XConfigureImage(display,resource_info,windows,*image);
06407       return;
06408     }
06409     case CutCommand:
06410     case PasteCommand:
06411     case ApplyCommand:
06412     case HalfSizeCommand:
06413     case OriginalSizeCommand:
06414     case DoubleSizeCommand:
06415     case ResizeCommand:
06416     case TrimCommand:
06417     case CropCommand:
06418     case ChopCommand:
06419     case FlipCommand:
06420     case FlopCommand:
06421     case RotateRightCommand:
06422     case RotateLeftCommand:
06423     case RotateCommand:
06424     case ShearCommand:
06425     case RollCommand:
06426     case NegateCommand:
06427     case ContrastStretchCommand:
06428     case SigmoidalContrastCommand:
06429     case NormalizeCommand:
06430     case EqualizeCommand:
06431     case HueCommand:
06432     case SaturationCommand:
06433     case BrightnessCommand:
06434     case GammaCommand:
06435     case SpiffCommand:
06436     case DullCommand:
06437     case GrayscaleCommand:
06438     case MapCommand:
06439     case QuantizeCommand:
06440     case DespeckleCommand:
06441     case EmbossCommand:
06442     case ReduceNoiseCommand:
06443     case AddNoiseCommand:
06444     case SharpenCommand:
06445     case BlurCommand:
06446     case ThresholdCommand:
06447     case EdgeDetectCommand:
06448     case SpreadCommand:
06449     case ShadeCommand:
06450     case RaiseCommand:
06451     case SegmentCommand:
06452     case SolarizeCommand:
06453     case SepiaToneCommand:
06454     case SwirlCommand:
06455     case ImplodeCommand:
06456     case VignetteCommand:
06457     case WaveCommand:
06458     case OilPaintCommand:
06459     case CharcoalDrawCommand:
06460     case AnnotateCommand:
06461     case AddBorderCommand:
06462     case AddFrameCommand:
06463     case CompositeCommand:
06464     case CommentCommand:
06465     case LaunchCommand:
06466     case RegionofInterestCommand:
06467     case SaveToUndoBufferCommand:
06468     case RedoCommand:
06469     {
06470       Image
06471         *previous_image;
06472 
06473       long
06474         bytes;
06475 
06476       bytes=(long) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
06477       if (undo_image != (Image *) NULL)
06478         {
06479           /*
06480             Ensure the undo stash.has enough memory available.
06481           */
06482           previous_image=undo_image;
06483           while (previous_image != (Image *) NULL)
06484           {
06485             bytes+=previous_image->list->columns*previous_image->list->rows*
06486               sizeof(PixelPacket);
06487             if (bytes <= (long) (resource_info->undo_cache << 20))
06488               {
06489                 previous_image=GetPreviousImageInList(previous_image);
06490                 continue;
06491               }
06492             bytes-=previous_image->list->columns*previous_image->list->rows*
06493               sizeof(PixelPacket);
06494             if (previous_image == undo_image)
06495               undo_image=NewImageList();
06496             else
06497               previous_image->next->previous=NewImageList();
06498             break;
06499           }
06500           while (previous_image != (Image *) NULL)
06501           {
06502             /*
06503               Delete any excess memory from undo cache.
06504             */
06505             cache_image=previous_image;
06506             previous_image=GetPreviousImageInList(previous_image);
06507             cache_image->list=DestroyImage(cache_image->list);
06508             cache_image=DestroyImage(cache_image);
06509           }
06510         }
06511       if (bytes > (long) (resource_info->undo_cache << 20))
06512         break;
06513       /*
06514         Save image before transformations are applied.
06515       */
06516       cache_image=AcquireImage((ImageInfo *) NULL);
06517       if (cache_image == (Image *) NULL)
06518         break;
06519       XSetCursorState(display,windows,MagickTrue);
06520       XCheckRefreshWindows(display,windows);
06521       cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
06522       XSetCursorState(display,windows,MagickFalse);
06523       if (cache_image->list == (Image *) NULL)
06524         {
06525           cache_image=DestroyImage(cache_image);
06526           break;
06527         }
06528       cache_image->columns=(unsigned long) windows->image.ximage->width;
06529       cache_image->rows=(unsigned long) windows->image.ximage->height;
06530       cache_image->geometry=windows->image.crop_geometry;
06531       if (windows->image.crop_geometry != (char *) NULL)
06532         {
06533           cache_image->geometry=AcquireString((char *) NULL);
06534           (void) CopyMagickString(cache_image->geometry,
06535             windows->image.crop_geometry,MaxTextExtent);
06536         }
06537       if (undo_image == (Image *) NULL)
06538         {
06539           undo_image=cache_image;
06540           break;
06541         }
06542       undo_image->next=cache_image;
06543       undo_image->next->previous=undo_image;
06544       undo_image=undo_image->next;
06545       break;
06546     }
06547     default:
06548       break;
06549   }
06550   if (command == RedoCommand)
06551     {
06552       /*
06553         Redo the last image transformation.
06554       */
06555       if (redo_image == (Image *) NULL)
06556         {
06557           (void) XBell(display,0);
06558           return;
06559         }
06560       windows->image.window_changes.width=(int) redo_image->columns;
06561       windows->image.window_changes.height=(int) redo_image->rows;
06562       if (windows->image.crop_geometry != (char *) NULL)
06563         windows->image.crop_geometry=(char *)
06564           RelinquishMagickMemory(windows->image.crop_geometry);
06565       windows->image.crop_geometry=redo_image->geometry;
06566       *image=DestroyImage(*image);
06567       *image=redo_image;
06568       redo_image=NewImageList();
06569       if (windows->image.orphan != MagickFalse)
06570         return;
06571       XConfigureImageColormap(display,resource_info,windows,*image);
06572       (void) XConfigureImage(display,resource_info,windows,*image);
06573       return;
06574     }
06575   if (command != InfoCommand)
06576     return;
06577   /*
06578     Display image info.
06579   */
06580   XSetCursorState(display,windows,MagickTrue);
06581   XCheckRefreshWindows(display,windows);
06582   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
06583   XSetCursorState(display,windows,MagickFalse);
06584 }
06585 
06586 /*
06587 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
06588 %                                                                             %
06589 %                                                                             %
06590 %                                                                             %
06591 +   X I m a g e W i n d o w C o m m a n d                                     %
06592 %                                                                             %
06593 %                                                                             %
06594 %                                                                             %
06595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
06596 %
06597 %  XImageWindowCommand() makes a transform to the image or Image window as
06598 %  specified by a user menu button or keyboard command.
06599 %
06600 %  The format of the XMagickCommand method is:
06601 %
06602 %      CommandType XImageWindowCommand(Display *display,
06603 %        XResourceInfo *resource_info,XWindows *windows,
06604 %        const MagickStatusType state,KeySym key_symbol,Image **image)
06605 %
06606 %  A description of each parameter follows:
06607 %
06608 %    o nexus:  Method XImageWindowCommand returns an image when the
06609 %      user chooses 'Open Image' from the command menu.  Otherwise a null
06610 %      image is returned.
06611 %
06612 %    o display: Specifies a connection to an X server; returned from
06613 %      XOpenDisplay.
06614 %
06615 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
06616 %
06617 %    o windows: Specifies a pointer to a XWindows structure.
06618 %
06619 %    o state: key mask.
06620 %
06621 %    o key_symbol: Specifies a command to perform.
06622 %
06623 %    o image: the image;  XImageWIndowCommand
06624 %      may transform the image and return a new image pointer.
06625 %
06626 */
06627 static CommandType XImageWindowCommand(Display *display,
06628   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
06629   KeySym key_symbol,Image **image)
06630 {
06631   static char
06632     delta[MaxTextExtent] = "";
06633 
06634   static const char
06635     Digits[] = "01234567890";
06636 
06637   static KeySym
06638     last_symbol = XK_0;
06639 
06640   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
06641     {
06642       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
06643         {
06644           *delta='\0';
06645           resource_info->quantum=1;
06646         }
06647       last_symbol=key_symbol;
06648       delta[strlen(delta)+1]='\0';
06649       delta[strlen(delta)]=Digits[key_symbol-XK_0];
06650       resource_info->quantum=atoi(delta);
06651       return(NullCommand);
06652     }
06653   last_symbol=key_symbol;
06654   if (resource_info->immutable)
06655     {
06656       /*
06657         Virtual image window has a restricted command set.
06658       */
06659       switch (key_symbol)
06660       {
06661         case XK_question:
06662           return(InfoCommand);
06663         case XK_p:
06664         case XK_Print:
06665           return(PrintCommand);
06666         case XK_space:
06667           return(NextCommand);
06668         case XK_q:
06669         case XK_Escape:
06670           return(QuitCommand);
06671         default:
06672           break;
06673       }
06674       return(NullCommand);
06675     }
06676   switch ((int) key_symbol)
06677   {
06678     case XK_o:
06679     {
06680       if ((state & ControlMask) == 0)
06681         break;
06682       return(OpenCommand);
06683     }
06684     case XK_space:
06685       return(NextCommand);
06686     case XK_BackSpace:
06687       return(FormerCommand);
06688     case XK_s:
06689     {
06690       if ((state & Mod1Mask) != 0)
06691         return(SwirlCommand);
06692       if ((state & ControlMask) == 0)
06693         return(ShearCommand);
06694       return(SaveCommand);
06695     }
06696     case XK_p:
06697     case XK_Print:
06698     {
06699       if ((state & Mod1Mask) != 0)
06700         return(OilPaintCommand);
06701       if ((state & Mod4Mask) != 0)
06702         return(ColorCommand);
06703       if ((state & ControlMask) == 0)
06704         return(NullCommand);
06705       return(PrintCommand);
06706     }
06707     case XK_d:
06708     {
06709       if ((state & Mod4Mask) != 0)
06710         return(DrawCommand);
06711       if ((state & ControlMask) == 0)
06712         return(NullCommand);
06713       return(DeleteCommand);
06714     }
06715     case XK_Select:
06716     {
06717       if ((state & ControlMask) == 0)
06718         return(NullCommand);
06719       return(SelectCommand);
06720     }
06721     case XK_n:
06722     {
06723       if ((state & ControlMask) == 0)
06724         return(NullCommand);
06725       return(NewCommand);
06726     }
06727     case XK_q:
06728     case XK_Escape:
06729       return(QuitCommand);
06730     case XK_z:
06731     case XK_Undo:
06732     {
06733       if ((state & ControlMask) == 0)
06734         return(NullCommand);
06735       return(UndoCommand);
06736     }
06737     case XK_r:
06738     case XK_Redo:
06739     {
06740       if ((state & ControlMask) == 0)
06741         return(RollCommand);
06742       return(RedoCommand);
06743     }
06744     case XK_x:
06745     {
06746       if ((state & ControlMask) == 0)
06747         return(NullCommand);
06748       return(CutCommand);
06749     }
06750     case XK_c:
06751     {
06752       if ((state & Mod1Mask) != 0)
06753         return(CharcoalDrawCommand);
06754       if ((state & ControlMask) == 0)
06755         return(CropCommand);
06756       return(CopyCommand);
06757     }
06758     case XK_v:
06759     case XK_Insert:
06760     {
06761       if ((state & Mod4Mask) != 0)
06762         return(CompositeCommand);
06763       if ((state & ControlMask) == 0)
06764         return(FlipCommand);
06765       return(PasteCommand);
06766     }
06767     case XK_less:
06768       return(HalfSizeCommand);
06769     case XK_minus:
06770       return(OriginalSizeCommand);
06771     case XK_greater:
06772       return(DoubleSizeCommand);
06773     case XK_percent:
06774       return(ResizeCommand);
06775     case XK_at:
06776       return(RefreshCommand);
06777     case XK_bracketleft:
06778       return(ChopCommand);
06779     case XK_h:
06780       return(FlopCommand);
06781     case XK_slash:
06782       return(RotateRightCommand);
06783     case XK_backslash:
06784       return(RotateLeftCommand);
06785     case XK_asterisk:
06786       return(RotateCommand);
06787     case XK_t:
06788       return(TrimCommand);
06789     case XK_H:
06790       return(HueCommand);
06791     case XK_S:
06792       return(SaturationCommand);
06793     case XK_L:
06794       return(BrightnessCommand);
06795     case XK_G:
06796       return(GammaCommand);
06797     case XK_C:
06798       return(SpiffCommand);
06799     case XK_Z:
06800       return(DullCommand);
06801     case XK_N:
06802       return(NormalizeCommand);
06803     case XK_equal:
06804       return(EqualizeCommand);
06805     case XK_asciitilde:
06806       return(NegateCommand);
06807     case XK_period:
06808       return(GrayscaleCommand);
06809     case XK_numbersign:
06810       return(QuantizeCommand);
06811     case XK_F2:
06812       return(DespeckleCommand);
06813     case XK_F3:
06814       return(EmbossCommand);
06815     case XK_F4:
06816       return(ReduceNoiseCommand);
06817     case XK_F5:
06818       return(AddNoiseCommand);
06819     case XK_F6:
06820       return(SharpenCommand);
06821     case XK_F7:
06822       return(BlurCommand);
06823     case XK_F8:
06824       return(ThresholdCommand);
06825     case XK_F9:
06826       return(EdgeDetectCommand);
06827     case XK_F10:
06828       return(SpreadCommand);
06829     case XK_F11:
06830       return(ShadeCommand);
06831     case XK_F12:
06832       return(RaiseCommand);
06833     case XK_F13:
06834       return(SegmentCommand);
06835     case XK_i:
06836     {
06837       if ((state & Mod1Mask) == 0)
06838         return(NullCommand);
06839       return(ImplodeCommand);
06840     }
06841     case XK_w:
06842     {
06843       if ((state & Mod1Mask) == 0)
06844         return(NullCommand);
06845       return(WaveCommand);
06846     }
06847     case XK_m:
06848     {
06849       if ((state & Mod4Mask) == 0)
06850         return(NullCommand);
06851       return(MatteCommand);
06852     }
06853     case XK_b:
06854     {
06855       if ((state & Mod4Mask) == 0)
06856         return(NullCommand);
06857       return(AddBorderCommand);
06858     }
06859     case XK_f:
06860     {
06861       if ((state & Mod4Mask) == 0)
06862         return(NullCommand);
06863       return(AddFrameCommand);
06864     }
06865     case XK_exclam:
06866     {
06867       if ((state & Mod4Mask) == 0)
06868         return(NullCommand);
06869       return(CommentCommand);
06870     }
06871     case XK_a:
06872     {
06873       if ((state & Mod1Mask) != 0)
06874         return(ApplyCommand);
06875       if ((state & Mod4Mask) != 0)
06876         return(AnnotateCommand);
06877       if ((state & ControlMask) == 0)
06878         return(NullCommand);
06879       return(RegionofInterestCommand);
06880     }
06881     case XK_question:
06882       return(InfoCommand);
06883     case XK_plus:
06884       return(ZoomCommand);
06885     case XK_P:
06886     {
06887       if ((state & ShiftMask) == 0)
06888         return(NullCommand);
06889       return(ShowPreviewCommand);
06890     }
06891     case XK_Execute:
06892       return(LaunchCommand);
06893     case XK_F1:
06894       return(HelpCommand);
06895     case XK_Find:
06896       return(BrowseDocumentationCommand);
06897     case XK_Menu:
06898     {
06899       (void) XMapRaised(display,windows->command.id);
06900       return(NullCommand);
06901     }
06902     case XK_Next:
06903     case XK_Prior:
06904     case XK_Home:
06905     case XK_KP_Home:
06906     {
06907       XTranslateImage(display,windows,*image,key_symbol);
06908       return(NullCommand);
06909     }
06910     case XK_Up:
06911     case XK_KP_Up:
06912     case XK_Down:
06913     case XK_KP_Down:
06914     case XK_Left:
06915     case XK_KP_Left:
06916     case XK_Right:
06917     case XK_KP_Right:
06918     {
06919       if ((state & Mod1Mask) != 0)
06920         {
06921           RectangleInfo
06922             crop_info;
06923 
06924           /*
06925             Trim one pixel from edge of image.
06926           */
06927           crop_info.x=0;
06928           crop_info.y=0;
06929           crop_info.width=(unsigned long) windows->image.ximage->width;
06930           crop_info.height=(unsigned long) windows->image.ximage->height;
06931           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
06932             {
06933               if (resource_info->quantum >= (int) crop_info.height)
06934                 resource_info->quantum=(int) crop_info.height-1;
06935               crop_info.height-=resource_info->quantum;
06936             }
06937           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
06938             {
06939               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
06940                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
06941               crop_info.y+=resource_info->quantum;
06942               crop_info.height-=resource_info->quantum;
06943             }
06944           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
06945             {
06946               if (resource_info->quantum >= (int) crop_info.width)
06947                 resource_info->quantum=(int) crop_info.width-1;
06948               crop_info.width-=resource_info->quantum;
06949             }
06950           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
06951             {
06952               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
06953                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
06954               crop_info.x+=resource_info->quantum;
06955               crop_info.width-=resource_info->quantum;
06956             }
06957           if ((int) (windows->image.x+windows->image.width) >
06958               (int) crop_info.width)
06959             windows->image.x=(int) (crop_info.width-windows->image.width);
06960           if ((int) (windows->image.y+windows->image.height) >
06961               (int) crop_info.height)
06962             windows->image.y=(int) (crop_info.height-windows->image.height);
06963           XSetCropGeometry(display,windows,&crop_info,*image);
06964           windows->image.window_changes.width=(int) crop_info.width;
06965           windows->image.window_changes.height=(int) crop_info.height;
06966           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
06967           (void) XConfigureImage(display,resource_info,windows,*image);
06968           return(NullCommand);
06969         }
06970       XTranslateImage(display,windows,*image,key_symbol);
06971       return(NullCommand);
06972     }
06973     default:
06974       return(NullCommand);
06975   }
06976   return(NullCommand);
06977 }
06978 
06979 /*
06980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
06981 %                                                                             %
06982 %                                                                             %
06983 %                                                                             %
06984 +   X M a g i c k C o m m a n d                                               %
06985 %                                                                             %
06986 %                                                                             %
06987 %                                                                             %
06988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
06989 %
06990 %  XMagickCommand() makes a transform to the image or Image window as
06991 %  specified by a user menu button or keyboard command.
06992 %
06993 %  The format of the XMagickCommand method is:
06994 %
06995 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
06996 %        XWindows *windows,const CommandType command,Image **image)
06997 %
06998 %  A description of each parameter follows:
06999 %
07000 %    o nexus:  Method XMagickCommand returns an image when the
07001 %      user chooses 'Load Image' from the command menu.  Otherwise a null
07002 %      image is returned.
07003 %
07004 %    o display: Specifies a connection to an X server; returned from
07005 %      XOpenDisplay.
07006 %
07007 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
07008 %
07009 %    o windows: Specifies a pointer to a XWindows structure.
07010 %
07011 %    o command: Specifies a command to perform.
07012 %
07013 %    o image: the image;  XMagickCommand
07014 %      may transform the image and return a new image pointer.
07015 %
07016 */
07017 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
07018   XWindows *windows,const CommandType command,Image **image)
07019 {
07020   char
07021     filename[MaxTextExtent],
07022     geometry[MaxTextExtent],
07023     modulate_factors[MaxTextExtent];
07024 
07025   GeometryInfo
07026     geometry_info;
07027 
07028   Image
07029     *nexus;
07030 
07031   ImageInfo
07032     *image_info;
07033 
07034   int
07035     x,
07036     y;
07037 
07038   MagickStatusType
07039     flags,
07040     status;
07041 
07042   QuantizeInfo
07043     quantize_info;
07044 
07045   RectangleInfo
07046     page_geometry;
07047 
07048   register int
07049     i;
07050 
07051   static char
07052     color[MaxTextExtent] = "gray";
07053 
07054   unsigned int
07055     height,
07056     width;
07057 
07058   /*
07059     Process user command.
07060   */
07061   XCheckRefreshWindows(display,windows);
07062   XImageCache(display,resource_info,windows,command,image);
07063   nexus=NewImageList();
07064   windows->image.window_changes.width=windows->image.ximage->width;
07065   windows->image.window_changes.height=windows->image.ximage->height;
07066   image_info=CloneImageInfo(resource_info->image_info);
07067   SetGeometryInfo(&geometry_info);
07068   GetQuantizeInfo(&quantize_info);
07069   switch (command)
07070   {
07071     case OpenCommand:
07072     {
07073       /*
07074         Load image.
07075       */
07076       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
07077       break;
07078     }
07079     case NextCommand:
07080     {
07081       /*
07082         Display next image.
07083       */
07084       for (i=0; i < resource_info->quantum; i++)
07085         XClientMessage(display,windows->image.id,windows->im_protocols,
07086           windows->im_next_image,CurrentTime);
07087       break;
07088     }
07089     case FormerCommand:
07090     {
07091       /*
07092         Display former image.
07093       */
07094       for (i=0; i < resource_info->quantum; i++)
07095         XClientMessage(display,windows->image.id,windows->im_protocols,
07096           windows->im_former_image,CurrentTime);
07097       break;
07098     }
07099     case SelectCommand:
07100     {
07101       int
07102         status;
07103 
07104       /*
07105         Select image.
07106       */
07107       status=chdir(resource_info->home_directory);
07108       if (status == -1)
07109         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
07110           FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
07111       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
07112       break;
07113     }
07114     case SaveCommand:
07115     {
07116       /*
07117         Save image.
07118       */
07119       status=XSaveImage(display,resource_info,windows,*image);
07120       if (status == MagickFalse)
07121         {
07122           XNoticeWidget(display,windows,"Unable to write X image:",
07123             (*image)->filename);
07124           break;
07125         }
07126       break;
07127     }
07128     case PrintCommand:
07129     {
07130       /*
07131         Print image.
07132       */
07133       status=XPrintImage(display,resource_info,windows,*image);
07134       if (status == MagickFalse)
07135         {
07136           XNoticeWidget(display,windows,"Unable to print X image:",
07137             (*image)->filename);
07138           break;
07139         }
07140       break;
07141     }
07142     case DeleteCommand:
07143     {
07144       static char
07145         filename[MaxTextExtent] = "\0";
07146 
07147       /*
07148         Delete image file.
07149       */
07150       XFileBrowserWidget(display,windows,"Delete",filename);
07151       if (*filename == '\0')
07152         break;
07153       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
07154       if (status != MagickFalse)
07155         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
07156       break;
07157     }
07158     case NewCommand:
07159     {
07160       int
07161         status;
07162 
07163       static char
07164         color[MaxTextExtent] = "gray",
07165         geometry[MaxTextExtent] = "640x480";
07166 
07167       static const char
07168         *format = "gradient";
07169 
07170       /*
07171         Query user for canvas geometry.
07172       */
07173       status=XDialogWidget(display,windows,"New","Enter image geometry:",
07174         geometry);
07175       if (*geometry == '\0')
07176         break;
07177       if (status == 0)
07178         format="xc";
07179       XColorBrowserWidget(display,windows,"Select",color);
07180       if (*color == '\0')
07181         break;
07182       /*
07183         Create canvas.
07184       */
07185       (void) FormatMagickString(image_info->filename,MaxTextExtent,
07186         "%s:%s",format,color);
07187       (void) CloneString(&image_info->size,geometry);
07188       nexus=ReadImage(image_info,&(*image)->exception);
07189       CatchException(&(*image)->exception);
07190       XClientMessage(display,windows->image.id,windows->im_protocols,
07191         windows->im_next_image,CurrentTime);
07192       break;
07193     }
07194     case VisualDirectoryCommand:
07195     {
07196       /*
07197         Visual Image directory.
07198       */
07199       nexus=XVisualDirectoryImage(display,resource_info,windows);
07200       break;
07201     }
07202     case QuitCommand:
07203     {
07204       /*
07205         exit program.
07206       */
07207       if (resource_info->confirm_exit == MagickFalse)
07208         XClientMessage(display,windows->image.id,windows->im_protocols,
07209           windows->im_exit,CurrentTime);
07210       else
07211         {
07212           int
07213             status;
07214 
07215           /*
07216             Confirm program exit.
07217           */
07218           status=XConfirmWidget(display,windows,"Do you really want to exit",
07219             resource_info->client_name);
07220           if (status > 0)
07221             XClientMessage(display,windows->image.id,windows->im_protocols,
07222               windows->im_exit,CurrentTime);
07223         }
07224       break;
07225     }
07226     case CutCommand:
07227     {
07228       /*
07229         Cut image.
07230       */
07231       (void) XCropImage(display,resource_info,windows,*image,CutMode);
07232       break;
07233     }
07234     case CopyCommand:
07235     {
07236       /*
07237         Copy image.
07238       */
07239       (void) XCropImage(display,resource_info,windows,*image,CopyMode);
07240       break;
07241     }
07242     case PasteCommand:
07243     {
07244       /*
07245         Paste image.
07246       */
07247       status=XPasteImage(display,resource_info,windows,*image);
07248       if (status == MagickFalse)
07249         {
07250           XNoticeWidget(display,windows,"Unable to paste X image",
07251             (*image)->filename);
07252           break;
07253         }
07254       break;
07255     }
07256     case HalfSizeCommand:
07257     {
07258       /*
07259         Half image size.
07260       */
07261       windows->image.window_changes.width=windows->image.ximage->width/2;
07262       windows->image.window_changes.height=windows->image.ximage->height/2;
07263       (void) XConfigureImage(display,resource_info,windows,*image);
07264       break;
07265     }
07266     case OriginalSizeCommand:
07267     {
07268       /*
07269         Original image size.
07270       */
07271       windows->image.window_changes.width=(int) (*image)->columns;
07272       windows->image.window_changes.height=(int) (*image)->rows;
07273       (void) XConfigureImage(display,resource_info,windows,*image);
07274       break;
07275     }
07276     case DoubleSizeCommand:
07277     {
07278       /*
07279         Double the image size.
07280       */
07281       windows->image.window_changes.width=windows->image.ximage->width << 1;
07282       windows->image.window_changes.height=windows->image.ximage->height << 1;
07283       (void) XConfigureImage(display,resource_info,windows,*image);
07284       break;
07285     }
07286     case ResizeCommand:
07287     {
07288       int
07289         status;
07290 
07291       long
07292         x,
07293         y;
07294 
07295       unsigned long
07296         height,
07297         width;
07298 
07299       /*
07300         Resize image.
07301       */
07302       width=(unsigned long) windows->image.ximage->width;
07303       height=(unsigned long) windows->image.ximage->height;
07304       x=0;
07305       y=0;
07306       (void) FormatMagickString(geometry,MaxTextExtent,"%lux%lu+0+0",
07307         width,height);
07308       status=XDialogWidget(display,windows,"Resize",
07309         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
07310       if (*geometry == '\0')
07311         break;
07312       if (status == 0)
07313         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
07314       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
07315       windows->image.window_changes.width=(int) width;
07316       windows->image.window_changes.height=(int) height;
07317       (void) XConfigureImage(display,resource_info,windows,*image);
07318       break;
07319     }
07320     case ApplyCommand:
07321     {
07322       char
07323         image_geometry[MaxTextExtent];
07324 
07325       if ((windows->image.crop_geometry == (char *) NULL) &&
07326           ((int) (*image)->columns == windows->image.ximage->width) &&
07327           ((int) (*image)->rows == windows->image.ximage->height))
07328         break;
07329       /*
07330         Apply size transforms to image.
07331       */
07332       XSetCursorState(display,windows,MagickTrue);
07333       XCheckRefreshWindows(display,windows);
07334       /*
07335         Crop and/or scale displayed image.
07336       */
07337       (void) FormatMagickString(image_geometry,MaxTextExtent,"%dx%d!",
07338         windows->image.ximage->width,windows->image.ximage->height);
07339       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
07340       if (windows->image.crop_geometry != (char *) NULL)
07341         windows->image.crop_geometry=(char *)
07342           RelinquishMagickMemory(windows->image.crop_geometry);
07343       windows->image.x=0;
07344       windows->image.y=0;
07345       XConfigureImageColormap(display,resource_info,windows,*image);
07346       (void) XConfigureImage(display,resource_info,windows,*image);
07347       break;
07348     }
07349     case RefreshCommand:
07350     {
07351       (void) XConfigureImage(display,resource_info,windows,*image);
07352       break;
07353     }
07354     case RestoreCommand:
07355     {
07356       /*
07357         Restore Image window to its original size.
07358       */
07359       if ((windows->image.width == (unsigned int) (*image)->columns) &&
07360           (windows->image.height == (unsigned int) (*image)->rows) &&
07361           (windows->image.crop_geometry == (char *) NULL))
07362         {
07363           (void) XBell(display,0);
07364           break;
07365         }
07366       windows->image.window_changes.width=(int) (*image)->columns;
07367       windows->image.window_changes.height=(int) (*image)->rows;
07368       if (windows->image.crop_geometry != (char *) NULL)
07369         {
07370           windows->image.crop_geometry=(char *)
07371             RelinquishMagickMemory(windows->image.crop_geometry);
07372           windows->image.crop_geometry=(char *) NULL;
07373           windows->image.x=0;
07374           windows->image.y=0;
07375         }
07376       XConfigureImageColormap(display,resource_info,windows,*image);
07377       (void) XConfigureImage(display,resource_info,windows,*image);
07378       break;
07379     }
07380     case CropCommand:
07381     {
07382       /*
07383         Crop image.
07384       */
07385       (void) XCropImage(display,resource_info,windows,*image,CropMode);
07386       break;
07387     }
07388     case ChopCommand:
07389     {
07390       /*
07391         Chop image.
07392       */
07393       status=XChopImage(display,resource_info,windows,image);
07394       if (status == MagickFalse)
07395         {
07396           XNoticeWidget(display,windows,"Unable to cut X image",
07397             (*image)->filename);
07398           break;
07399         }
07400       break;
07401     }
07402     case FlopCommand:
07403     {
07404       Image
07405         *flop_image;
07406 
07407       /*
07408         Flop image scanlines.
07409       */
07410       XSetCursorState(display,windows,MagickTrue);
07411       XCheckRefreshWindows(display,windows);
07412       flop_image=FlopImage(*image,&(*image)->exception);
07413       if (flop_image != (Image *) NULL)
07414         {
07415           *image=DestroyImage(*image);
07416           *image=flop_image;
07417         }
07418       CatchException(&(*image)->exception);
07419       XSetCursorState(display,windows,MagickFalse);
07420       if (windows->image.crop_geometry != (char *) NULL)
07421         {
07422           /*
07423             Flop crop geometry.
07424           */
07425           width=(unsigned int) (*image)->columns;
07426           height=(unsigned int) (*image)->rows;
07427           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
07428             &width,&height);
07429           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
07430             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
07431         }
07432       if (windows->image.orphan != MagickFalse)
07433         break;
07434       (void) XConfigureImage(display,resource_info,windows,*image);
07435       break;
07436     }
07437     case FlipCommand:
07438     {
07439       Image
07440         *flip_image;
07441 
07442       /*
07443         Flip image scanlines.
07444       */
07445       XSetCursorState(display,windows,MagickTrue);
07446       XCheckRefreshWindows(display,windows);
07447       flip_image=FlipImage(*image,&(*image)->exception);
07448       if (flip_image != (Image *) NULL)
07449         {
07450           *image=DestroyImage(*image);
07451           *image=flip_image;
07452         }
07453       CatchException(&(*image)->exception);
07454       XSetCursorState(display,windows,MagickFalse);
07455       if (windows->image.crop_geometry != (char *) NULL)
07456         {
07457           /*
07458             Flip crop geometry.
07459           */
07460           width=(unsigned int) (*image)->columns;
07461           height=(unsigned int) (*image)->rows;
07462           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
07463             &width,&height);
07464           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
07465             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
07466         }
07467       if (windows->image.orphan != MagickFalse)
07468         break;
07469       (void) XConfigureImage(display,resource_info,windows,*image);
07470       break;
07471     }
07472     case RotateRightCommand:
07473     {
07474       /*
07475         Rotate image 90 degrees clockwise.
07476       */
07477       status=XRotateImage(display,resource_info,windows,90.0,image);
07478       if (status == MagickFalse)
07479         {
07480           XNoticeWidget(display,windows,"Unable to rotate X image",
07481             (*image)->filename);
07482           break;
07483         }
07484       break;
07485     }
07486     case RotateLeftCommand:
07487     {
07488       /*
07489         Rotate image 90 degrees counter-clockwise.
07490       */
07491       status=XRotateImage(display,resource_info,windows,-90.0,image);
07492       if (status == MagickFalse)
07493         {
07494           XNoticeWidget(display,windows,"Unable to rotate X image",
07495             (*image)->filename);
07496           break;
07497         }
07498       break;
07499     }
07500     case RotateCommand:
07501     {
07502       /*
07503         Rotate image.
07504       */
07505       status=XRotateImage(display,resource_info,windows,0.0,image);
07506       if (status == MagickFalse)
07507         {
07508           XNoticeWidget(display,windows,"Unable to rotate X image",
07509             (*image)->filename);
07510           break;
07511         }
07512       break;
07513     }
07514     case ShearCommand:
07515     {
07516       Image
07517         *shear_image;
07518 
07519       static char
07520         geometry[MaxTextExtent] = "45.0x45.0";
07521 
07522       /*
07523         Query user for shear color and geometry.
07524       */
07525       XColorBrowserWidget(display,windows,"Select",color);
07526       if (*color == '\0')
07527         break;
07528       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
07529         geometry);
07530       if (*geometry == '\0')
07531         break;
07532       /*
07533         Shear image.
07534       */
07535       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
07536       XSetCursorState(display,windows,MagickTrue);
07537       XCheckRefreshWindows(display,windows);
07538       (void) QueryColorDatabase(color,&(*image)->background_color,
07539         &(*image)->exception);
07540       flags=ParseGeometry(geometry,&geometry_info);
07541       if ((flags & SigmaValue) == 0)
07542         geometry_info.sigma=geometry_info.rho;
07543       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
07544         &(*image)->exception);
07545       if (shear_image != (Image *) NULL)
07546         {
07547           *image=DestroyImage(*image);
07548           *image=shear_image;
07549         }
07550       CatchException(&(*image)->exception);
07551       XSetCursorState(display,windows,MagickFalse);
07552       if (windows->image.orphan != MagickFalse)
07553         break;
07554       windows->image.window_changes.width=(int) (*image)->columns;
07555       windows->image.window_changes.height=(int) (*image)->rows;
07556       XConfigureImageColormap(display,resource_info,windows,*image);
07557       (void) XConfigureImage(display,resource_info,windows,*image);
07558       break;
07559     }
07560     case RollCommand:
07561     {
07562       Image
07563         *roll_image;
07564 
07565       static char
07566         geometry[MaxTextExtent] = "+2+2";
07567 
07568       /*
07569         Query user for the roll geometry.
07570       */
07571       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
07572         geometry);
07573       if (*geometry == '\0')
07574         break;
07575       /*
07576         Roll image.
07577       */
07578       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
07579       XSetCursorState(display,windows,MagickTrue);
07580       XCheckRefreshWindows(display,windows);
07581       (void) ParsePageGeometry(*image,geometry,&page_geometry,
07582         &(*image)->exception);
07583       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
07584         &(*image)->exception);
07585       if (roll_image != (Image *) NULL)
07586         {
07587           *image=DestroyImage(*image);
07588           *image=roll_image;
07589         }
07590       CatchException(&(*image)->exception);
07591       XSetCursorState(display,windows,MagickFalse);
07592       if (windows->image.orphan != MagickFalse)
07593         break;
07594       windows->image.window_changes.width=(int) (*image)->columns;
07595       windows->image.window_changes.height=(int) (*image)->rows;
07596       XConfigureImageColormap(display,resource_info,windows,*image);
07597       (void) XConfigureImage(display,resource_info,windows,*image);
07598       break;
07599     }
07600     case TrimCommand:
07601     {
07602       static char
07603         fuzz[MaxTextExtent];
07604 
07605       /*
07606         Query user for the fuzz factor.
07607       */
07608       (void) FormatMagickString(fuzz,MaxTextExtent,"%g%%",100.0*(*image)->fuzz/
07609         (QuantumRange+1.0));
07610       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
07611       if (*fuzz == '\0')
07612         break;
07613       (*image)->fuzz=StringToDouble(fuzz,(double) QuantumRange+1.0);
07614       /*
07615         Trim image.
07616       */
07617       status=XTrimImage(display,resource_info,windows,*image);
07618       if (status == MagickFalse)
07619         {
07620           XNoticeWidget(display,windows,"Unable to trim X image",
07621             (*image)->filename);
07622           break;
07623         }
07624       break;
07625     }
07626     case HueCommand:
07627     {
07628       static char
07629         hue_percent[MaxTextExtent] = "110";
07630 
07631       /*
07632         Query user for percent hue change.
07633       */
07634       (void) XDialogWidget(display,windows,"Apply",
07635         "Enter percent change in image hue (0-200):",hue_percent);
07636       if (*hue_percent == '\0')
07637         break;
07638       /*
07639         Vary the image hue.
07640       */
07641       XSetCursorState(display,windows,MagickTrue);
07642       XCheckRefreshWindows(display,windows);
07643       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
07644       (void) ConcatenateMagickString(modulate_factors,hue_percent,
07645         MaxTextExtent);
07646       (void) ModulateImage(*image,modulate_factors);
07647       XSetCursorState(display,windows,MagickFalse);
07648       if (windows->image.orphan != MagickFalse)
07649         break;
07650       XConfigureImageColormap(display,resource_info,windows,*image);
07651       (void) XConfigureImage(display,resource_info,windows,*image);
07652       break;
07653     }
07654     case SaturationCommand:
07655     {
07656       static char
07657         saturation_percent[MaxTextExtent] = "110";
07658 
07659       /*
07660         Query user for percent saturation change.
07661       */
07662       (void) XDialogWidget(display,windows,"Apply",
07663         "Enter percent change in color saturation (0-200):",saturation_percent);
07664       if (*saturation_percent == '\0')
07665         break;
07666       /*
07667         Vary color saturation.
07668       */
07669       XSetCursorState(display,windows,MagickTrue);
07670       XCheckRefreshWindows(display,windows);
07671       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
07672       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
07673         MaxTextExtent);
07674       (void) ModulateImage(*image,modulate_factors);
07675       XSetCursorState(display,windows,MagickFalse);
07676       if (windows->image.orphan != MagickFalse)
07677         break;
07678       XConfigureImageColormap(display,resource_info,windows,*image);
07679       (void) XConfigureImage(display,resource_info,windows,*image);
07680       break;
07681     }
07682     case BrightnessCommand:
07683     {
07684       static char
07685         brightness_percent[MaxTextExtent] = "110";
07686 
07687       /*
07688         Query user for percent brightness change.
07689       */
07690       (void) XDialogWidget(display,windows,"Apply",
07691         "Enter percent change in color brightness (0-200):",brightness_percent);
07692       if (*brightness_percent == '\0')
07693         break;
07694       /*
07695         Vary the color brightness.
07696       */
07697       XSetCursorState(display,windows,MagickTrue);
07698       XCheckRefreshWindows(display,windows);
07699       (void) CopyMagickString(modulate_factors,brightness_percent,
07700         MaxTextExtent);
07701       (void) ModulateImage(*image,modulate_factors);
07702       XSetCursorState(display,windows,MagickFalse);
07703       if (windows->image.orphan != MagickFalse)
07704         break;
07705       XConfigureImageColormap(display,resource_info,windows,*image);
07706       (void) XConfigureImage(display,resource_info,windows,*image);
07707       break;
07708     }
07709     case GammaCommand:
07710     {
07711       static char
07712         factor[MaxTextExtent] = "1.6";
07713 
07714       /*
07715         Query user for gamma value.
07716       */
07717       (void) XDialogWidget(display,windows,"Gamma",
07718         "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
07719       if (*factor == '\0')
07720         break;
07721       /*
07722         Gamma correct image.
07723       */
07724       XSetCursorState(display,windows,MagickTrue);
07725       XCheckRefreshWindows(display,windows);
07726       (void) GammaImage(*image,factor);
07727       XSetCursorState(display,windows,MagickFalse);
07728       if (windows->image.orphan != MagickFalse)
07729         break;
07730       XConfigureImageColormap(display,resource_info,windows,*image);
07731       (void) XConfigureImage(display,resource_info,windows,*image);
07732       break;
07733     }
07734     case SpiffCommand:
07735     {
07736       /*
07737         Sharpen the image contrast.
07738       */
07739       XSetCursorState(display,windows,MagickTrue);
07740       XCheckRefreshWindows(display,windows);
07741       (void) ContrastImage(*image,MagickTrue);
07742       XSetCursorState(display,windows,MagickFalse);
07743       if (windows->image.orphan != MagickFalse)
07744         break;
07745       XConfigureImageColormap(display,resource_info,windows,*image);
07746       (void) XConfigureImage(display,resource_info,windows,*image);
07747       break;
07748     }
07749     case DullCommand:
07750     {
07751       /*
07752         Dull the image contrast.
07753       */
07754       XSetCursorState(display,windows,MagickTrue);
07755       XCheckRefreshWindows(display,windows);
07756       (void) ContrastImage(*image,MagickFalse);
07757       XSetCursorState(display,windows,MagickFalse);
07758       if (windows->image.orphan != MagickFalse)
07759         break;
07760       XConfigureImageColormap(display,resource_info,windows,*image);
07761       (void) XConfigureImage(display,resource_info,windows,*image);
07762       break;
07763     }
07764     case ContrastStretchCommand:
07765     {
07766       double
07767         black_point,
07768         white_point;
07769 
07770       static char
07771         levels[MaxTextExtent] = "1%";
07772 
07773       /*
07774         Query user for gamma value.
07775       */
07776       (void) XDialogWidget(display,windows,"Contrast Stretch",
07777         "Enter black and white points:",levels);
07778       if (*levels == '\0')
07779         break;
07780       /*
07781         Contrast stretch image.
07782       */
07783       XSetCursorState(display,windows,MagickTrue);
07784       XCheckRefreshWindows(display,windows);
07785       flags=ParseGeometry(levels,&geometry_info);
07786       black_point=geometry_info.rho;
07787       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
07788       if ((flags & PercentValue) != 0)
07789         {
07790           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
07791           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
07792         }
07793       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
07794       (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
07795         white_point);
07796       XSetCursorState(display,windows,MagickFalse);
07797       if (windows->image.orphan != MagickFalse)
07798         break;
07799       XConfigureImageColormap(display,resource_info,windows,*image);
07800       (void) XConfigureImage(display,resource_info,windows,*image);
07801       break;
07802     }
07803     case SigmoidalContrastCommand:
07804     {
07805       static char
07806         levels[MaxTextExtent] = "3x50%";
07807 
07808       /*
07809         Query user for gamma value.
07810       */
07811       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
07812         "Enter contrast and midpoint:",levels);
07813       if (*levels == '\0')
07814         break;
07815       /*
07816         Contrast stretch image.
07817       */
07818       XSetCursorState(display,windows,MagickTrue);
07819       XCheckRefreshWindows(display,windows);
07820       (void) SigmoidalContrastImage(*image,MagickTrue,levels);
07821       XSetCursorState(display,windows,MagickFalse);
07822       if (windows->image.orphan != MagickFalse)
07823         break;
07824       XConfigureImageColormap(display,resource_info,windows,*image);
07825       (void) XConfigureImage(display,resource_info,windows,*image);
07826       break;
07827     }
07828     case NormalizeCommand:
07829     {
07830       /*
07831         Perform histogram normalization on the image.
07832       */
07833       XSetCursorState(display,windows,MagickTrue);
07834       XCheckRefreshWindows(display,windows);
07835       (void) NormalizeImage(*image);
07836       XSetCursorState(display,windows,MagickFalse);
07837       if (windows->image.orphan != MagickFalse)
07838         break;
07839       XConfigureImageColormap(display,resource_info,windows,*image);
07840       (void) XConfigureImage(display,resource_info,windows,*image);
07841       break;
07842     }
07843     case EqualizeCommand:
07844     {
07845       /*
07846         Perform histogram equalization on the image.
07847       */
07848       XSetCursorState(display,windows,MagickTrue);
07849       XCheckRefreshWindows(display,windows);
07850       (void) EqualizeImage(*image);
07851       XSetCursorState(display,windows,MagickFalse);
07852       if (windows->image.orphan != MagickFalse)
07853         break;
07854       XConfigureImageColormap(display,resource_info,windows,*image);
07855       (void) XConfigureImage(display,resource_info,windows,*image);
07856       break;
07857     }
07858     case NegateCommand:
07859     {
07860       /*
07861         Negate colors in image.
07862       */
07863       XSetCursorState(display,windows,MagickTrue);
07864       XCheckRefreshWindows(display,windows);
07865       (void) NegateImage(*image,MagickFalse);
07866       XSetCursorState(display,windows,MagickFalse);
07867       if (windows->image.orphan != MagickFalse)
07868         break;
07869       XConfigureImageColormap(display,resource_info,windows,*image);
07870       (void) XConfigureImage(display,resource_info,windows,*image);
07871       break;
07872     }
07873     case GrayscaleCommand:
07874     {
07875       /*
07876         Convert image to grayscale.
07877       */
07878       XSetCursorState(display,windows,MagickTrue);
07879       XCheckRefreshWindows(display,windows);
07880       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
07881         GrayscaleType : GrayscaleMatteType);
07882       XSetCursorState(display,windows,MagickFalse);
07883       if (windows->image.orphan != MagickFalse)
07884         break;
07885       XConfigureImageColormap(display,resource_info,windows,*image);
07886       (void) XConfigureImage(display,resource_info,windows,*image);
07887       break;
07888     }
07889     case MapCommand:
07890     {
07891       Image
07892         *affinity_image;
07893 
07894       static char
07895         filename[MaxTextExtent] = "\0";
07896 
07897       /*
07898         Request image file name from user.
07899       */
07900       XFileBrowserWidget(display,windows,"Map",filename);
07901       if (*filename == '\0')
07902         break;
07903       /*
07904         Map image.
07905       */
07906       XSetCursorState(display,windows,MagickTrue);
07907       XCheckRefreshWindows(display,windows);
07908       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
07909       affinity_image=ReadImage(image_info,&(*image)->exception);
07910       if (affinity_image != (Image *) NULL)
07911         {
07912           (void) RemapImage(&quantize_info,*image,affinity_image);
07913           affinity_image=DestroyImage(affinity_image);
07914         }
07915       CatchException(&(*image)->exception);
07916       XSetCursorState(display,windows,MagickFalse);
07917       if (windows->image.orphan != MagickFalse)
07918         break;
07919       XConfigureImageColormap(display,resource_info,windows,*image);
07920       (void) XConfigureImage(display,resource_info,windows,*image);
07921       break;
07922     }
07923     case QuantizeCommand:
07924     {
07925       int
07926         status;
07927 
07928       static char
07929         colors[MaxTextExtent] = "256";
07930 
07931       /*
07932         Query user for maximum number of colors.
07933       */
07934       status=XDialogWidget(display,windows,"Quantize",
07935         "Maximum number of colors:",colors);
07936       if (*colors == '\0')
07937         break;
07938       /*
07939         Color reduce the image.
07940       */
07941       XSetCursorState(display,windows,MagickTrue);
07942       XCheckRefreshWindows(display,windows);
07943       quantize_info.number_colors=(unsigned long) atol(colors);
07944       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
07945       (void) QuantizeImage(&quantize_info,*image);
07946       XSetCursorState(display,windows,MagickFalse);
07947       if (windows->image.orphan != MagickFalse)
07948         break;
07949       XConfigureImageColormap(display,resource_info,windows,*image);
07950       (void) XConfigureImage(display,resource_info,windows,*image);
07951       break;
07952     }
07953     case DespeckleCommand:
07954     {
07955       Image
07956         *despeckle_image;
07957 
07958       /*
07959         Despeckle image.
07960       */
07961       XSetCursorState(display,windows,MagickTrue);
07962       XCheckRefreshWindows(display,windows);
07963       despeckle_image=DespeckleImage(*image,&(*image)->exception);
07964       if (despeckle_image != (Image *) NULL)
07965         {
07966           *image=DestroyImage(*image);
07967           *image=despeckle_image;
07968         }
07969       CatchException(&(*image)->exception);
07970       XSetCursorState(display,windows,MagickFalse);
07971       if (windows->image.orphan != MagickFalse)
07972         break;
07973       XConfigureImageColormap(display,resource_info,windows,*image);
07974       (void) XConfigureImage(display,resource_info,windows,*image);
07975       break;
07976     }
07977     case EmbossCommand:
07978     {
07979       Image
07980         *emboss_image;
07981 
07982       static char
07983         radius[MaxTextExtent] = "0.0x1.0";
07984 
07985       /*
07986         Query user for emboss radius.
07987       */
07988       (void) XDialogWidget(display,windows,"Emboss",
07989         "Enter the emboss radius and standard deviation:",radius);
07990       if (*radius == '\0')
07991         break;
07992       /*
07993         Reduce noise in the image.
07994       */
07995       XSetCursorState(display,windows,MagickTrue);
07996       XCheckRefreshWindows(display,windows);
07997       flags=ParseGeometry(radius,&geometry_info);
07998       if ((flags & SigmaValue) == 0)
07999         geometry_info.sigma=1.0;
08000       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
08001         &(*image)->exception);
08002       if (emboss_image != (Image *) NULL)
08003         {
08004           *image=DestroyImage(*image);
08005           *image=emboss_image;
08006         }
08007       CatchException(&(*image)->exception);
08008       XSetCursorState(display,windows,MagickFalse);
08009       if (windows->image.orphan != MagickFalse)
08010         break;
08011       XConfigureImageColormap(display,resource_info,windows,*image);
08012       (void) XConfigureImage(display,resource_info,windows,*image);
08013       break;
08014     }
08015     case ReduceNoiseCommand:
08016     {
08017       Image
08018         *noise_image;
08019 
08020       static char
08021         radius[MaxTextExtent] = "0";
08022 
08023       /*
08024         Query user for noise radius.
08025       */
08026       (void) XDialogWidget(display,windows,"Reduce Noise",
08027         "Enter the noise radius:",radius);
08028       if (*radius == '\0')
08029         break;
08030       /*
08031         Reduce noise in the image.
08032       */
08033       XSetCursorState(display,windows,MagickTrue);
08034       XCheckRefreshWindows(display,windows);
08035       flags=ParseGeometry(radius,&geometry_info);
08036       noise_image=ReduceNoiseImage(*image,geometry_info.rho,
08037         &(*image)->exception);
08038       if (noise_image != (Image *) NULL)
08039         {
08040           *image=DestroyImage(*image);
08041           *image=noise_image;
08042         }
08043       CatchException(&(*image)->exception);
08044       XSetCursorState(display,windows,MagickFalse);
08045       if (windows->image.orphan != MagickFalse)
08046         break;
08047       XConfigureImageColormap(display,resource_info,windows,*image);
08048       (void) XConfigureImage(display,resource_info,windows,*image);
08049       break;
08050     }
08051     case AddNoiseCommand:
08052     {
08053       char
08054         **noises;
08055 
08056       Image
08057         *noise_image;
08058 
08059       static char
08060         noise_type[MaxTextExtent] = "Gaussian";
08061 
08062       /*
08063         Add noise to the image.
08064       */
08065       noises=GetMagickOptions(MagickNoiseOptions);
08066       if (noises == (char **) NULL)
08067         break;
08068       XListBrowserWidget(display,windows,&windows->widget,
08069         (const char **) noises,"Add Noise",
08070         "Select a type of noise to add to your image:",noise_type);
08071       noises=DestroyStringList(noises);
08072       if (*noise_type == '\0')
08073         break;
08074       XSetCursorState(display,windows,MagickTrue);
08075       XCheckRefreshWindows(display,windows);
08076       noise_image=AddNoiseImage(*image,(NoiseType) ParseMagickOption(
08077         MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
08078       if (noise_image != (Image *) NULL)
08079         {
08080           *image=DestroyImage(*image);
08081           *image=noise_image;
08082         }
08083       CatchException(&(*image)->exception);
08084       XSetCursorState(display,windows,MagickFalse);
08085       if (windows->image.orphan != MagickFalse)
08086         break;
08087       XConfigureImageColormap(display,resource_info,windows,*image);
08088       (void) XConfigureImage(display,resource_info,windows,*image);
08089       break;
08090     }
08091     case SharpenCommand:
08092     {
08093       Image
08094         *sharp_image;
08095 
08096       static char
08097         radius[MaxTextExtent] = "0.0x1.0";
08098 
08099       /*
08100         Query user for sharpen radius.
08101       */
08102       (void) XDialogWidget(display,windows,"Sharpen",
08103         "Enter the sharpen radius and standard deviation:",radius);
08104       if (*radius == '\0')
08105         break;
08106       /*
08107         Sharpen image scanlines.
08108       */
08109       XSetCursorState(display,windows,MagickTrue);
08110       XCheckRefreshWindows(display,windows);
08111       flags=ParseGeometry(radius,&geometry_info);
08112       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
08113         &(*image)->exception);
08114       if (sharp_image != (Image *) NULL)
08115         {
08116           *image=DestroyImage(*image);
08117           *image=sharp_image;
08118         }
08119       CatchException(&(*image)->exception);
08120       XSetCursorState(display,windows,MagickFalse);
08121       if (windows->image.orphan != MagickFalse)
08122         break;
08123       XConfigureImageColormap(display,resource_info,windows,*image);
08124       (void) XConfigureImage(display,resource_info,windows,*image);
08125       break;
08126     }
08127     case BlurCommand:
08128     {
08129       Image
08130         *blur_image;
08131 
08132       static char
08133         radius[MaxTextExtent] = "0.0x1.0";
08134 
08135       /*
08136         Query user for blur radius.
08137       */
08138       (void) XDialogWidget(display,windows,"Blur",
08139         "Enter the blur radius and standard deviation:",radius);
08140       if (*radius == '\0')
08141         break;
08142       /*
08143         Blur an image.
08144       */
08145       XSetCursorState(display,windows,MagickTrue);
08146       XCheckRefreshWindows(display,windows);
08147       flags=ParseGeometry(radius,&geometry_info);
08148       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
08149         &(*image)->exception);
08150       if (blur_image != (Image *) NULL)
08151         {
08152           *image=DestroyImage(*image);
08153           *image=blur_image;
08154         }
08155       CatchException(&(*image)->exception);
08156       XSetCursorState(display,windows,MagickFalse);
08157       if (windows->image.orphan != MagickFalse)
08158         break;
08159       XConfigureImageColormap(display,resource_info,windows,*image);
08160       (void) XConfigureImage(display,resource_info,windows,*image);
08161       break;
08162     }
08163     case ThresholdCommand:
08164     {
08165       double
08166         threshold;
08167 
08168       static char
08169         factor[MaxTextExtent] = "128";
08170 
08171       /*
08172         Query user for threshold value.
08173       */
08174       (void) XDialogWidget(display,windows,"Threshold",
08175         "Enter threshold value:",factor);
08176       if (*factor == '\0')
08177         break;
08178       /*
08179         Gamma correct image.
08180       */
08181       XSetCursorState(display,windows,MagickTrue);
08182       XCheckRefreshWindows(display,windows);
08183       threshold=StringToDouble(factor,QuantumRange);
08184       (void) BilevelImage(*image,threshold);
08185       XSetCursorState(display,windows,MagickFalse);
08186       if (windows->image.orphan != MagickFalse)
08187         break;
08188       XConfigureImageColormap(display,resource_info,windows,*image);
08189       (void) XConfigureImage(display,resource_info,windows,*image);
08190       break;
08191     }
08192     case EdgeDetectCommand:
08193     {
08194       Image
08195         *edge_image;
08196 
08197       static char
08198         radius[MaxTextExtent] = "0";
08199 
08200       /*
08201         Query user for edge factor.
08202       */
08203       (void) XDialogWidget(display,windows,"Detect Edges",
08204         "Enter the edge detect radius:",radius);
08205       if (*radius == '\0')
08206         break;
08207       /*
08208         Detect edge in image.
08209       */
08210       XSetCursorState(display,windows,MagickTrue);
08211       XCheckRefreshWindows(display,windows);
08212       flags=ParseGeometry(radius,&geometry_info);
08213       edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
08214       if (edge_image != (Image *) NULL)
08215         {
08216           *image=DestroyImage(*image);
08217           *image=edge_image;
08218         }
08219       CatchException(&(*image)->exception);
08220       XSetCursorState(display,windows,MagickFalse);
08221       if (windows->image.orphan != MagickFalse)
08222         break;
08223       XConfigureImageColormap(display,resource_info,windows,*image);
08224       (void) XConfigureImage(display,resource_info,windows,*image);
08225       break;
08226     }
08227     case SpreadCommand:
08228     {
08229       Image
08230         *spread_image;
08231 
08232       static char
08233         amount[MaxTextExtent] = "2";
08234 
08235       /*
08236         Query user for spread amount.
08237       */
08238       (void) XDialogWidget(display,windows,"Spread",
08239         "Enter the displacement amount:",amount);
08240       if (*amount == '\0')
08241         break;
08242       /*
08243         Displace image pixels by a random amount.
08244       */
08245       XSetCursorState(display,windows,MagickTrue);
08246       XCheckRefreshWindows(display,windows);
08247       flags=ParseGeometry(amount,&geometry_info);
08248       spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
08249       if (spread_image != (Image *) NULL)
08250         {
08251           *image=DestroyImage(*image);
08252           *image=spread_image;
08253         }
08254       CatchException(&(*image)->exception);
08255       XSetCursorState(display,windows,MagickFalse);
08256       if (windows->image.orphan != MagickFalse)
08257         break;
08258       XConfigureImageColormap(display,resource_info,windows,*image);
08259       (void) XConfigureImage(display,resource_info,windows,*image);
08260       break;
08261     }
08262     case ShadeCommand:
08263     {
08264       Image
08265         *shade_image;
08266 
08267       int
08268         status;
08269 
08270       static char
08271         geometry[MaxTextExtent] = "30x30";
08272 
08273       /*
08274         Query user for the shade geometry.
08275       */
08276       status=XDialogWidget(display,windows,"Shade",
08277         "Enter the azimuth and elevation of the light source:",geometry);
08278       if (*geometry == '\0')
08279         break;
08280       /*
08281         Shade image pixels.
08282       */
08283       XSetCursorState(display,windows,MagickTrue);
08284       XCheckRefreshWindows(display,windows);
08285       flags=ParseGeometry(geometry,&geometry_info);
08286       if ((flags & SigmaValue) == 0)
08287         geometry_info.sigma=1.0;
08288       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
08289         geometry_info.rho,geometry_info.sigma,&(*image)->exception);
08290       if (shade_image != (Image *) NULL)
08291         {
08292           *image=DestroyImage(*image);
08293           *image=shade_image;
08294         }
08295       CatchException(&(*image)->exception);
08296       XSetCursorState(display,windows,MagickFalse);
08297       if (windows->image.orphan != MagickFalse)
08298         break;
08299       XConfigureImageColormap(display,resource_info,windows,*image);
08300       (void) XConfigureImage(display,resource_info,windows,*image);
08301       break;
08302     }
08303     case RaiseCommand:
08304     {
08305       static char
08306         bevel_width[MaxTextExtent] = "10";
08307 
08308       /*
08309         Query user for bevel width.
08310       */
08311       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
08312       if (*bevel_width == '\0')
08313         break;
08314       /*
08315         Raise an image.
08316       */
08317       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
08318       XSetCursorState(display,windows,MagickTrue);
08319       XCheckRefreshWindows(display,windows);
08320       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
08321         &(*image)->exception);
08322       (void) RaiseImage(*image,&page_geometry,MagickTrue);
08323       XSetCursorState(display,windows,MagickFalse);
08324       if (windows->image.orphan != MagickFalse)
08325         break;
08326       XConfigureImageColormap(display,resource_info,windows,*image);
08327       (void) XConfigureImage(display,resource_info,windows,*image);
08328       break;
08329     }
08330     case SegmentCommand:
08331     {
08332       static char
08333         threshold[MaxTextExtent] = "1.0x1.5";
08334 
08335       /*
08336         Query user for smoothing threshold.
08337       */
08338       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
08339         threshold);
08340       if (*threshold == '\0')
08341         break;
08342       /*
08343         Segment an image.
08344       */
08345       XSetCursorState(display,windows,MagickTrue);
08346       XCheckRefreshWindows(display,windows);
08347       flags=ParseGeometry(threshold,&geometry_info);
08348       if ((flags & SigmaValue) == 0)
08349         geometry_info.sigma=1.0;
08350       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
08351         geometry_info.sigma);
08352       XSetCursorState(display,windows,MagickFalse);
08353       if (windows->image.orphan != MagickFalse)
08354         break;
08355       XConfigureImageColormap(display,resource_info,windows,*image);
08356       (void) XConfigureImage(display,resource_info,windows,*image);
08357       break;
08358     }
08359     case SepiaToneCommand:
08360     {
08361       double
08362         threshold;
08363 
08364       Image
08365         *sepia_image;
08366 
08367       static char
08368         factor[MaxTextExtent] = "80%";
08369 
08370       /*
08371         Query user for sepia-tone factor.
08372       */
08373       (void) XDialogWidget(display,windows,"Sepia Tone",
08374         "Enter the sepia tone factor (0 - 99.9%):",factor);
08375       if (*factor == '\0')
08376         break;
08377       /*
08378         Sepia tone image pixels.
08379       */
08380       XSetCursorState(display,windows,MagickTrue);
08381       XCheckRefreshWindows(display,windows);
08382       threshold=StringToDouble(factor,QuantumRange);
08383       sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
08384       if (sepia_image != (Image *) NULL)
08385         {
08386           *image=DestroyImage(*image);
08387           *image=sepia_image;
08388         }
08389       CatchException(&(*image)->exception);
08390       XSetCursorState(display,windows,MagickFalse);
08391       if (windows->image.orphan != MagickFalse)
08392         break;
08393       XConfigureImageColormap(display,resource_info,windows,*image);
08394       (void) XConfigureImage(display,resource_info,windows,*image);
08395       break;
08396     }
08397     case SolarizeCommand:
08398     {
08399       double
08400         threshold;
08401 
08402       static char
08403         factor[MaxTextExtent] = "60%";
08404 
08405       /*
08406         Query user for solarize factor.
08407       */
08408       (void) XDialogWidget(display,windows,"Solarize",
08409         "Enter the solarize factor (0 - 99.9%):",factor);
08410       if (*factor == '\0')
08411         break;
08412       /*
08413         Solarize image pixels.
08414       */
08415       XSetCursorState(display,windows,MagickTrue);
08416       XCheckRefreshWindows(display,windows);
08417       threshold=StringToDouble(factor,QuantumRange);
08418       (void) SolarizeImage(*image,threshold);
08419       XSetCursorState(display,windows,MagickFalse);
08420       if (windows->image.orphan != MagickFalse)
08421         break;
08422       XConfigureImageColormap(display,resource_info,windows,*image);
08423       (void) XConfigureImage(display,resource_info,windows,*image);
08424       break;
08425     }
08426     case SwirlCommand:
08427     {
08428       Image
08429         *swirl_image;
08430 
08431       static char
08432         degrees[MaxTextExtent] = "60";
08433 
08434       /*
08435         Query user for swirl angle.
08436       */
08437       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
08438         degrees);
08439       if (*degrees == '\0')
08440         break;
08441       /*
08442         Swirl image pixels about the center.
08443       */
08444       XSetCursorState(display,windows,MagickTrue);
08445       XCheckRefreshWindows(display,windows);
08446       flags=ParseGeometry(degrees,&geometry_info);
08447       swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
08448       if (swirl_image != (Image *) NULL)
08449         {
08450           *image=DestroyImage(*image);
08451           *image=swirl_image;
08452         }
08453       CatchException(&(*image)->exception);
08454       XSetCursorState(display,windows,MagickFalse);
08455       if (windows->image.orphan != MagickFalse)
08456         break;
08457       XConfigureImageColormap(display,resource_info,windows,*image);
08458       (void) XConfigureImage(display,resource_info,windows,*image);
08459       break;
08460     }
08461     case ImplodeCommand:
08462     {
08463       Image
08464         *implode_image;
08465 
08466       static char
08467         factor[MaxTextExtent] = "0.3";
08468 
08469       /*
08470         Query user for implode factor.
08471       */
08472       (void) XDialogWidget(display,windows,"Implode",
08473         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
08474       if (*factor == '\0')
08475         break;
08476       /*
08477         Implode image pixels about the center.
08478       */
08479       XSetCursorState(display,windows,MagickTrue);
08480       XCheckRefreshWindows(display,windows);
08481       flags=ParseGeometry(factor,&geometry_info);
08482       implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
08483       if (implode_image != (Image *) NULL)
08484         {
08485           *image=DestroyImage(*image);
08486           *image=implode_image;
08487         }
08488       CatchException(&(*image)->exception);
08489       XSetCursorState(display,windows,MagickFalse);
08490       if (windows->image.orphan != MagickFalse)
08491         break;
08492       XConfigureImageColormap(display,resource_info,windows,*image);
08493       (void) XConfigureImage(display,resource_info,windows,*image);
08494       break;
08495     }
08496     case VignetteCommand:
08497     {
08498       Image
08499         *vignette_image;
08500 
08501       static char
08502         geometry[MaxTextExtent] = "0x20";
08503 
08504       /*
08505         Query user for the vignette geometry.
08506       */
08507       (void) XDialogWidget(display,windows,"Vignette",
08508         "Enter the radius, sigma, and x and y offsets:",geometry);
08509       if (*geometry == '\0')
08510         break;
08511       /*
08512         Soften the edges of the image in vignette style
08513       */
08514       XSetCursorState(display,windows,MagickTrue);
08515       XCheckRefreshWindows(display,windows);
08516       flags=ParseGeometry(geometry,&geometry_info);
08517       if ((flags & SigmaValue) == 0)
08518         geometry_info.sigma=1.0;
08519       if ((flags & XiValue) == 0)
08520         geometry_info.xi=0.1*(*image)->columns;
08521       if ((flags & PsiValue) == 0)
08522         geometry_info.psi=0.1*(*image)->rows;
08523       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
08524         (long) (geometry_info.xi+0.5),(long) (geometry_info.psi+0.5),
08525         &(*image)->exception);
08526       if (vignette_image != (Image *) NULL)
08527         {
08528           *image=DestroyImage(*image);
08529           *image=vignette_image;
08530         }
08531       CatchException(&(*image)->exception);
08532       XSetCursorState(display,windows,MagickFalse);
08533       if (windows->image.orphan != MagickFalse)
08534         break;
08535       XConfigureImageColormap(display,resource_info,windows,*image);
08536       (void) XConfigureImage(display,resource_info,windows,*image);
08537       break;
08538     }
08539     case WaveCommand:
08540     {
08541       Image
08542         *wave_image;
08543 
08544       static char
08545         geometry[MaxTextExtent] = "25x150";
08546 
08547       /*
08548         Query user for the wave geometry.
08549       */
08550       (void) XDialogWidget(display,windows,"Wave",
08551         "Enter the amplitude and length of the wave:",geometry);
08552       if (*geometry == '\0')
08553         break;
08554       /*
08555         Alter an image along a sine wave.
08556       */
08557       XSetCursorState(display,windows,MagickTrue);
08558       XCheckRefreshWindows(display,windows);
08559       flags=ParseGeometry(geometry,&geometry_info);
08560       if ((flags & SigmaValue) == 0)
08561         geometry_info.sigma=1.0;
08562       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
08563         &(*image)->exception);
08564       if (wave_image != (Image *) NULL)
08565         {
08566           *image=DestroyImage(*image);
08567           *image=wave_image;
08568         }
08569       CatchException(&(*image)->exception);
08570       XSetCursorState(display,windows,MagickFalse);
08571       if (windows->image.orphan != MagickFalse)
08572         break;
08573       XConfigureImageColormap(display,resource_info,windows,*image);
08574       (void) XConfigureImage(display,resource_info,windows,*image);
08575       break;
08576     }
08577     case OilPaintCommand:
08578     {
08579       Image
08580         *paint_image;
08581 
08582       static char
08583         radius[MaxTextExtent] = "0";
08584 
08585       /*
08586         Query user for circular neighborhood radius.
08587       */
08588       (void) XDialogWidget(display,windows,"Oil Paint",
08589         "Enter the mask radius:",radius);
08590       if (*radius == '\0')
08591         break;
08592       /*
08593         OilPaint image scanlines.
08594       */
08595       XSetCursorState(display,windows,MagickTrue);
08596       XCheckRefreshWindows(display,windows);
08597       flags=ParseGeometry(radius,&geometry_info);
08598       paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
08599       if (paint_image != (Image *) NULL)
08600         {
08601           *image=DestroyImage(*image);
08602           *image=paint_image;
08603         }
08604       CatchException(&(*image)->exception);
08605       XSetCursorState(display,windows,MagickFalse);
08606       if (windows->image.orphan != MagickFalse)
08607         break;
08608       XConfigureImageColormap(display,resource_info,windows,*image);
08609       (void) XConfigureImage(display,resource_info,windows,*image);
08610       break;
08611     }
08612     case CharcoalDrawCommand:
08613     {
08614       Image
08615         *charcoal_image;
08616 
08617       static char
08618         radius[MaxTextExtent] = "0x1";
08619 
08620       /*
08621         Query user for charcoal radius.
08622       */
08623       (void) XDialogWidget(display,windows,"Charcoal Draw",
08624         "Enter the charcoal radius and sigma:",radius);
08625       if (*radius == '\0')
08626         break;
08627       /*
08628         Charcoal the image.
08629       */
08630       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
08631       XSetCursorState(display,windows,MagickTrue);
08632       XCheckRefreshWindows(display,windows);
08633       flags=ParseGeometry(radius,&geometry_info);
08634       if ((flags & SigmaValue) == 0)
08635         geometry_info.sigma=geometry_info.rho;
08636       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
08637         &(*image)->exception);
08638       if (charcoal_image != (Image *) NULL)
08639         {
08640           *image=DestroyImage(*image);
08641           *image=charcoal_image;
08642         }
08643       CatchException(&(*image)->exception);
08644       XSetCursorState(display,windows,MagickFalse);
08645       if (windows->image.orphan != MagickFalse)
08646         break;
08647       XConfigureImageColormap(display,resource_info,windows,*image);
08648       (void) XConfigureImage(display,resource_info,windows,*image);
08649       break;
08650     }
08651     case AnnotateCommand:
08652     {
08653       /*
08654         Annotate the image with text.
08655       */
08656       status=XAnnotateEditImage(display,resource_info,windows,*image);
08657       if (status == MagickFalse)
08658         {
08659           XNoticeWidget(display,windows,"Unable to annotate X image",
08660             (*image)->filename);
08661           break;
08662         }
08663       break;
08664     }
08665     case DrawCommand:
08666     {
08667       /*
08668         Draw image.
08669       */
08670       status=XDrawEditImage(display,resource_info,windows,image);
08671       if (status == MagickFalse)
08672         {
08673           XNoticeWidget(display,windows,"Unable to draw on the X image",
08674             (*image)->filename);
08675           break;
08676         }
08677       break;
08678     }
08679     case ColorCommand:
08680     {
08681       /*
08682         Color edit.
08683       */
08684       status=XColorEditImage(display,resource_info,windows,image);
08685       if (status == MagickFalse)
08686         {
08687           XNoticeWidget(display,windows,"Unable to pixel edit X image",
08688             (*image)->filename);
08689           break;
08690         }
08691       break;
08692     }
08693     case MatteCommand:
08694     {
08695       /*
08696         Matte edit.
08697       */
08698       status=XMatteEditImage(display,resource_info,windows,image);
08699       if (status == MagickFalse)
08700         {
08701           XNoticeWidget(display,windows,"Unable to matte edit X image",
08702             (*image)->filename);
08703           break;
08704         }
08705       break;
08706     }
08707     case CompositeCommand:
08708     {
08709       /*
08710         Composite image.
08711       */
08712       status=XCompositeImage(display,resource_info,windows,*image);
08713       if (status == MagickFalse)
08714         {
08715           XNoticeWidget(display,windows,"Unable to composite X image",
08716             (*image)->filename);
08717           break;
08718         }
08719       break;
08720     }
08721     case AddBorderCommand:
08722     {
08723       Image
08724         *border_image;
08725 
08726       static char
08727         geometry[MaxTextExtent] = "6x6";
08728 
08729       /*
08730         Query user for border color and geometry.
08731       */
08732       XColorBrowserWidget(display,windows,"Select",color);
08733       if (*color == '\0')
08734         break;
08735       (void) XDialogWidget(display,windows,"Add Border",
08736         "Enter border geometry:",geometry);
08737       if (*geometry == '\0')
08738         break;
08739       /*
08740         Add a border to the image.
08741       */
08742       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
08743       XSetCursorState(display,windows,MagickTrue);
08744       XCheckRefreshWindows(display,windows);
08745       (void) QueryColorDatabase(color,&(*image)->border_color,
08746         &(*image)->exception);
08747       (void) ParsePageGeometry(*image,geometry,&page_geometry,
08748         &(*image)->exception);
08749       border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
08750       if (border_image != (Image *) NULL)
08751         {
08752           *image=DestroyImage(*image);
08753           *image=border_image;
08754         }
08755       CatchException(&(*image)->exception);
08756       XSetCursorState(display,windows,MagickFalse);
08757       if (windows->image.orphan != MagickFalse)
08758         break;
08759       windows->image.window_changes.width=(int) (*image)->columns;
08760       windows->image.window_changes.height=(int) (*image)->rows;
08761       XConfigureImageColormap(display,resource_info,windows,*image);
08762       (void) XConfigureImage(display,resource_info,windows,*image);
08763       break;
08764     }
08765     case AddFrameCommand:
08766     {
08767       FrameInfo
08768         frame_info;
08769 
08770       Image
08771         *frame_image;
08772 
08773       static char
08774         geometry[MaxTextExtent] = "6x6";
08775 
08776       /*
08777         Query user for frame color and geometry.
08778       */
08779       XColorBrowserWidget(display,windows,"Select",color);
08780       if (*color == '\0')
08781         break;
08782       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
08783         geometry);
08784       if (*geometry == '\0')
08785         break;
08786       /*
08787         Surround image with an ornamental border.
08788       */
08789       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
08790       XSetCursorState(display,windows,MagickTrue);
08791       XCheckRefreshWindows(display,windows);
08792       (void) QueryColorDatabase(color,&(*image)->matte_color,
08793         &(*image)->exception);
08794       (void) ParsePageGeometry(*image,geometry,&page_geometry,
08795         &(*image)->exception);
08796       frame_info.width=page_geometry.width;
08797       frame_info.height=page_geometry.height;
08798       frame_info.outer_bevel=page_geometry.x;
08799       frame_info.inner_bevel=page_geometry.y;
08800       frame_info.x=(long) frame_info.width;
08801       frame_info.y=(long) frame_info.height;
08802       frame_info.width=(*image)->columns+2*frame_info.width;
08803       frame_info.height=(*image)->rows+2*frame_info.height;
08804       frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
08805       if (frame_image != (Image *) NULL)
08806         {
08807           *image=DestroyImage(*image);
08808           *image=frame_image;
08809         }
08810       CatchException(&(*image)->exception);
08811       XSetCursorState(display,windows,MagickFalse);
08812       if (windows->image.orphan != MagickFalse)
08813         break;
08814       windows->image.window_changes.width=(int) (*image)->columns;
08815       windows->image.window_changes.height=(int) (*image)->rows;
08816       XConfigureImageColormap(display,resource_info,windows,*image);
08817       (void) XConfigureImage(display,resource_info,windows,*image);
08818       break;
08819     }
08820     case CommentCommand:
08821     {
08822       const char
08823         *value;
08824 
08825       FILE
08826         *file;
08827 
08828       int
08829         unique_file;
08830 
08831       /*
08832         Edit image comment.
08833       */
08834       unique_file=AcquireUniqueFileResource(image_info->filename);
08835       if (unique_file == -1)
08836         XNoticeWidget(display,windows,"Unable to edit image comment",
08837           image_info->filename);
08838       value=GetImageProperty(*image,"comment");
08839       if (value == (char *) NULL)
08840         unique_file=close(unique_file)-1;
08841       else
08842         {
08843           register const char
08844             *p;
08845 
08846           file=fdopen(unique_file,"w");
08847           if (file == (FILE *) NULL)
08848             {
08849               XNoticeWidget(display,windows,"Unable to edit image comment",
08850                 image_info->filename);
08851               break;
08852             }
08853           for (p=value; *p != '\0'; p++)
08854             (void) fputc((int) *p,file);
08855           (void) fputc('\n',file);
08856           (void) fclose(file);
08857         }
08858       XSetCursorState(display,windows,MagickTrue);
08859       XCheckRefreshWindows(display,windows);
08860       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
08861         &(*image)->exception);
08862       if (status == MagickFalse)
08863         XNoticeWidget(display,windows,"Unable to edit image comment",
08864           (char *) NULL);
08865       else
08866         {
08867           char
08868             *comment;
08869 
08870           comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
08871           if (comment != (char *) NULL)
08872             {
08873               (void) SetImageProperty(*image,"comment",comment);
08874               (*image)->taint=MagickTrue;
08875             }
08876         }
08877       (void) RelinquishUniqueFileResource(image_info->filename);
08878       XSetCursorState(display,windows,MagickFalse);
08879       break;
08880     }
08881     case LaunchCommand:
08882     {
08883       /*
08884         Launch program.
08885       */
08886       XSetCursorState(display,windows,MagickTrue);
08887       XCheckRefreshWindows(display,windows);
08888       (void) AcquireUniqueFilename(filename);
08889       (void) FormatMagickString((*image)->filename,MaxTextExtent,"launch:%s",
08890         filename);
08891       status=WriteImage(image_info,*image);
08892       if (status == MagickFalse)
08893         XNoticeWidget(display,windows,"Unable to launch image editor",
08894           (char *) NULL);
08895       else
08896         {
08897           nexus=ReadImage(resource_info->image_info,&(*image)->exception);
08898           CatchException(&(*image)->exception);
08899           XClientMessage(display,windows->image.id,windows->im_protocols,
08900             windows->im_next_image,CurrentTime);
08901         }
08902       (void) RelinquishUniqueFileResource(filename);
08903       XSetCursorState(display,windows,MagickFalse);
08904       break;
08905     }
08906     case RegionofInterestCommand:
08907     {
08908       /*
08909         Apply an image processing technique to a region of interest.
08910       */
08911       (void) XROIImage(display,resource_info,windows,image);
08912       break;
08913     }
08914     case InfoCommand:
08915       break;
08916     case ZoomCommand:
08917     {
08918       /*
08919         Zoom image.
08920       */
08921       if (windows->magnify.mapped != MagickFalse)
08922         (void) XRaiseWindow(display,windows->magnify.id);
08923       else
08924         {
08925           /*
08926             Make magnify image.
08927           */
08928           XSetCursorState(display,windows,MagickTrue);
08929           (void) XMapRaised(display,windows->magnify.id);
08930           XSetCursorState(display,windows,MagickFalse);
08931         }
08932       break;
08933     }
08934     case ShowPreviewCommand:
08935     {
08936       char
08937         **previews;
08938 
08939       static char
08940         preview_type[MaxTextExtent] = "Gamma";
08941 
08942       /*
08943         Select preview type from menu.
08944       */
08945       previews=GetMagickOptions(MagickPreviewOptions);
08946       if (previews == (char **) NULL)
08947         break;
08948       XListBrowserWidget(display,windows,&windows->widget,
08949         (const char **) previews,"Preview",
08950         "Select an enhancement, effect, or F/X:",preview_type);
08951       previews=DestroyStringList(previews);
08952       if (*preview_type == '\0')
08953         break;
08954       /*
08955         Show image preview.
08956       */
08957       XSetCursorState(display,windows,MagickTrue);
08958       XCheckRefreshWindows(display,windows);
08959       image_info->preview_type=(PreviewType)
08960         ParseMagickOption(MagickPreviewOptions,MagickFalse,preview_type);
08961       image_info->group=(long) windows->image.id;
08962       (void) DeleteImageProperty(*image,"label");
08963       (void) SetImageProperty(*image,"label","Preview");
08964       (void) AcquireUniqueFilename(filename);
08965       (void) FormatMagickString((*image)->filename,MaxTextExtent,"preview:%s",
08966         filename);
08967       status=WriteImage(image_info,*image);
08968       (void) FormatMagickString((*image)->filename,MaxTextExtent,"show:%s",
08969         filename);
08970       status=WriteImage(image_info,*image);
08971       if (status == MagickFalse)
08972         XNoticeWidget(display,windows,"Unable to show image preview",
08973           (*image)->filename);
08974       XDelay(display,1500);
08975       XSetCursorState(display,windows,MagickFalse);
08976       break;
08977     }
08978     case ShowHistogramCommand:
08979     {
08980       /*
08981         Show image histogram.
08982       */
08983       XSetCursorState(display,windows,MagickTrue);
08984       XCheckRefreshWindows(display,windows);
08985       image_info->group=(long) windows->image.id;
08986       (void) DeleteImageProperty(*image,"label");
08987       (void) SetImageProperty(*image,"label","Histogram");
08988       (void) AcquireUniqueFilename(filename);
08989       (void) FormatMagickString((*image)->filename,MaxTextExtent,"histogram:%s",
08990         filename);
08991       status=WriteImage(image_info,*image);
08992       (void) FormatMagickString((*image)->filename,MaxTextExtent,"show:%s",
08993         filename);
08994       status=WriteImage(image_info,*image);
08995       if (status == MagickFalse)
08996         XNoticeWidget(display,windows,"Unable to show histogram",
08997           (*image)->filename);
08998       XDelay(display,1500);
08999       XSetCursorState(display,windows,MagickFalse);
09000       break;
09001     }
09002     case ShowMatteCommand:
09003     {
09004       if ((*image)->matte == MagickFalse)
09005         {
09006           XNoticeWidget(display,windows,
09007             "Image does not have any matte information",(*image)->filename);
09008           break;
09009         }
09010       /*
09011         Show image matte.
09012       */
09013       XSetCursorState(display,windows,MagickTrue);
09014       XCheckRefreshWindows(display,windows);
09015       image_info->group=(long) windows->image.id;
09016       (void) DeleteImageProperty(*image,"label");
09017       (void) SetImageProperty(*image,"label","Matte");
09018       (void) AcquireUniqueFilename(filename);
09019       (void) FormatMagickString((*image)->filename,MaxTextExtent,"matte:%s",
09020         filename);
09021       status=WriteImage(image_info,*image);
09022       (void) FormatMagickString((*image)->filename,MaxTextExtent,"show:%s",
09023         filename);
09024       status=WriteImage(image_info,*image);
09025       if (status == MagickFalse)
09026         XNoticeWidget(display,windows,"Unable to show matte",
09027           (*image)->filename);
09028       XDelay(display,1500);
09029       XSetCursorState(display,windows,MagickFalse);
09030       break;
09031     }
09032     case BackgroundCommand:
09033     {
09034       /*
09035         Background image.
09036       */
09037       status=XBackgroundImage(display,resource_info,windows,image);
09038       if (status == MagickFalse)
09039         break;
09040       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
09041       if (nexus != (Image *) NULL)
09042         XClientMessage(display,windows->image.id,windows->im_protocols,
09043           windows->im_next_image,CurrentTime);
09044       break;
09045     }
09046     case SlideShowCommand:
09047     {
09048       static char
09049         delay[MaxTextExtent] = "5";
09050 
09051       /*
09052         Display next image after pausing.
09053       */
09054       (void) XDialogWidget(display,windows,"Slide Show",
09055         "Pause how many 1/100ths of a second between images:",delay);
09056       if (*delay == '\0')
09057         break;
09058       resource_info->delay=(unsigned long) atol(delay);
09059       XClientMessage(display,windows->image.id,windows->im_protocols,
09060         windows->im_next_image,CurrentTime);
09061       break;
09062     }
09063     case PreferencesCommand:
09064     {
09065       /*
09066         Set user preferences.
09067       */
09068       status=XPreferencesWidget(display,resource_info,windows);
09069       if (status == MagickFalse)
09070         break;
09071       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
09072       if (nexus != (Image *) NULL)
09073         XClientMessage(display,windows->image.id,windows->im_protocols,
09074           windows->im_next_image,CurrentTime);
09075       break;
09076     }
09077     case HelpCommand:
09078     {
09079       /*
09080         User requested help.
09081       */
09082       XTextViewWidget(display,resource_info,windows,MagickFalse,
09083         "Help Viewer - Display",DisplayHelp);
09084       break;
09085     }
09086     case BrowseDocumentationCommand:
09087     {
09088       Atom
09089         mozilla_atom;
09090 
09091       Window
09092         mozilla_window,
09093         root_window;
09094 
09095       /*
09096         Browse the ImageMagick documentation.
09097       */
09098       root_window=XRootWindow(display,XDefaultScreen(display));
09099       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
09100       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
09101       if (mozilla_window != (Window) NULL)
09102         {
09103           char
09104             command[MaxTextExtent],
09105             *url;
09106 
09107           /*
09108             Display documentation using Netscape remote control.
09109           */
09110           url=GetMagickHomeURL();
09111           (void) FormatMagickString(command,MaxTextExtent,
09112             "openurl(%s,new-tab)",url);
09113           url=DestroyString(url);
09114           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
09115           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
09116             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
09117           XSetCursorState(display,windows,MagickFalse);
09118           break;
09119         }
09120       XSetCursorState(display,windows,MagickTrue);
09121       XCheckRefreshWindows(display,windows);
09122       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
09123         &(*image)->exception);
09124       if (status == MagickFalse)
09125         XNoticeWidget(display,windows,"Unable to browse documentation",
09126           (char *) NULL);
09127       XDelay(display,1500);
09128       XSetCursorState(display,windows,MagickFalse);
09129       break;
09130     }
09131     case VersionCommand:
09132     {
09133       XNoticeWidget(display,windows,GetMagickVersion((unsigned long *) NULL),
09134         GetMagickCopyright());
09135       break;
09136     }
09137     case SaveToUndoBufferCommand:
09138       break;
09139     default:
09140     {
09141       (void) XBell(display,0);
09142       break;
09143     }
09144   }
09145   image_info=DestroyImageInfo(image_info);
09146   return(nexus);
09147 }
09148 
09149 /*
09150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
09151 %                                                                             %
09152 %                                                                             %
09153 %                                                                             %
09154 +   X M a g n i f y I m a g e                                                 %
09155 %                                                                             %
09156 %                                                                             %
09157 %                                                                             %
09158 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
09159 %
09160 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
09161 %  The magnified portion is displayed in a separate window.
09162 %
09163 %  The format of the XMagnifyImage method is:
09164 %
09165 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
09166 %
09167 %  A description of each parameter follows:
09168 %
09169 %    o display: Specifies a connection to an X server;  returned from
09170 %      XOpenDisplay.
09171 %
09172 %    o windows: Specifies a pointer to a XWindows structure.
09173 %
09174 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
09175 %      the entire image is refreshed.
09176 %
09177 */
09178 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
09179 {
09180   char
09181     text[MaxTextExtent];
09182 
09183   register int
09184     x,
09185     y;
09186 
09187   unsigned long
09188     state;
09189 
09190   /*
09191     Update magnified image until the mouse button is released.
09192   */
09193   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
09194   state=DefaultState;
09195   x=event->xbutton.x;
09196   y=event->xbutton.y;
09197   windows->magnify.x=windows->image.x+x;
09198   windows->magnify.y=windows->image.y+y;
09199   do
09200   {
09201     /*
09202       Map and unmap Info widget as text cursor crosses its boundaries.
09203     */
09204     if (windows->info.mapped != MagickFalse)
09205       {
09206         if ((x < (int) (windows->info.x+windows->info.width)) &&
09207             (y < (int) (windows->info.y+windows->info.height)))
09208           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
09209       }
09210     else
09211       if ((x > (int) (windows->info.x+windows->info.width)) ||
09212           (y > (int) (windows->info.y+windows->info.height)))
09213         (void) XMapWindow(display,windows->info.id);
09214     if (windows->info.mapped != MagickFalse)
09215       {
09216         /*
09217           Display pointer position.
09218         */
09219         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
09220           windows->magnify.x,windows->magnify.y);
09221         XInfoWidget(display,windows,text);
09222       }
09223     /*
09224       Wait for next event.
09225     */
09226     XScreenEvent(display,windows,event);
09227     switch (event->type)
09228     {
09229       case ButtonPress:
09230         break;
09231       case ButtonRelease:
09232       {
09233         /*
09234           User has finished magnifying image.
09235         */
09236         x=event->xbutton.x;
09237         y=event->xbutton.y;
09238         state|=ExitState;
09239         break;
09240       }
09241       case Expose:
09242         break;
09243       case MotionNotify:
09244       {
09245         x=event->xmotion.x;
09246         y=event->xmotion.y;
09247         break;
09248       }
09249       default:
09250         break;
09251     }
09252     /*
09253       Check boundary conditions.
09254     */
09255     if (x < 0)
09256       x=0;
09257     else
09258       if (x >= (int) windows->image.width)
09259         x=(int) windows->image.width-1;
09260     if (y < 0)
09261       y=0;
09262     else
09263      if (y >= (int) windows->image.height)
09264        y=(int) windows->image.height-1;
09265   } while ((state & ExitState) == 0);
09266   /*
09267     Display magnified image.
09268   */
09269   XSetCursorState(display,windows,MagickFalse);
09270 }
09271 
09272 /*
09273 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
09274 %                                                                             %
09275 %                                                                             %
09276 %                                                                             %
09277 +   X M a g n i f y W i n d o w C o m m a n d                                 %
09278 %                                                                             %
09279 %                                                                             %
09280 %                                                                             %
09281 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
09282 %
09283 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
09284 %  pixel as specified by the key symbol.
09285 %
09286 %  The format of the XMagnifyWindowCommand method is:
09287 %
09288 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
09289 %        const MagickStatusType state,const KeySym key_symbol)
09290 %
09291 %  A description of each parameter follows:
09292 %
09293 %    o display: Specifies a connection to an X server; returned from
09294 %      XOpenDisplay.
09295 %
09296 %    o windows: Specifies a pointer to a XWindows structure.
09297 %
09298 %    o state: key mask.
09299 %
09300 %    o key_symbol: Specifies a KeySym which indicates which side of the image
09301 %      to trim.
09302 %
09303 */
09304 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
09305   const MagickStatusType state,const KeySym key_symbol)
09306 {
09307   unsigned int
09308     quantum;
09309 
09310   /*
09311     User specified a magnify factor or position.
09312   */
09313   quantum=1;
09314   if ((state & Mod1Mask) != 0)
09315     quantum=10;
09316   switch ((int) key_symbol)
09317   {
09318     case QuitCommand:
09319     {
09320       (void) XWithdrawWindow(display,windows->magnify.id,
09321         windows->magnify.screen);
09322       break;
09323     }
09324     case XK_Home:
09325     case XK_KP_Home:
09326     {
09327       windows->magnify.x=(int) windows->image.width/2;
09328       windows->magnify.y=(int) windows->image.height/2;
09329       break;
09330     }
09331     case XK_Left:
09332     case XK_KP_Left:
09333     {
09334       if (windows->magnify.x > 0)
09335         windows->magnify.x-=quantum;
09336       break;
09337     }
09338     case XK_Up:
09339     case XK_KP_Up:
09340     {
09341       if (windows->magnify.y > 0)
09342         windows->magnify.y-=quantum;
09343       break;
09344     }
09345     case XK_Right:
09346     case XK_KP_Right:
09347     {
09348       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
09349         windows->magnify.x+=quantum;
09350       break;
09351     }
09352     case XK_Down:
09353     case XK_KP_Down:
09354     {
09355       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
09356         windows->magnify.y+=quantum;
09357       break;
09358     }
09359     case XK_0:
09360     case XK_1:
09361     case XK_2:
09362     case XK_3:
09363     case XK_4:
09364     case XK_5:
09365     case XK_6:
09366     case XK_7:
09367     case XK_8:
09368     case XK_9:
09369     {
09370       windows->magnify.data=(key_symbol-XK_0);
09371       break;
09372     }
09373     case XK_KP_0:
09374     case XK_KP_1:
09375     case XK_KP_2:
09376     case XK_KP_3:
09377     case XK_KP_4:
09378     case XK_KP_5:
09379     case XK_KP_6:
09380     case XK_KP_7:
09381     case XK_KP_8:
09382     case XK_KP_9:
09383     {
09384       windows->magnify.data=(key_symbol-XK_KP_0);
09385       break;
09386     }
09387     default:
09388       break;
09389   }
09390   XMakeMagnifyImage(display,windows);
09391 }
09392 
09393 /*
09394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
09395 %                                                                             %
09396 %                                                                             %
09397 %                                                                             %
09398 +   X M a k e P a n I m a g e                                                 %
09399 %                                                                             %
09400 %                                                                             %
09401 %                                                                             %
09402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
09403 %
09404 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
09405 %  icon window.
09406 %
09407 %  The format of the XMakePanImage method is:
09408 %
09409 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
09410 %          XWindows *windows,Image *image)
09411 %
09412 %  A description of each parameter follows:
09413 %
09414 %    o display: Specifies a connection to an X server;  returned from
09415 %      XOpenDisplay.
09416 %
09417 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
09418 %
09419 %    o windows: Specifies a pointer to a XWindows structure.
09420 %
09421 %    o image: the image;  returned from
09422 %      ReadImage.
09423 %
09424 */
09425 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
09426   XWindows *windows,Image *image)
09427 {
09428   MagickStatusType
09429     status;
09430 
09431   /*
09432     Create and display image for panning icon.
09433   */
09434   XSetCursorState(display,windows,MagickTrue);
09435   XCheckRefreshWindows(display,windows);
09436   windows->pan.x=windows->image.x;
09437   windows->pan.y=windows->image.y;
09438   status=XMakeImage(display,resource_info,&windows->pan,image,
09439     windows->pan.width,windows->pan.height);
09440   if (status == MagickFalse)
09441     ThrowXWindowFatalException(XServerError,image->exception.reason,
09442       image->exception.description);
09443   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
09444     windows->pan.pixmap);
09445   (void) XClearWindow(display,windows->pan.id);
09446   XDrawPanRectangle(display,windows);
09447   XSetCursorState(display,windows,MagickFalse);
09448 }
09449 
09450 /*
09451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
09452 %                                                                             %
09453 %                                                                             %
09454 %                                                                             %
09455 +   X M a t t a E d i t I m a g e                                             %
09456 %                                                                             %
09457 %                                                                             %
09458 %                                                                             %
09459 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
09460 %
09461 %  XMatteEditImage() allows the user to interactively change the Matte channel
09462 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
09463 %  before the matte information is stored.
09464 %
09465 %  The format of the XMatteEditImage method is:
09466 %
09467 %      MagickBooleanType XMatteEditImage(Display *display,
09468 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
09469 %
09470 %  A description of each parameter follows:
09471 %
09472 %    o display: Specifies a connection to an X server;  returned from
09473 %      XOpenDisplay.
09474 %
09475 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
09476 %
09477 %    o windows: Specifies a pointer to a XWindows structure.
09478 %
09479 %    o image: the image; returned from
09480 %      ReadImage.
09481 %
09482 */
09483 static MagickBooleanType XMatteEditImage(Display *display,
09484   XResourceInfo *resource_info,XWindows *windows,Image **image)
09485 {
09486   static char
09487     matte[MaxTextExtent] = "0";
09488 
09489   static const char
09490     *MatteEditMenu[] =
09491     {
09492       "Method",
09493       "Border Color",
09494       "Fuzz",
09495       "Matte Value",
09496       "Undo",
09497       "Help",
09498       "Dismiss",
09499       (char *) NULL
09500     };
09501 
09502   static const ModeType
09503     MatteEditCommands[] =
09504     {
09505       MatteEditMethod,
09506       MatteEditBorderCommand,
09507       MatteEditFuzzCommand,
09508       MatteEditValueCommand,
09509       MatteEditUndoCommand,
09510       MatteEditHelpCommand,
09511       MatteEditDismissCommand
09512     };
09513 
09514   static PaintMethod
09515     method = PointMethod;
09516 
09517   static XColor
09518     border_color = { 0, 0, 0, 0, 0, 0 };
09519 
09520   char
09521     command[MaxTextExtent],
09522     text[MaxTextExtent];
09523 
09524   Cursor
09525     cursor;
09526 
09527   int
09528     entry,
09529     id,
09530     x,
09531     x_offset,
09532     y,
09533     y_offset;
09534 
09535   register int
09536     i;
09537 
09538   register PixelPacket
09539     *q;
09540 
09541   unsigned int
09542     height,
09543     width;
09544 
09545   unsigned long
09546     state;
09547 
09548   XEvent
09549     event;
09550 
09551   /*
09552     Map Command widget.
09553   */
09554   (void) CloneString(&windows->command.name,"Matte Edit");
09555   windows->command.data=4;
09556   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
09557   (void) XMapRaised(display,windows->command.id);
09558   XClientMessage(display,windows->image.id,windows->im_protocols,
09559     windows->im_update_widget,CurrentTime);
09560   /*
09561     Make cursor.
09562   */
09563   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
09564     resource_info->background_color,resource_info->foreground_color);
09565   (void) XCheckDefineCursor(display,windows->image.id,cursor);
09566   /*
09567     Track pointer until button 1 is pressed.
09568   */
09569   XQueryPosition(display,windows->image.id,&x,&y);
09570   (void) XSelectInput(display,windows->image.id,
09571     windows->image.attributes.event_mask | PointerMotionMask);
09572   state=DefaultState;
09573   do
09574   {
09575     if (windows->info.mapped != MagickFalse)
09576       {
09577         /*
09578           Display pointer position.
09579         */
09580         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
09581           x+windows->image.x,y+windows->image.y);
09582         XInfoWidget(display,windows,text);
09583       }
09584     /*
09585       Wait for next event.
09586     */
09587     XScreenEvent(display,windows,&event);
09588     if (event.xany.window == windows->command.id)
09589       {
09590         /*
09591           Select a command from the Command widget.
09592         */
09593         id=XCommandWidget(display,windows,MatteEditMenu,&event);
09594         if (id < 0)
09595           {
09596             (void) XCheckDefineCursor(display,windows->image.id,cursor);
09597             continue;
09598           }
09599         switch (MatteEditCommands[id])
09600         {
09601           case MatteEditMethod:
09602           {
09603             char
09604               **methods;
09605 
09606             /*
09607               Select a method from the pop-up menu.
09608             */
09609             methods=GetMagickOptions(MagickMethodOptions);
09610             if (methods == (char **) NULL)
09611               break;
09612             entry=XMenuWidget(display,windows,MatteEditMenu[id],
09613               (const char **) methods,command);
09614             if (entry >= 0)
09615               method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
09616                 MagickFalse,methods[entry]);
09617             methods=DestroyStringList(methods);
09618             break;
09619           }
09620           case MatteEditBorderCommand:
09621           {
09622             const char
09623               *ColorMenu[MaxNumberPens];
09624 
09625             int
09626               pen_number;
09627 
09628             /*
09629               Initialize menu selections.
09630             */
09631             for (i=0; i < (int) (MaxNumberPens-2); i++)
09632               ColorMenu[i]=resource_info->pen_colors[i];
09633             ColorMenu[MaxNumberPens-2]="Browser...";
09634             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
09635             /*
09636               Select a pen color from the pop-up menu.
09637             */
09638             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
09639               (const char **) ColorMenu,command);
09640             if (pen_number < 0)
09641               break;
09642             if (pen_number == (MaxNumberPens-2))
09643               {
09644                 static char
09645                   color_name[MaxTextExtent] = "gray";
09646 
09647                 /*
09648                   Select a pen color from a dialog.
09649                 */
09650                 resource_info->pen_colors[pen_number]=color_name;
09651                 XColorBrowserWidget(display,windows,"Select",color_name);
09652                 if (*color_name == '\0')
09653                   break;
09654               }
09655             /*
09656               Set border color.
09657             */
09658             (void) XParseColor(display,windows->map_info->colormap,
09659               resource_info->pen_colors[pen_number],&border_color);
09660             break;
09661           }
09662           case MatteEditFuzzCommand:
09663           {
09664             static char
09665               fuzz[MaxTextExtent];
09666 
09667             static const char
09668               *FuzzMenu[] =
09669               {
09670                 "0%",
09671                 "2%",
09672                 "5%",
09673                 "10%",
09674                 "15%",
09675                 "Dialog...",
09676                 (char *) NULL,
09677               };
09678 
09679             /*
09680               Select a command from the pop-up menu.
09681             */
09682             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
09683               command);
09684             if (entry < 0)
09685               break;
09686             if (entry != 5)
09687               {
09688                 (*image)->fuzz=StringToDouble(FuzzMenu[entry],1.0*QuantumRange+
09689                   1.0);
09690                 break;
09691               }
09692             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
09693             (void) XDialogWidget(display,windows,"Ok",
09694               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
09695             if (*fuzz == '\0')
09696               break;
09697             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
09698             (*image)->fuzz=StringToDouble(fuzz,1.0*QuantumRange+1.0);
09699             break;
09700           }
09701           case MatteEditValueCommand:
09702           {
09703             static char
09704               message[MaxTextExtent];
09705 
09706             static const char
09707               *MatteMenu[] =
09708               {
09709                 "Opaque",
09710                 "Transparent",
09711                 "Dialog...",
09712                 (char *) NULL,
09713               };
09714 
09715             /*
09716               Select a command from the pop-up menu.
09717             */
09718             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
09719               command);
09720             if (entry < 0)
09721               break;
09722             if (entry != 2)
09723               {
09724                 (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
09725                   OpaqueOpacity);
09726                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
09727                   (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
09728                     (Quantum) TransparentOpacity);
09729                 break;
09730               }
09731             (void) FormatMagickString(message,MaxTextExtent,
09732               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
09733               QuantumRange);
09734             (void) XDialogWidget(display,windows,"Matte",message,matte);
09735             if (*matte == '\0')
09736               break;
09737             break;
09738           }
09739           case MatteEditUndoCommand:
09740           {
09741             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
09742               image);
09743             break;
09744           }
09745           case MatteEditHelpCommand:
09746           {
09747             XTextViewWidget(display,resource_info,windows,MagickFalse,
09748               "Help Viewer - Matte Edit",ImageMatteEditHelp);
09749             break;
09750           }
09751           case MatteEditDismissCommand:
09752           {
09753             /*
09754               Prematurely exit.
09755             */
09756             state|=EscapeState;
09757             state|=ExitState;
09758             break;
09759           }
09760           default:
09761             break;
09762         }
09763         (void) XCheckDefineCursor(display,windows->image.id,cursor);
09764         continue;
09765       }
09766     switch (event.type)
09767     {
09768       case ButtonPress:
09769       {
09770         if (event.xbutton.button != Button1)
09771           break;
09772         if ((event.xbutton.window != windows->image.id) &&
09773             (event.xbutton.window != windows->magnify.id))
09774           break;
09775         /*
09776           Update matte data.
09777         */
09778         x=event.xbutton.x;
09779         y=event.xbutton.y;
09780         (void) XMagickCommand(display,resource_info,windows,
09781           SaveToUndoBufferCommand,image);
09782         state|=UpdateConfigurationState;
09783         break;
09784       }
09785       case ButtonRelease:
09786       {
09787         if (event.xbutton.button != Button1)
09788           break;
09789         if ((event.xbutton.window != windows->image.id) &&
09790             (event.xbutton.window != windows->magnify.id))
09791           break;
09792         /*
09793           Update colormap information.
09794         */
09795         x=event.xbutton.x;
09796         y=event.xbutton.y;
09797         XConfigureImageColormap(display,resource_info,windows,*image);
09798         (void) XConfigureImage(display,resource_info,windows,*image);
09799         XInfoWidget(display,windows,text);
09800         (void) XCheckDefineCursor(display,windows->image.id,cursor);
09801         state&=(~UpdateConfigurationState);
09802         break;
09803       }
09804       case Expose:
09805         break;
09806       case KeyPress:
09807       {
09808         char
09809           command[MaxTextExtent];
09810 
09811         KeySym
09812           key_symbol;
09813 
09814         if (event.xkey.window == windows->magnify.id)
09815           {
09816             Window
09817               window;
09818 
09819             window=windows->magnify.id;
09820             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
09821  
09822           }
09823         if (event.xkey.window != windows->image.id)
09824           break;
09825         /*
09826           Respond to a user key press.
09827         */
09828         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
09829           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
09830         switch ((int) key_symbol)
09831         {
09832           case XK_Escape:
09833           case XK_F20:
09834           {
09835             /*
09836               Prematurely exit.
09837             */
09838             state|=ExitState;
09839             break;
09840           }
09841           case XK_F1:
09842           case XK_Help:
09843           {
09844             XTextViewWidget(display,resource_info,windows,MagickFalse,
09845               "Help Viewer - Matte Edit",ImageMatteEditHelp);
09846             break;
09847           }
09848           default:
09849           {
09850             (void) XBell(display,0);
09851             break;
09852           }
09853         }
09854         break;
09855       }
09856       case MotionNotify:
09857       {
09858         /*
09859           Map and unmap Info widget as cursor crosses its boundaries.
09860         */
09861         x=event.xmotion.x;
09862         y=event.xmotion.y;
09863         if (windows->info.mapped != MagickFalse)
09864           {
09865             if ((x < (int) (windows->info.x+windows->info.width)) &&
09866                 (y < (int) (windows->info.y+windows->info.height)))
09867               (void) XWithdrawWindow(display,windows->info.id,
09868                 windows->info.screen);
09869           }
09870         else
09871           if ((x > (int) (windows->info.x+windows->info.width)) ||
09872               (y > (int) (windows->info.y+windows->info.height)))
09873             (void) XMapWindow(display,windows->info.id);
09874         break;
09875       }
09876       default:
09877         break;
09878     }
09879     if (event.xany.window == windows->magnify.id)
09880       {
09881         x=windows->magnify.x-windows->image.x;
09882         y=windows->magnify.y-windows->image.y;
09883       }
09884     x_offset=x;
09885     y_offset=y;
09886     if ((state & UpdateConfigurationState) != 0)
09887       {
09888         ExceptionInfo
09889           *exception;
09890 
09891         int
09892           x,
09893           y;
09894 
09895         /*
09896           Matte edit is relative to image configuration.
09897         */
09898         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
09899           MagickTrue);
09900         XPutPixel(windows->image.ximage,x_offset,y_offset,
09901           windows->pixel_info->background_color.pixel);
09902         width=(unsigned int) (*image)->columns;
09903         height=(unsigned int) (*image)->rows;
09904         x=0;
09905         y=0;
09906         if (windows->image.crop_geometry != (char *) NULL)
09907           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
09908             &width,&height);
09909         x_offset=(int)
09910           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
09911         y_offset=(int)
09912           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
09913         if ((x_offset < 0) || (y_offset < 0))
09914           continue;
09915         if ((x_offset >= (int) (*image)->columns) ||
09916             (y_offset >= (int) (*image)->rows))
09917           continue;
09918         if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
09919           return(MagickFalse);
09920         (*image)->matte=MagickTrue;
09921         exception=(&(*image)->exception);
09922         switch (method)
09923         {
09924           case PointMethod:
09925           default:
09926           {
09927             /*
09928               Update matte information using point algorithm.
09929             */
09930             q=GetAuthenticPixels(*image,x_offset,y_offset,1,1,exception);
09931             if (q == (PixelPacket *) NULL)
09932               break;
09933             q->opacity=(Quantum) atol(matte);
09934             (void) SyncAuthenticPixels(*image,exception);
09935             break;
09936           }
09937           case ReplaceMethod:
09938           {
09939             PixelPacket
09940               target;
09941 
09942             /*
09943               Update matte information using replace algorithm.
09944             */
09945             (void) GetOneVirtualPixel(*image,x_offset,y_offset,&target,
09946               exception);
09947             for (y=0; y < (long) (*image)->rows; y++)
09948             {
09949               q=GetAuthenticPixels(*image,0,y,(*image)->columns,1,
09950                 &(*image)->exception);
09951               if (q == (PixelPacket *) NULL)
09952                 break;
09953               for (x=0; x < (int) (*image)->columns; x++)
09954               {
09955                 if (IsColorSimilar(*image,q,&target))
09956                   q->opacity=(Quantum) atol(matte);
09957                 q++;
09958               }
09959               if (SyncAuthenticPixels(*image,exception) == MagickFalse)
09960                 break;
09961             }
09962             break;
09963           }
09964           case FloodfillMethod:
09965           case FillToBorderMethod:
09966           {
09967             DrawInfo
09968               *draw_info;
09969 
09970             MagickPixelPacket
09971               target;
09972 
09973             /*
09974               Update matte information using floodfill algorithm.
09975             */
09976             (void) GetOneVirtualMagickPixel(*image,x_offset,y_offset,&target,
09977               exception);
09978             if (method == FillToBorderMethod)
09979               {
09980                 target.red=(MagickRealType)
09981                   ScaleShortToQuantum(border_color.red);
09982                 target.green=(MagickRealType)
09983                   ScaleShortToQuantum(border_color.green);
09984                 target.blue=(MagickRealType)
09985                   ScaleShortToQuantum(border_color.blue);
09986               }
09987             draw_info=CloneDrawInfo(resource_info->image_info,
09988               (DrawInfo *) NULL);
09989             draw_info->fill.opacity=RoundToQuantum(atof(matte));
09990             (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
09991               x_offset,y_offset,method == FloodfillMethod ? MagickFalse :
09992               MagickTrue);
09993             draw_info=DestroyDrawInfo(draw_info);
09994             break;
09995           }
09996           case ResetMethod:
09997           {
09998             /*
09999               Update matte information using reset algorithm.
10000             */
10001             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10002               return(MagickFalse);
10003             for (y=0; y < (long) (*image)->rows; y++)
10004             {
10005               q=QueueAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
10006               if (q == (PixelPacket *) NULL)
10007                 break;
10008               for (x=0; x < (int) (*image)->columns; x++)
10009               {
10010                 q->opacity=(Quantum) atol(matte);
10011                 q++;
10012               }
10013               if (SyncAuthenticPixels(*image,exception) == MagickFalse)
10014                 break;
10015             }
10016             if (atol(matte) == OpaqueOpacity)
10017               (*image)->matte=MagickFalse;
10018             break;
10019           }
10020         }
10021         state&=(~UpdateConfigurationState);
10022       }
10023   } while ((state & ExitState) == 0);
10024   (void) XSelectInput(display,windows->image.id,
10025     windows->image.attributes.event_mask);
10026   XSetCursorState(display,windows,MagickFalse);
10027   (void) XFreeCursor(display,cursor);
10028   return(MagickTrue);
10029 }
10030 
10031 /*
10032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10033 %                                                                             %
10034 %                                                                             %
10035 %                                                                             %
10036 +   X O p e n I m a g e                                                       %
10037 %                                                                             %
10038 %                                                                             %
10039 %                                                                             %
10040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10041 %
10042 %  XOpenImage() loads an image from a file.
10043 %
10044 %  The format of the XOpenImage method is:
10045 %
10046 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10047 %       XWindows *windows,const unsigned int command)
10048 %
10049 %  A description of each parameter follows:
10050 %
10051 %    o display: Specifies a connection to an X server; returned from
10052 %      XOpenDisplay.
10053 %
10054 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10055 %
10056 %    o windows: Specifies a pointer to a XWindows structure.
10057 %
10058 %    o command: A value other than zero indicates that the file is selected
10059 %      from the command line argument list.
10060 %
10061 */
10062 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10063   XWindows *windows,const MagickBooleanType command)
10064 {
10065   const MagickInfo
10066     *magick_info;
10067 
10068   ExceptionInfo
10069     *exception;
10070 
10071   Image
10072     *nexus;
10073 
10074   ImageInfo
10075     *image_info;
10076 
10077   static char
10078     filename[MaxTextExtent] = "\0";
10079 
10080   /*
10081     Request file name from user.
10082   */
10083   if (command == MagickFalse)
10084     XFileBrowserWidget(display,windows,"Open",filename);
10085   else
10086     {
10087       char
10088         **filelist,
10089         **files;
10090 
10091       int
10092         count,
10093         status;
10094 
10095       register int
10096         i,
10097         j;
10098 
10099       /*
10100         Select next image from the command line.
10101       */
10102       status=XGetCommand(display,windows->image.id,&files,&count);
10103       if (status == 0)
10104         {
10105           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10106           return((Image *) NULL);
10107         }
10108       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10109       if (filelist == (char **) NULL)
10110         {
10111           ThrowXWindowFatalException(ResourceLimitError,
10112             "MemoryAllocationFailed","...");
10113           (void) XFreeStringList(files);
10114           return((Image *) NULL);
10115         }
10116       j=0;
10117       for (i=1; i < count; i++)
10118         if (*files[i] != '-')
10119           filelist[j++]=files[i];
10120       filelist[j]=(char *) NULL;
10121       XListBrowserWidget(display,windows,&windows->widget,
10122         (const char **) filelist,"Load","Select Image to Load:",filename);
10123       filelist=(char **) RelinquishMagickMemory(filelist);
10124       (void) XFreeStringList(files);
10125     }
10126   if (*filename == '\0')
10127     return((Image *) NULL);
10128   image_info=CloneImageInfo(resource_info->image_info);
10129   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10130     (void *) NULL);
10131   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10132   exception=AcquireExceptionInfo();
10133   (void) SetImageInfo(image_info,MagickFalse,exception);
10134   if (LocaleCompare(image_info->magick,"X") == 0)
10135     {
10136       char
10137         seconds[MaxTextExtent];
10138 
10139       /*
10140         User may want to delay the X server screen grab.
10141       */
10142       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10143       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10144         seconds);
10145       if (*seconds == '\0')
10146         return((Image *) NULL);
10147       XDelay(display,(unsigned long) (1000*atol(seconds)));
10148     }
10149   magick_info=GetMagickInfo(image_info->magick,exception);
10150   if ((magick_info != (const MagickInfo *) NULL) &&
10151       (magick_info->raw != MagickFalse))
10152     {
10153       char
10154         geometry[MaxTextExtent];
10155 
10156       /*
10157         Request image size from the user.
10158       */
10159       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10160       if (image_info->size != (char *) NULL)
10161         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10162       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10163         geometry);
10164       (void) CloneString(&image_info->size,geometry);
10165     }
10166   /*
10167     Load the image.
10168   */
10169   XSetCursorState(display,windows,MagickTrue);
10170   XCheckRefreshWindows(display,windows);
10171   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10172   nexus=ReadImage(image_info,exception);
10173   CatchException(exception);
10174   XSetCursorState(display,windows,MagickFalse);
10175   if (nexus != (Image *) NULL)
10176     XClientMessage(display,windows->image.id,windows->im_protocols,
10177       windows->im_next_image,CurrentTime);
10178   else
10179     {
10180       char
10181         *text,
10182         **textlist;
10183 
10184       /*
10185         Unknown image format.
10186       */
10187       text=FileToString(filename,~0,exception);
10188       if (text == (char *) NULL)
10189         return((Image *) NULL);
10190       textlist=StringToList(text);
10191       if (textlist != (char **) NULL)
10192         {
10193           char
10194             title[MaxTextExtent];
10195 
10196           register int
10197             i;
10198 
10199           (void) FormatMagickString(title,MaxTextExtent,
10200             "Unknown format: %s",filename);
10201           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10202             (const char **) textlist);
10203           for (i=0; textlist[i] != (char *) NULL; i++)
10204             textlist[i]=DestroyString(textlist[i]);
10205           textlist=(char **) RelinquishMagickMemory(textlist);
10206         }
10207       text=DestroyString(text);
10208     }
10209   exception=DestroyExceptionInfo(exception);
10210   image_info=DestroyImageInfo(image_info);
10211   return(nexus);
10212 }
10213 
10214 /*
10215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10216 %                                                                             %
10217 %                                                                             %
10218 %                                                                             %
10219 +   X P a n I m a g e                                                         %
10220 %                                                                             %
10221 %                                                                             %
10222 %                                                                             %
10223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10224 %
10225 %  XPanImage() pans the image until the mouse button is released.
10226 %
10227 %  The format of the XPanImage method is:
10228 %
10229 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10230 %
10231 %  A description of each parameter follows:
10232 %
10233 %    o display: Specifies a connection to an X server;  returned from
10234 %      XOpenDisplay.
10235 %
10236 %    o windows: Specifies a pointer to a XWindows structure.
10237 %
10238 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10239 %      the entire image is refreshed.
10240 %
10241 */
10242 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10243 {
10244   char
10245     text[MaxTextExtent];
10246 
10247   Cursor
10248     cursor;
10249 
10250   MagickRealType
10251     x_factor,
10252     y_factor;
10253 
10254   RectangleInfo
10255     pan_info;
10256 
10257   unsigned long
10258     state;
10259 
10260   /*
10261     Define cursor.
10262   */
10263   if ((windows->image.ximage->width > (int) windows->image.width) &&
10264       (windows->image.ximage->height > (int) windows->image.height))
10265     cursor=XCreateFontCursor(display,XC_fleur);
10266   else
10267     if (windows->image.ximage->width > (int) windows->image.width)
10268       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10269     else
10270       if (windows->image.ximage->height > (int) windows->image.height)
10271         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10272       else
10273         cursor=XCreateFontCursor(display,XC_arrow);
10274   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10275   /*
10276     Pan image as pointer moves until the mouse button is released.
10277   */
10278   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10279   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10280   pan_info.width=windows->pan.width*windows->image.width/
10281     windows->image.ximage->width;
10282   pan_info.height=windows->pan.height*windows->image.height/
10283     windows->image.ximage->height;
10284   pan_info.x=0;
10285   pan_info.y=0;
10286   state=UpdateConfigurationState;
10287   do
10288   {
10289     switch (event->type)
10290     {
10291       case ButtonPress:
10292       {
10293         /*
10294           User choose an initial pan location.
10295         */
10296         pan_info.x=event->xbutton.x;
10297         pan_info.y=event->xbutton.y;
10298         state|=UpdateConfigurationState;
10299         break;
10300       }
10301       case ButtonRelease:
10302       {
10303         /*
10304           User has finished panning the image.
10305         */
10306         pan_info.x=event->xbutton.x;
10307         pan_info.y=event->xbutton.y;
10308         state|=UpdateConfigurationState | ExitState;
10309         break;
10310       }
10311       case MotionNotify:
10312       {
10313         pan_info.x=event->xmotion.x;
10314         pan_info.y=event->xmotion.y;
10315         state|=UpdateConfigurationState;
10316       }
10317       default:
10318         break;
10319     }
10320     if ((state & UpdateConfigurationState) != 0)
10321       {
10322         /*
10323           Check boundary conditions.
10324         */
10325         if (pan_info.x < (int) (pan_info.width/2))
10326           pan_info.x=0;
10327         else
10328           pan_info.x=(int) (x_factor*(pan_info.x-(pan_info.width/2)));
10329         if (pan_info.x < 0)
10330           pan_info.x=0;
10331         else
10332           if ((int) (pan_info.x+windows->image.width) >
10333               windows->image.ximage->width)
10334             pan_info.x=(long)
10335               (windows->image.ximage->width-windows->image.width);
10336         if (pan_info.y < (long) (pan_info.height/2))
10337           pan_info.y=0;
10338         else
10339           pan_info.y=(long) (y_factor*(pan_info.y-(pan_info.height/2)));
10340         if (pan_info.y < 0)
10341           pan_info.y=0;
10342         else
10343           if ((int) (pan_info.y+windows->image.height) >
10344               windows->image.ximage->height)
10345             pan_info.y=(long)
10346               (windows->image.ximage->height-windows->image.height);
10347         if ((windows->image.x != (int) pan_info.x) ||
10348             (windows->image.y != (int) pan_info.y))
10349           {
10350             /*
10351               Display image pan offset.
10352             */
10353             windows->image.x=(int) pan_info.x;
10354             windows->image.y=(int) pan_info.y;
10355             (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
10356               windows->image.width,windows->image.height,windows->image.x,
10357               windows->image.y);
10358             XInfoWidget(display,windows,text);
10359             /*
10360               Refresh Image window.
10361             */
10362             XDrawPanRectangle(display,windows);
10363             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10364           }
10365         state&=(~UpdateConfigurationState);
10366       }
10367     /*
10368       Wait for next event.
10369     */
10370     if ((state & ExitState) == 0)
10371       XScreenEvent(display,windows,event);
10372   } while ((state & ExitState) == 0);
10373   /*
10374     Restore cursor.
10375   */
10376   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10377   (void) XFreeCursor(display,cursor);
10378   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10379 }
10380 
10381 /*
10382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10383 %                                                                             %
10384 %                                                                             %
10385 %                                                                             %
10386 +   X P a s t e I m a g e                                                     %
10387 %                                                                             %
10388 %                                                                             %
10389 %                                                                             %
10390 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10391 %
10392 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10393 %  window image at a location the user chooses with the pointer.
10394 %
10395 %  The format of the XPasteImage method is:
10396 %
10397 %      MagickBooleanType XPasteImage(Display *display,
10398 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10399 %
10400 %  A description of each parameter follows:
10401 %
10402 %    o display: Specifies a connection to an X server;  returned from
10403 %      XOpenDisplay.
10404 %
10405 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10406 %
10407 %    o windows: Specifies a pointer to a XWindows structure.
10408 %
10409 %    o image: the image; returned from
10410 %      ReadImage.
10411 %
10412 */
10413 static MagickBooleanType XPasteImage(Display *display,
10414   XResourceInfo *resource_info,XWindows *windows,Image *image)
10415 {
10416   static const char
10417     *PasteMenu[] =
10418     {
10419       "Operator",
10420       "Help",
10421       "Dismiss",
10422       (char *) NULL
10423     };
10424 
10425   static const ModeType
10426     PasteCommands[] =
10427     {
10428       PasteOperatorsCommand,
10429       PasteHelpCommand,
10430       PasteDismissCommand
10431     };
10432 
10433   static CompositeOperator
10434     compose = CopyCompositeOp;
10435 
10436   char
10437     text[MaxTextExtent];
10438 
10439   Cursor
10440     cursor;
10441 
10442   Image
10443     *paste_image;
10444 
10445   int
10446     entry,
10447     id,
10448     x,
10449     y;
10450 
10451   MagickRealType
10452     scale_factor;
10453 
10454   RectangleInfo
10455     highlight_info,
10456     paste_info;
10457 
10458   unsigned int
10459     height,
10460     width;
10461 
10462   unsigned long
10463     state;
10464 
10465   XEvent
10466     event;
10467 
10468   /*
10469     Copy image.
10470   */
10471   if (resource_info->copy_image == (Image *) NULL)
10472     return(MagickFalse);
10473   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10474     &image->exception);
10475   /*
10476     Map Command widget.
10477   */
10478   (void) CloneString(&windows->command.name,"Paste");
10479   windows->command.data=1;
10480   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10481   (void) XMapRaised(display,windows->command.id);
10482   XClientMessage(display,windows->image.id,windows->im_protocols,
10483     windows->im_update_widget,CurrentTime);
10484   /*
10485     Track pointer until button 1 is pressed.
10486   */
10487   XSetCursorState(display,windows,MagickFalse);
10488   XQueryPosition(display,windows->image.id,&x,&y);
10489   (void) XSelectInput(display,windows->image.id,
10490     windows->image.attributes.event_mask | PointerMotionMask);
10491   paste_info.x=windows->image.x+x;
10492   paste_info.y=windows->image.y+y;
10493   paste_info.width=0;
10494   paste_info.height=0;
10495   cursor=XCreateFontCursor(display,XC_ul_angle);
10496   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10497   state=DefaultState;
10498   do
10499   {
10500     if (windows->info.mapped != MagickFalse)
10501       {
10502         /*
10503           Display pointer position.
10504         */
10505         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
10506           paste_info.x,paste_info.y);
10507         XInfoWidget(display,windows,text);
10508       }
10509     highlight_info=paste_info;
10510     highlight_info.x=paste_info.x-windows->image.x;
10511     highlight_info.y=paste_info.y-windows->image.y;
10512     XHighlightRectangle(display,windows->image.id,
10513       windows->image.highlight_context,&highlight_info);
10514     /*
10515       Wait for next event.
10516     */
10517     XScreenEvent(display,windows,&event);
10518     XHighlightRectangle(display,windows->image.id,
10519       windows->image.highlight_context,&highlight_info);
10520     if (event.xany.window == windows->command.id)
10521       {
10522         /*
10523           Select a command from the Command widget.
10524         */
10525         id=XCommandWidget(display,windows,PasteMenu,&event);
10526         if (id < 0)
10527           continue;
10528         switch (PasteCommands[id])
10529         {
10530           case PasteOperatorsCommand:
10531           {
10532             char
10533               command[MaxTextExtent],
10534               **operators;
10535 
10536             /*
10537               Select a command from the pop-up menu.
10538             */
10539             operators=GetMagickOptions(MagickComposeOptions);
10540             if (operators == (char **) NULL)
10541               break;
10542             entry=XMenuWidget(display,windows,PasteMenu[id],
10543               (const char **) operators,command);
10544             if (entry >= 0)
10545               compose=(CompositeOperator) ParseMagickOption(
10546                 MagickComposeOptions,MagickFalse,operators[entry]);
10547             operators=DestroyStringList(operators);
10548             break;
10549           }
10550           case PasteHelpCommand:
10551           {
10552             XTextViewWidget(display,resource_info,windows,MagickFalse,
10553               "Help Viewer - Image Composite",ImagePasteHelp);
10554             break;
10555           }
10556           case PasteDismissCommand:
10557           {
10558             /*
10559               Prematurely exit.
10560             */
10561             state|=EscapeState;
10562             state|=ExitState;
10563             break;
10564           }
10565           default:
10566             break;
10567         }
10568         continue;
10569       }
10570     switch (event.type)
10571     {
10572       case ButtonPress:
10573       {
10574         if (image->debug != MagickFalse)
10575           (void) LogMagickEvent(X11Event,GetMagickModule(),
10576             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10577             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10578         if (event.xbutton.button != Button1)
10579           break;
10580         if (event.xbutton.window != windows->image.id)
10581           break;
10582         /*
10583           Paste rectangle is relative to image configuration.
10584         */
10585         width=(unsigned int) image->columns;
10586         height=(unsigned int) image->rows;
10587         x=0;
10588         y=0;
10589         if (windows->image.crop_geometry != (char *) NULL)
10590           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10591             &width,&height);
10592         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10593         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10594         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10595         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10596         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10597         paste_info.x=windows->image.x+event.xbutton.x;
10598         paste_info.y=windows->image.y+event.xbutton.y;
10599         break;
10600       }
10601       case ButtonRelease:
10602       {
10603         if (image->debug != MagickFalse)
10604           (void) LogMagickEvent(X11Event,GetMagickModule(),
10605             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10606             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10607         if (event.xbutton.button != Button1)
10608           break;
10609         if (event.xbutton.window != windows->image.id)
10610           break;
10611         if ((paste_info.width != 0) && (paste_info.height != 0))
10612           {
10613             /*
10614               User has selected the location of the paste image.
10615             */
10616             paste_info.x=windows->image.x+event.xbutton.x;
10617             paste_info.y=windows->image.y+event.xbutton.y;
10618             state|=ExitState;
10619           }
10620         break;
10621       }
10622       case Expose:
10623         break;
10624       case KeyPress:
10625       {
10626         char
10627           command[MaxTextExtent];
10628 
10629         KeySym
10630           key_symbol;
10631 
10632         int
10633           length;
10634 
10635         if (event.xkey.window != windows->image.id)
10636           break;
10637         /*
10638           Respond to a user key press.
10639         */
10640         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10641           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10642         *(command+length)='\0';
10643         if (image->debug != MagickFalse)
10644           (void) LogMagickEvent(X11Event,GetMagickModule(),
10645             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10646         switch ((int) key_symbol)
10647         {
10648           case XK_Escape:
10649           case XK_F20:
10650           {
10651             /*
10652               Prematurely exit.
10653             */
10654             paste_image=DestroyImage(paste_image);
10655             state|=EscapeState;
10656             state|=ExitState;
10657             break;
10658           }
10659           case XK_F1:
10660           case XK_Help:
10661           {
10662             (void) XSetFunction(display,windows->image.highlight_context,
10663               GXcopy);
10664             XTextViewWidget(display,resource_info,windows,MagickFalse,
10665               "Help Viewer - Image Composite",ImagePasteHelp);
10666             (void) XSetFunction(display,windows->image.highlight_context,
10667               GXinvert);
10668             break;
10669           }
10670           default:
10671           {
10672             (void) XBell(display,0);
10673             break;
10674           }
10675         }
10676         break;
10677       }
10678       case MotionNotify:
10679       {
10680         /*
10681           Map and unmap Info widget as text cursor crosses its boundaries.
10682         */
10683         x=event.xmotion.x;
10684         y=event.xmotion.y;
10685         if (windows->info.mapped != MagickFalse)
10686           {
10687             if ((x < (int) (windows->info.x+windows->info.width)) &&
10688                 (y < (int) (windows->info.y+windows->info.height)))
10689               (void) XWithdrawWindow(display,windows->info.id,
10690                 windows->info.screen);
10691           }
10692         else
10693           if ((x > (int) (windows->info.x+windows->info.width)) ||
10694               (y > (int) (windows->info.y+windows->info.height)))
10695             (void) XMapWindow(display,windows->info.id);
10696         paste_info.x=windows->image.x+x;
10697         paste_info.y=windows->image.y+y;
10698         break;
10699       }
10700       default:
10701       {
10702         if (image->debug != MagickFalse)
10703           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10704             event.type);
10705         break;
10706       }
10707     }
10708   } while ((state & ExitState) == 0);
10709   (void) XSelectInput(display,windows->image.id,
10710     windows->image.attributes.event_mask);
10711   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10712   XSetCursorState(display,windows,MagickFalse);
10713   (void) XFreeCursor(display,cursor);
10714   if ((state & EscapeState) != 0)
10715     return(MagickTrue);
10716   /*
10717     Image pasting is relative to image configuration.
10718   */
10719   XSetCursorState(display,windows,MagickTrue);
10720   XCheckRefreshWindows(display,windows);
10721   width=(unsigned int) image->columns;
10722   height=(unsigned int) image->rows;
10723   x=0;
10724   y=0;
10725   if (windows->image.crop_geometry != (char *) NULL)
10726     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10727   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10728   paste_info.x+=x;
10729   paste_info.x=(int) (scale_factor*paste_info.x+0.5);
10730   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10731   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10732   paste_info.y+=y;
10733   paste_info.y=(int) (scale_factor*paste_info.y*scale_factor+0.5);
10734   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10735   /*
10736     Paste image with X Image window.
10737   */
10738   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10739   paste_image=DestroyImage(paste_image);
10740   XSetCursorState(display,windows,MagickFalse);
10741   /*
10742     Update image colormap.
10743   */
10744   XConfigureImageColormap(display,resource_info,windows,image);
10745   (void) XConfigureImage(display,resource_info,windows,image);
10746   return(MagickTrue);
10747 }
10748 
10749 /*
10750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10751 %                                                                             %
10752 %                                                                             %
10753 %                                                                             %
10754 +   X P r i n t I m a g e                                                     %
10755 %                                                                             %
10756 %                                                                             %
10757 %                                                                             %
10758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10759 %
10760 %  XPrintImage() prints an image to a Postscript printer.
10761 %
10762 %  The format of the XPrintImage method is:
10763 %
10764 %      MagickBooleanType XPrintImage(Display *display,
10765 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10766 %
10767 %  A description of each parameter follows:
10768 %
10769 %    o display: Specifies a connection to an X server; returned from
10770 %      XOpenDisplay.
10771 %
10772 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10773 %
10774 %    o windows: Specifies a pointer to a XWindows structure.
10775 %
10776 %    o image: the image;  returned from
10777 %      ReadImage.
10778 %
10779 */
10780 static MagickBooleanType XPrintImage(Display *display,
10781   XResourceInfo *resource_info,XWindows *windows,Image *image)
10782 {
10783   char
10784     filename[MaxTextExtent],
10785     geometry[MaxTextExtent];
10786 
10787   Image
10788     *print_image;
10789 
10790   ImageInfo
10791     *image_info;
10792 
10793   MagickStatusType
10794     status;
10795 
10796   /*
10797     Request Postscript page geometry from user.
10798   */
10799   image_info=CloneImageInfo(resource_info->image_info);
10800   (void) FormatMagickString(geometry,MaxTextExtent,"Letter");
10801   if (image_info->page != (char *) NULL)
10802     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10803   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10804     "Select Postscript Page Geometry:",geometry);
10805   if (*geometry == '\0')
10806     return(MagickTrue);
10807   image_info->page=GetPageGeometry(geometry);
10808   /*
10809     Apply image transforms.
10810   */
10811   XSetCursorState(display,windows,MagickTrue);
10812   XCheckRefreshWindows(display,windows);
10813   print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10814   if (print_image == (Image *) NULL)
10815     return(MagickFalse);
10816   (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
10817     windows->image.ximage->width,windows->image.ximage->height);
10818   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10819   /*
10820     Print image.
10821   */
10822   (void) AcquireUniqueFilename(filename);
10823   (void) FormatMagickString(print_image->filename,MaxTextExtent,"print:%s",
10824     filename);
10825   status=WriteImage(image_info,print_image);
10826  (void) RelinquishUniqueFileResource(filename);
10827   print_image=DestroyImage(print_image);
10828   image_info=DestroyImageInfo(image_info);
10829   XSetCursorState(display,windows,MagickFalse);
10830   return(status != 0 ? MagickTrue : MagickFalse);
10831 }
10832 
10833 /*
10834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10835 %                                                                             %
10836 %                                                                             %
10837 %                                                                             %
10838 +   X R O I I m a g e                                                         %
10839 %                                                                             %
10840 %                                                                             %
10841 %                                                                             %
10842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10843 %
10844 %  XROIImage() applies an image processing technique to a region of interest.
10845 %
10846 %  The format of the XROIImage method is:
10847 %
10848 %      MagickBooleanType XROIImage(Display *display,
10849 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
10850 %
10851 %  A description of each parameter follows:
10852 %
10853 %    o display: Specifies a connection to an X server; returned from
10854 %      XOpenDisplay.
10855 %
10856 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10857 %
10858 %    o windows: Specifies a pointer to a XWindows structure.
10859 %
10860 %    o image: the image; returned from
10861 %      ReadImage.
10862 %
10863 */
10864 static MagickBooleanType XROIImage(Display *display,
10865   XResourceInfo *resource_info,XWindows *windows,Image **image)
10866 {
10867 #define ApplyMenus  7
10868 
10869   static const char
10870     *ROIMenu[] =
10871     {
10872       "Help",
10873       "Dismiss",
10874       (char *) NULL
10875     },
10876     *ApplyMenu[] =
10877     {
10878       "File",
10879       "Edit",
10880       "Transform",
10881       "Enhance",
10882       "Effects",
10883       "F/X",
10884       "Miscellany",
10885       "Help",
10886       "Dismiss",
10887       (char *) NULL
10888     },
10889     *FileMenu[] =
10890     {
10891       "Save...",
10892       "Print...",
10893       (char *) NULL
10894     },
10895     *EditMenu[] =
10896     {
10897       "Undo",
10898       "Redo",
10899       (char *) NULL
10900     },
10901     *TransformMenu[] =
10902     {
10903       "Flop",
10904       "Flip",
10905       "Rotate Right",
10906       "Rotate Left",
10907       (char *) NULL
10908     },
10909     *EnhanceMenu[] =
10910     {
10911       "Hue...",
10912       "Saturation...",
10913       "Brightness...",
10914       "Gamma...",
10915       "Spiff",
10916       "Dull",
10917       "Contrast Stretch...",
10918       "Sigmoidal Contrast...",
10919       "Normalize",
10920       "Equalize",
10921       "Negate",
10922       "Grayscale",
10923       "Map...",
10924       "Quantize...",
10925       (char *) NULL
10926     },
10927     *EffectsMenu[] =
10928     {
10929       "Despeckle",
10930       "Emboss",
10931       "Reduce Noise",
10932       "Add Noise",
10933       "Sharpen...",
10934       "Blur...",
10935       "Threshold...",
10936       "Edge Detect...",
10937       "Spread...",
10938       "Shade...",
10939       "Raise...",
10940       "Segment...",
10941       (char *) NULL
10942     },
10943     *FXMenu[] =
10944     {
10945       "Solarize...",
10946       "Sepia Tone...",
10947       "Swirl...",
10948       "Implode...",
10949       "Vignette...",
10950       "Wave...",
10951       "Oil Paint...",
10952       "Charcoal Draw...",
10953       (char *) NULL
10954     },
10955     *MiscellanyMenu[] =
10956     {
10957       "Image Info",
10958       "Zoom Image",
10959       "Show Preview...",
10960       "Show Histogram",
10961       "Show Matte",
10962       (char *) NULL
10963     };
10964 
10965   static const char
10966     **Menus[ApplyMenus] =
10967     {
10968       FileMenu,
10969       EditMenu,
10970       TransformMenu,
10971       EnhanceMenu,
10972       EffectsMenu,
10973       FXMenu,
10974       MiscellanyMenu
10975     };
10976 
10977   static const CommandType
10978     ApplyCommands[] =
10979     {
10980       NullCommand,
10981       NullCommand,
10982       NullCommand,
10983       NullCommand,
10984       NullCommand,
10985       NullCommand,
10986       NullCommand,
10987       HelpCommand,
10988       QuitCommand
10989     },
10990     FileCommands[] =
10991     {
10992       SaveCommand,
10993       PrintCommand
10994     },
10995     EditCommands[] =
10996     {
10997       UndoCommand,
10998       RedoCommand
10999     },
11000     TransformCommands[] =
11001     {
11002       FlopCommand,
11003       FlipCommand,
11004       RotateRightCommand,
11005       RotateLeftCommand
11006     },
11007     EnhanceCommands[] =
11008     {
11009       HueCommand,
11010       SaturationCommand,
11011       BrightnessCommand,
11012       GammaCommand,
11013       SpiffCommand,
11014       DullCommand,
11015       ContrastStretchCommand,
11016       SigmoidalContrastCommand,
11017       NormalizeCommand,
11018       EqualizeCommand,
11019       NegateCommand,
11020       GrayscaleCommand,
11021       MapCommand,
11022       QuantizeCommand
11023     },
11024     EffectsCommands[] =
11025     {
11026       DespeckleCommand,
11027       EmbossCommand,
11028       ReduceNoiseCommand,
11029       AddNoiseCommand,
11030       SharpenCommand,
11031       BlurCommand,
11032       EdgeDetectCommand,
11033       SpreadCommand,
11034       ShadeCommand,
11035       RaiseCommand,
11036       SegmentCommand
11037     },
11038     FXCommands[] =
11039     {
11040       SolarizeCommand,
11041       SepiaToneCommand,
11042       SwirlCommand,
11043       ImplodeCommand,
11044       VignetteCommand,
11045       WaveCommand,
11046       OilPaintCommand,
11047       CharcoalDrawCommand
11048     },
11049     MiscellanyCommands[] =
11050     {
11051       InfoCommand,
11052       ZoomCommand,
11053       ShowPreviewCommand,
11054       ShowHistogramCommand,
11055       ShowMatteCommand
11056     },
11057     ROICommands[] =
11058     {
11059       ROIHelpCommand,
11060       ROIDismissCommand
11061     };
11062 
11063   static const CommandType
11064     *Commands[ApplyMenus] =
11065     {
11066       FileCommands,
11067       EditCommands,
11068       TransformCommands,
11069       EnhanceCommands,
11070       EffectsCommands,
11071       FXCommands,
11072       MiscellanyCommands
11073     };
11074 
11075   char
11076     command[MaxTextExtent],
11077     text[MaxTextExtent];
11078 
11079   CommandType
11080     command_type;
11081 
11082   Cursor
11083     cursor;
11084 
11085   Image
11086     *roi_image;
11087 
11088   int
11089     entry,
11090     id,
11091     x,
11092     y;
11093 
11094   MagickRealType
11095     scale_factor;
11096 
11097   MagickProgressMonitor
11098     progress_monitor;
11099 
11100   RectangleInfo
11101     crop_info,
11102     highlight_info,
11103     roi_info;
11104 
11105   unsigned int
11106     height,
11107     width;
11108 
11109   unsigned long
11110     state;
11111 
11112   XEvent
11113     event;
11114 
11115   /*
11116     Map Command widget.
11117   */
11118   (void) CloneString(&windows->command.name,"ROI");
11119   windows->command.data=0;
11120   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11121   (void) XMapRaised(display,windows->command.id);
11122   XClientMessage(display,windows->image.id,windows->im_protocols,
11123     windows->im_update_widget,CurrentTime);
11124   /*
11125     Track pointer until button 1 is pressed.
11126   */
11127   XQueryPosition(display,windows->image.id,&x,&y);
11128   (void) XSelectInput(display,windows->image.id,
11129     windows->image.attributes.event_mask | PointerMotionMask);
11130   roi_info.x=windows->image.x+x;
11131   roi_info.y=windows->image.y+y;
11132   roi_info.width=0;
11133   roi_info.height=0;
11134   cursor=XCreateFontCursor(display,XC_fleur);
11135   state=DefaultState;
11136   do
11137   {
11138     if (windows->info.mapped != MagickFalse)
11139       {
11140         /*
11141           Display pointer position.
11142         */
11143         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
11144           roi_info.x,roi_info.y);
11145         XInfoWidget(display,windows,text);
11146       }
11147     /*
11148       Wait for next event.
11149     */
11150     XScreenEvent(display,windows,&event);
11151     if (event.xany.window == windows->command.id)
11152       {
11153         /*
11154           Select a command from the Command widget.
11155         */
11156         id=XCommandWidget(display,windows,ROIMenu,&event);
11157         if (id < 0)
11158           continue;
11159         switch (ROICommands[id])
11160         {
11161           case ROIHelpCommand:
11162           {
11163             XTextViewWidget(display,resource_info,windows,MagickFalse,
11164               "Help Viewer - Region of Interest",ImageROIHelp);
11165             break;
11166           }
11167           case ROIDismissCommand:
11168           {
11169             /*
11170               Prematurely exit.
11171             */
11172             state|=EscapeState;
11173             state|=ExitState;
11174             break;
11175           }
11176           default:
11177             break;
11178         }
11179         continue;
11180       }
11181     switch (event.type)
11182     {
11183       case ButtonPress:
11184       {
11185         if (event.xbutton.button != Button1)
11186           break;
11187         if (event.xbutton.window != windows->image.id)
11188           break;
11189         /*
11190           Note first corner of region of interest rectangle-- exit loop.
11191         */
11192         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11193         roi_info.x=windows->image.x+event.xbutton.x;
11194         roi_info.y=windows->image.y+event.xbutton.y;
11195         state|=ExitState;
11196         break;
11197       }
11198       case ButtonRelease:
11199         break;
11200       case Expose:
11201         break;
11202       case KeyPress:
11203       {
11204         KeySym
11205           key_symbol;
11206 
11207         if (event.xkey.window != windows->image.id)
11208           break;
11209         /*
11210           Respond to a user key press.
11211         */
11212         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11213           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11214         switch ((int) key_symbol)
11215         {
11216           case XK_Escape:
11217           case XK_F20:
11218           {
11219             /*
11220               Prematurely exit.
11221             */
11222             state|=EscapeState;
11223             state|=ExitState;
11224             break;
11225           }
11226           case XK_F1:
11227           case XK_Help:
11228           {
11229             XTextViewWidget(display,resource_info,windows,MagickFalse,
11230               "Help Viewer - Region of Interest",ImageROIHelp);
11231             break;
11232           }
11233           default:
11234           {
11235             (void) XBell(display,0);
11236             break;
11237           }
11238         }
11239         break;
11240       }
11241       case MotionNotify:
11242       {
11243         /*
11244           Map and unmap Info widget as text cursor crosses its boundaries.
11245         */
11246         x=event.xmotion.x;
11247         y=event.xmotion.y;
11248         if (windows->info.mapped != MagickFalse)
11249           {
11250             if ((x < (int) (windows->info.x+windows->info.width)) &&
11251                 (y < (int) (windows->info.y+windows->info.height)))
11252               (void) XWithdrawWindow(display,windows->info.id,
11253                 windows->info.screen);
11254           }
11255         else
11256           if ((x > (int) (windows->info.x+windows->info.width)) ||
11257               (y > (int) (windows->info.y+windows->info.height)))
11258             (void) XMapWindow(display,windows->info.id);
11259         roi_info.x=windows->image.x+x;
11260         roi_info.y=windows->image.y+y;
11261         break;
11262       }
11263       default:
11264         break;
11265     }
11266   } while ((state & ExitState) == 0);
11267   (void) XSelectInput(display,windows->image.id,
11268     windows->image.attributes.event_mask);
11269   if ((state & EscapeState) != 0)
11270     {
11271       /*
11272         User want to exit without region of interest.
11273       */
11274       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11275       (void) XFreeCursor(display,cursor);
11276       return(MagickTrue);
11277     }
11278   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11279   do
11280   {
11281     /*
11282       Size rectangle as pointer moves until the mouse button is released.
11283     */
11284     x=(int) roi_info.x;
11285     y=(int) roi_info.y;
11286     roi_info.width=0;
11287     roi_info.height=0;
11288     state=DefaultState;
11289     do
11290     {
11291       highlight_info=roi_info;
11292       highlight_info.x=roi_info.x-windows->image.x;
11293       highlight_info.y=roi_info.y-windows->image.y;
11294       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11295         {
11296           /*
11297             Display info and draw region of interest rectangle.
11298           */
11299           if (windows->info.mapped == MagickFalse)
11300             (void) XMapWindow(display,windows->info.id);
11301           (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
11302             roi_info.width,roi_info.height,roi_info.x,roi_info.y);
11303           XInfoWidget(display,windows,text);
11304           XHighlightRectangle(display,windows->image.id,
11305             windows->image.highlight_context,&highlight_info);
11306         }
11307       else
11308         if (windows->info.mapped != MagickFalse)
11309           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11310       /*
11311         Wait for next event.
11312       */
11313       XScreenEvent(display,windows,&event);
11314       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11315         XHighlightRectangle(display,windows->image.id,
11316           windows->image.highlight_context,&highlight_info);
11317       switch (event.type)
11318       {
11319         case ButtonPress:
11320         {
11321           roi_info.x=windows->image.x+event.xbutton.x;
11322           roi_info.y=windows->image.y+event.xbutton.y;
11323           break;
11324         }
11325         case ButtonRelease:
11326         {
11327           /*
11328             User has committed to region of interest rectangle.
11329           */
11330           roi_info.x=windows->image.x+event.xbutton.x;
11331           roi_info.y=windows->image.y+event.xbutton.y;
11332           XSetCursorState(display,windows,MagickFalse);
11333           state|=ExitState;
11334           if (LocaleCompare(windows->command.name,"Apply") == 0)
11335             break;
11336           (void) CloneString(&windows->command.name,"Apply");
11337           windows->command.data=ApplyMenus;
11338           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11339           break;
11340         }
11341         case Expose:
11342           break;
11343         case MotionNotify:
11344         {
11345           roi_info.x=windows->image.x+event.xmotion.x;
11346           roi_info.y=windows->image.y+event.xmotion.y;
11347         }
11348         default:
11349           break;
11350       }
11351       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11352           ((state & ExitState) != 0))
11353         {
11354           /*
11355             Check boundary conditions.
11356           */
11357           if (roi_info.x < 0)
11358             roi_info.x=0;
11359           else
11360             if (roi_info.x > (int) windows->image.ximage->width)
11361               roi_info.x=windows->image.ximage->width;
11362           if ((int) roi_info.x < x)
11363             roi_info.width=(unsigned int) (x-roi_info.x);
11364           else
11365             {
11366               roi_info.width=(unsigned int) (roi_info.x-x);
11367               roi_info.x=x;
11368             }
11369           if (roi_info.y < 0)
11370             roi_info.y=0;
11371           else
11372             if (roi_info.y > (int) windows->image.ximage->height)
11373               roi_info.y=windows->image.ximage->height;
11374           if ((int) roi_info.y < y)
11375             roi_info.height=(unsigned int) (y-roi_info.y);
11376           else
11377             {
11378               roi_info.height=(unsigned int) (roi_info.y-y);
11379               roi_info.y=y;
11380             }
11381         }
11382     } while ((state & ExitState) == 0);
11383     /*
11384       Wait for user to grab a corner of the rectangle or press return.
11385     */
11386     state=DefaultState;
11387     command_type=NullCommand;
11388     (void) XMapWindow(display,windows->info.id);
11389     do
11390     {
11391       if (windows->info.mapped != MagickFalse)
11392         {
11393           /*
11394             Display pointer position.
11395           */
11396           (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
11397             roi_info.width,roi_info.height,roi_info.x,roi_info.y);
11398           XInfoWidget(display,windows,text);
11399         }
11400       highlight_info=roi_info;
11401       highlight_info.x=roi_info.x-windows->image.x;
11402       highlight_info.y=roi_info.y-windows->image.y;
11403       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11404         {
11405           state|=EscapeState;
11406           state|=ExitState;
11407           break;
11408         }
11409       if ((state & UpdateRegionState) != 0)
11410         {
11411           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11412           switch (command_type)
11413           {
11414             case UndoCommand:
11415             case RedoCommand:
11416             {
11417               (void) XMagickCommand(display,resource_info,windows,command_type,
11418                 image);
11419               break;
11420             }
11421             default:
11422             {
11423               /*
11424                 Region of interest is relative to image configuration.
11425               */
11426               progress_monitor=SetImageProgressMonitor(*image,
11427                 (MagickProgressMonitor) NULL,(*image)->client_data);
11428               crop_info=roi_info;
11429               width=(unsigned int) (*image)->columns;
11430               height=(unsigned int) (*image)->rows;
11431               x=0;
11432               y=0;
11433               if (windows->image.crop_geometry != (char *) NULL)
11434                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11435                   &width,&height);
11436               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11437               crop_info.x+=x;
11438               crop_info.x=(int) (scale_factor*crop_info.x+0.5);
11439               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11440               scale_factor=(MagickRealType)
11441                 height/windows->image.ximage->height;
11442               crop_info.y+=y;
11443               crop_info.y=(int) (scale_factor*crop_info.y+0.5);
11444               crop_info.height=(unsigned int)
11445                 (scale_factor*crop_info.height+0.5);
11446               roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11447               (void) SetImageProgressMonitor(*image,progress_monitor,
11448                 (*image)->client_data);
11449               if (roi_image == (Image *) NULL)
11450                 continue;
11451               /*
11452                 Apply image processing technique to the region of interest.
11453               */
11454               windows->image.orphan=MagickTrue;
11455               (void) XMagickCommand(display,resource_info,windows,command_type,
11456                 &roi_image);
11457               progress_monitor=SetImageProgressMonitor(*image,
11458                 (MagickProgressMonitor) NULL,(*image)->client_data);
11459               (void) XMagickCommand(display,resource_info,windows,
11460                 SaveToUndoBufferCommand,image);
11461               windows->image.orphan=MagickFalse;
11462               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11463                 crop_info.x,crop_info.y);
11464               roi_image=DestroyImage(roi_image);
11465               (void) SetImageProgressMonitor(*image,progress_monitor,
11466                 (*image)->client_data);
11467               break;
11468             }
11469           }
11470           if (command_type != InfoCommand)
11471             {
11472               XConfigureImageColormap(display,resource_info,windows,*image);
11473               (void) XConfigureImage(display,resource_info,windows,*image);
11474             }
11475           XCheckRefreshWindows(display,windows);
11476           XInfoWidget(display,windows,text);
11477           (void) XSetFunction(display,windows->image.highlight_context,
11478             GXinvert);
11479           state&=(~UpdateRegionState);
11480         }
11481       XHighlightRectangle(display,windows->image.id,
11482         windows->image.highlight_context,&highlight_info);
11483       XScreenEvent(display,windows,&event);
11484       if (event.xany.window == windows->command.id)
11485         {
11486           /*
11487             Select a command from the Command widget.
11488           */
11489           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11490           command_type=NullCommand;
11491           id=XCommandWidget(display,windows,ApplyMenu,&event);
11492           if (id >= 0)
11493             {
11494               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11495               command_type=ApplyCommands[id];
11496               if (id < ApplyMenus)
11497                 {
11498                   /*
11499                     Select a command from a pop-up menu.
11500                   */
11501                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11502                     (const char **) Menus[id],command);
11503                   if (entry >= 0)
11504                     {
11505                       (void) CopyMagickString(command,Menus[id][entry],
11506                         MaxTextExtent);
11507                       command_type=Commands[id][entry];
11508                     }
11509                 }
11510             }
11511           (void) XSetFunction(display,windows->image.highlight_context,
11512             GXinvert);
11513           XHighlightRectangle(display,windows->image.id,
11514             windows->image.highlight_context,&highlight_info);
11515           if (command_type == HelpCommand)
11516             {
11517               (void) XSetFunction(display,windows->image.highlight_context,
11518                 GXcopy);
11519               XTextViewWidget(display,resource_info,windows,MagickFalse,
11520                 "Help Viewer - Region of Interest",ImageROIHelp);
11521               (void) XSetFunction(display,windows->image.highlight_context,
11522                 GXinvert);
11523               continue;
11524             }
11525           if (command_type == QuitCommand)
11526             {
11527               /*
11528                 exit.
11529               */
11530               state|=EscapeState;
11531               state|=ExitState;
11532               continue;
11533             }
11534           if (command_type != NullCommand)
11535             state|=UpdateRegionState;
11536           continue;
11537         }
11538       XHighlightRectangle(display,windows->image.id,
11539         windows->image.highlight_context,&highlight_info);
11540       switch (event.type)
11541       {
11542         case ButtonPress:
11543         {
11544           x=windows->image.x;
11545           y=windows->image.y;
11546           if (event.xbutton.button != Button1)
11547             break;
11548           if (event.xbutton.window != windows->image.id)
11549             break;
11550           x=windows->image.x+event.xbutton.x;
11551           y=windows->image.y+event.xbutton.y;
11552           if ((x < (int) (roi_info.x+RoiDelta)) &&
11553               (x > (int) (roi_info.x-RoiDelta)) &&
11554               (y < (int) (roi_info.y+RoiDelta)) &&
11555               (y > (int) (roi_info.y-RoiDelta)))
11556             {
11557               roi_info.x=(long) (roi_info.x+roi_info.width);
11558               roi_info.y=(long) (roi_info.y+roi_info.height);
11559               state|=UpdateConfigurationState;
11560               break;
11561             }
11562           if ((x < (int) (roi_info.x+RoiDelta)) &&
11563               (x > (int) (roi_info.x-RoiDelta)) &&
11564               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11565               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11566             {
11567               roi_info.x=(long) (roi_info.x+roi_info.width);
11568               state|=UpdateConfigurationState;
11569               break;
11570             }
11571           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11572               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11573               (y < (int) (roi_info.y+RoiDelta)) &&
11574               (y > (int) (roi_info.y-RoiDelta)))
11575             {
11576               roi_info.y=(long) (roi_info.y+roi_info.height);
11577               state|=UpdateConfigurationState;
11578               break;
11579             }
11580           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11581               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11582               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11583               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11584             {
11585               state|=UpdateConfigurationState;
11586               break;
11587             }
11588         }
11589         case ButtonRelease:
11590         {
11591           if (event.xbutton.window == windows->pan.id)
11592             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11593                 (highlight_info.y != crop_info.y-windows->image.y))
11594               XHighlightRectangle(display,windows->image.id,
11595                 windows->image.highlight_context,&highlight_info);
11596           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11597             event.xbutton.time);
11598           break;
11599         }
11600         case Expose:
11601         {
11602           if (event.xexpose.window == windows->image.id)
11603             if (event.xexpose.count == 0)
11604               {
11605                 event.xexpose.x=(int) highlight_info.x;
11606                 event.xexpose.y=(int) highlight_info.y;
11607                 event.xexpose.width=(int) highlight_info.width;
11608                 event.xexpose.height=(int) highlight_info.height;
11609                 XRefreshWindow(display,&windows->image,&event);
11610               }
11611           if (event.xexpose.window == windows->info.id)
11612             if (event.xexpose.count == 0)
11613               XInfoWidget(display,windows,text);
11614           break;
11615         }
11616         case KeyPress:
11617         {
11618           KeySym
11619             key_symbol;
11620 
11621           if (event.xkey.window != windows->image.id)
11622             break;
11623           /*
11624             Respond to a user key press.
11625           */
11626           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11627             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11628           switch ((int) key_symbol)
11629           {
11630             case XK_Shift_L:
11631             case XK_Shift_R:
11632               break;
11633             case XK_Escape:
11634             case XK_F20:
11635               state|=EscapeState;
11636             case XK_Return:
11637             {
11638               state|=ExitState;
11639               break;
11640             }
11641             case XK_Home:
11642             case XK_KP_Home:
11643             {
11644               roi_info.x=(long) (windows->image.width/2L-roi_info.width/2L);
11645               roi_info.y=(long) (windows->image.height/2L-roi_info.height/2L);
11646               break;
11647             }
11648             case XK_Left:
11649             case XK_KP_Left:
11650             {
11651               roi_info.x--;
11652               break;
11653             }
11654             case XK_Up:
11655             case XK_KP_Up:
11656             case XK_Next:
11657             {
11658               roi_info.y--;
11659               break;
11660             }
11661             case XK_Right:
11662             case XK_KP_Right:
11663             {
11664               roi_info.x++;
11665               break;
11666             }
11667             case XK_Prior:
11668             case XK_Down:
11669             case XK_KP_Down:
11670             {
11671               roi_info.y++;
11672               break;
11673             }
11674             case XK_F1:
11675             case XK_Help:
11676             {
11677               (void) XSetFunction(display,windows->image.highlight_context,
11678                 GXcopy);
11679               XTextViewWidget(display,resource_info,windows,MagickFalse,
11680                 "Help Viewer - Region of Interest",ImageROIHelp);
11681               (void) XSetFunction(display,windows->image.highlight_context,
11682                 GXinvert);
11683               break;
11684             }
11685             default:
11686             {
11687               command_type=XImageWindowCommand(display,resource_info,windows,
11688                 event.xkey.state,key_symbol,image);
11689               if (command_type != NullCommand)
11690                 state|=UpdateRegionState;
11691               break;
11692             }
11693           }
11694           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11695             event.xkey.time);
11696           break;
11697         }
11698         case KeyRelease:
11699           break;
11700         case MotionNotify:
11701         {
11702           if (event.xbutton.window != windows->image.id)
11703             break;
11704           /*
11705             Map and unmap Info widget as text cursor crosses its boundaries.
11706           */
11707           x=event.xmotion.x;
11708           y=event.xmotion.y;
11709           if (windows->info.mapped != MagickFalse)
11710             {
11711               if ((x < (int) (windows->info.x+windows->info.width)) &&
11712                   (y < (int) (windows->info.y+windows->info.height)))
11713                 (void) XWithdrawWindow(display,windows->info.id,
11714                   windows->info.screen);
11715             }
11716           else
11717             if ((x > (int) (windows->info.x+windows->info.width)) ||
11718                 (y > (int) (windows->info.y+windows->info.height)))
11719               (void) XMapWindow(display,windows->info.id);
11720           roi_info.x=windows->image.x+event.xmotion.x;
11721           roi_info.y=windows->image.y+event.xmotion.y;
11722           break;
11723         }
11724         case SelectionRequest:
11725         {
11726           XSelectionEvent
11727             notify;
11728 
11729           XSelectionRequestEvent
11730             *request;
11731 
11732           /*
11733             Set primary selection.
11734           */
11735           (void) FormatMagickString(text,MaxTextExtent,"%lux%lu%+ld%+ld",
11736             roi_info.width,roi_info.height,roi_info.x,roi_info.y);
11737           request=(&(event.xselectionrequest));
11738           (void) XChangeProperty(request->display,request->requestor,
11739             request->property,request->target,8,PropModeReplace,
11740             (unsigned char *) text,(int) strlen(text));
11741           notify.type=SelectionNotify;
11742           notify.display=request->display;
11743           notify.requestor=request->requestor;
11744           notify.selection=request->selection;
11745           notify.target=request->target;
11746           notify.time=request->time;
11747           if (request->property == None)
11748             notify.property=request->target;
11749           else
11750             notify.property=request->property;
11751           (void) XSendEvent(request->display,request->requestor,False,0,
11752             (XEvent *) &notify);
11753         }
11754         default:
11755           break;
11756       }
11757       if ((state & UpdateConfigurationState) != 0)
11758         {
11759           (void) XPutBackEvent(display,&event);
11760           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11761           break;
11762         }
11763     } while ((state & ExitState) == 0);
11764   } while ((state & ExitState) == 0);
11765   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11766   XSetCursorState(display,windows,MagickFalse);
11767   if ((state & EscapeState) != 0)
11768     return(MagickTrue);
11769   return(MagickTrue);
11770 }
11771 
11772 /*
11773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11774 %                                                                             %
11775 %                                                                             %
11776 %                                                                             %
11777 +   X R o t a t e I m a g e                                                   %
11778 %                                                                             %
11779 %                                                                             %
11780 %                                                                             %
11781 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11782 %
11783 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11784 %  rotation angle is computed from the slope of a line drawn by the user.
11785 %
11786 %  The format of the XRotateImage method is:
11787 %
11788 %      MagickBooleanType XRotateImage(Display *display,
11789 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11790 %        Image **image)
11791 %
11792 %  A description of each parameter follows:
11793 %
11794 %    o display: Specifies a connection to an X server; returned from
11795 %      XOpenDisplay.
11796 %
11797 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11798 %
11799 %    o windows: Specifies a pointer to a XWindows structure.
11800 %
11801 %    o degrees: Specifies the number of degrees to rotate the image.
11802 %
11803 %    o image: the image;  returned from
11804 %      ReadImage.
11805 %
11806 */
11807 static MagickBooleanType XRotateImage(Display *display,
11808   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11809 {
11810   static const char
11811     *RotateMenu[] =
11812     {
11813       "Pixel Color",
11814       "Direction",
11815       "Help",
11816       "Dismiss",
11817       (char *) NULL
11818     };
11819 
11820   static ModeType
11821     direction = HorizontalRotateCommand;
11822 
11823   static const ModeType
11824     DirectionCommands[] =
11825     {
11826       HorizontalRotateCommand,
11827       VerticalRotateCommand
11828     },
11829     RotateCommands[] =
11830     {
11831       RotateColorCommand,
11832       RotateDirectionCommand,
11833       RotateHelpCommand,
11834       RotateDismissCommand
11835     };
11836 
11837   static unsigned int
11838     pen_id = 0;
11839 
11840   char
11841     command[MaxTextExtent],
11842     text[MaxTextExtent];
11843 
11844   Image
11845     *rotate_image;
11846 
11847   int
11848     id,
11849     x,
11850     y;
11851 
11852   MagickRealType
11853     normalized_degrees;
11854 
11855   register int
11856     i;
11857 
11858   unsigned int
11859     height,
11860     rotations,
11861     width;
11862 
11863   if (degrees == 0.0)
11864     {
11865       unsigned int
11866         distance;
11867 
11868       unsigned long
11869         state;
11870 
11871       XEvent
11872         event;
11873 
11874       XSegment
11875         rotate_info;
11876 
11877       /*
11878         Map Command widget.
11879       */
11880       (void) CloneString(&windows->command.name,"Rotate");
11881       windows->command.data=2;
11882       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11883       (void) XMapRaised(display,windows->command.id);
11884       XClientMessage(display,windows->image.id,windows->im_protocols,
11885         windows->im_update_widget,CurrentTime);
11886       /*
11887         Wait for first button press.
11888       */
11889       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11890       XQueryPosition(display,windows->image.id,&x,&y);
11891       rotate_info.x1=x;
11892       rotate_info.y1=y;
11893       rotate_info.x2=x;
11894       rotate_info.y2=y;
11895       state=DefaultState;
11896       do
11897       {
11898         XHighlightLine(display,windows->image.id,
11899           windows->image.highlight_context,&rotate_info);
11900         /*
11901           Wait for next event.
11902         */
11903         XScreenEvent(display,windows,&event);
11904         XHighlightLine(display,windows->image.id,
11905           windows->image.highlight_context,&rotate_info);
11906         if (event.xany.window == windows->command.id)
11907           {
11908             /*
11909               Select a command from the Command widget.
11910             */
11911             id=XCommandWidget(display,windows,RotateMenu,&event);
11912             if (id < 0)
11913               continue;
11914             (void) XSetFunction(display,windows->image.highlight_context,
11915               GXcopy);
11916             switch (RotateCommands[id])
11917             {
11918               case RotateColorCommand:
11919               {
11920                 const char
11921                   *ColorMenu[MaxNumberPens];
11922 
11923                 int
11924                   pen_number;
11925 
11926                 XColor
11927                   color;
11928 
11929                 /*
11930                   Initialize menu selections.
11931                 */
11932                 for (i=0; i < (int) (MaxNumberPens-2); i++)
11933                   ColorMenu[i]=resource_info->pen_colors[i];
11934                 ColorMenu[MaxNumberPens-2]="Browser...";
11935                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
11936                 /*
11937                   Select a pen color from the pop-up menu.
11938                 */
11939                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
11940                   (const char **) ColorMenu,command);
11941                 if (pen_number < 0)
11942                   break;
11943                 if (pen_number == (MaxNumberPens-2))
11944                   {
11945                     static char
11946                       color_name[MaxTextExtent] = "gray";
11947 
11948                     /*
11949                       Select a pen color from a dialog.
11950                     */
11951                     resource_info->pen_colors[pen_number]=color_name;
11952                     XColorBrowserWidget(display,windows,"Select",color_name);
11953                     if (*color_name == '\0')
11954                       break;
11955                   }
11956                 /*
11957                   Set pen color.
11958                 */
11959                 (void) XParseColor(display,windows->map_info->colormap,
11960                   resource_info->pen_colors[pen_number],&color);
11961                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
11962                   (unsigned int) MaxColors,&color);
11963                 windows->pixel_info->pen_colors[pen_number]=color;
11964                 pen_id=(unsigned int) pen_number;
11965                 break;
11966               }
11967               case RotateDirectionCommand:
11968               {
11969                 static const char
11970                   *Directions[] =
11971                   {
11972                     "horizontal",
11973                     "vertical",
11974                     (char *) NULL,
11975                   };
11976 
11977                 /*
11978                   Select a command from the pop-up menu.
11979                 */
11980                 id=XMenuWidget(display,windows,RotateMenu[id],
11981                   Directions,command);
11982                 if (id >= 0)
11983                   direction=DirectionCommands[id];
11984                 break;
11985               }
11986               case RotateHelpCommand:
11987               {
11988                 XTextViewWidget(display,resource_info,windows,MagickFalse,
11989                   "Help Viewer - Image Rotation",ImageRotateHelp);
11990                 break;
11991               }
11992               case RotateDismissCommand:
11993               {
11994                 /*
11995                   Prematurely exit.
11996                 */
11997                 state|=EscapeState;
11998                 state|=ExitState;
11999                 break;
12000               }
12001               default:
12002                 break;
12003             }
12004             (void) XSetFunction(display,windows->image.highlight_context,
12005               GXinvert);
12006             continue;
12007           }
12008         switch (event.type)
12009         {
12010           case ButtonPress:
12011           {
12012             if (event.xbutton.button != Button1)
12013               break;
12014             if (event.xbutton.window != windows->image.id)
12015               break;
12016             /*
12017               exit loop.
12018             */
12019             (void) XSetFunction(display,windows->image.highlight_context,
12020               GXcopy);
12021             rotate_info.x1=event.xbutton.x;
12022             rotate_info.y1=event.xbutton.y;
12023             state|=ExitState;
12024             break;
12025           }
12026           case ButtonRelease:
12027             break;
12028           case Expose:
12029             break;
12030           case KeyPress:
12031           {
12032             char
12033               command[MaxTextExtent];
12034 
12035             KeySym
12036               key_symbol;
12037 
12038             if (event.xkey.window != windows->image.id)
12039               break;
12040             /*
12041               Respond to a user key press.
12042             */
12043             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12044               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12045             switch ((int) key_symbol)
12046             {
12047               case XK_Escape:
12048               case XK_F20:
12049               {
12050                 /*
12051                   Prematurely exit.
12052                 */
12053                 state|=EscapeState;
12054                 state|=ExitState;
12055                 break;
12056               }
12057               case XK_F1:
12058               case XK_Help:
12059               {
12060                 (void) XSetFunction(display,windows->image.highlight_context,
12061                   GXcopy);
12062                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12063                   "Help Viewer - Image Rotation",ImageRotateHelp);
12064                 (void) XSetFunction(display,windows->image.highlight_context,
12065                   GXinvert);
12066                 break;
12067               }
12068               default:
12069               {
12070                 (void) XBell(display,0);
12071                 break;
12072               }
12073             }
12074             break;
12075           }
12076           case MotionNotify:
12077           {
12078             rotate_info.x1=event.xmotion.x;
12079             rotate_info.y1=event.xmotion.y;
12080           }
12081         }
12082         rotate_info.x2=rotate_info.x1;
12083         rotate_info.y2=rotate_info.y1;
12084         if (direction == HorizontalRotateCommand)
12085           rotate_info.x2+=32;
12086         else
12087           rotate_info.y2-=32;
12088       } while ((state & ExitState) == 0);
12089       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12090       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12091       if ((state & EscapeState) != 0)
12092         return(MagickTrue);
12093       /*
12094         Draw line as pointer moves until the mouse button is released.
12095       */
12096       distance=0;
12097       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12098       state=DefaultState;
12099       do
12100       {
12101         if (distance > 9)
12102           {
12103             /*
12104               Display info and draw rotation line.
12105             */
12106             if (windows->info.mapped == MagickFalse)
12107               (void) XMapWindow(display,windows->info.id);
12108             (void) FormatMagickString(text,MaxTextExtent," %g",
12109               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12110             XInfoWidget(display,windows,text);
12111             XHighlightLine(display,windows->image.id,
12112               windows->image.highlight_context,&rotate_info);
12113           }
12114         else
12115           if (windows->info.mapped != MagickFalse)
12116             (void) XWithdrawWindow(display,windows->info.id,
12117               windows->info.screen);
12118         /*
12119           Wait for next event.
12120         */
12121         XScreenEvent(display,windows,&event);
12122         if (distance > 9)
12123           XHighlightLine(display,windows->image.id,
12124             windows->image.highlight_context,&rotate_info);
12125         switch (event.type)
12126         {
12127           case ButtonPress:
12128             break;
12129           case ButtonRelease:
12130           {
12131             /*
12132               User has committed to rotation line.
12133             */
12134             rotate_info.x2=event.xbutton.x;
12135             rotate_info.y2=event.xbutton.y;
12136             state|=ExitState;
12137             break;
12138           }
12139           case Expose:
12140             break;
12141           case MotionNotify:
12142           {
12143             rotate_info.x2=event.xmotion.x;
12144             rotate_info.y2=event.xmotion.y;
12145           }
12146           default:
12147             break;
12148         }
12149         /*
12150           Check boundary conditions.
12151         */
12152         if (rotate_info.x2 < 0)
12153           rotate_info.x2=0;
12154         else
12155           if (rotate_info.x2 > (int) windows->image.width)
12156             rotate_info.x2=(short) windows->image.width;
12157         if (rotate_info.y2 < 0)
12158           rotate_info.y2=0;
12159         else
12160           if (rotate_info.y2 > (int) windows->image.height)
12161             rotate_info.y2=(short) windows->image.height;
12162         /*
12163           Compute rotation angle from the slope of the line.
12164         */
12165         degrees=0.0;
12166         distance=(unsigned int)
12167           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12168           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12169         if (distance > 9)
12170           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12171             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12172       } while ((state & ExitState) == 0);
12173       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12174       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12175       if (distance <= 9)
12176         return(MagickTrue);
12177     }
12178   if (direction == VerticalRotateCommand)
12179     degrees-=90.0;
12180   if (degrees == 0.0)
12181     return(MagickTrue);
12182   /*
12183     Rotate image.
12184   */
12185   normalized_degrees=degrees;
12186   while (normalized_degrees < -45.0)
12187     normalized_degrees+=360.0;
12188   for (rotations=0; normalized_degrees > 45.0; rotations++)
12189     normalized_degrees-=90.0;
12190   if (normalized_degrees != 0.0)
12191     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12192   XSetCursorState(display,windows,MagickTrue);
12193   XCheckRefreshWindows(display,windows);
12194   (*image)->background_color.red=ScaleShortToQuantum(
12195     windows->pixel_info->pen_colors[pen_id].red);
12196   (*image)->background_color.green=ScaleShortToQuantum(
12197     windows->pixel_info->pen_colors[pen_id].green);
12198   (*image)->background_color.blue=ScaleShortToQuantum(
12199     windows->pixel_info->pen_colors[pen_id].blue);
12200   rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12201   XSetCursorState(display,windows,MagickFalse);
12202   if (rotate_image == (Image *) NULL)
12203     return(MagickFalse);
12204   *image=DestroyImage(*image);
12205   *image=rotate_image;
12206   if (windows->image.crop_geometry != (char *) NULL)
12207     {
12208       /*
12209         Rotate crop geometry.
12210       */
12211       width=(unsigned int) (*image)->columns;
12212       height=(unsigned int) (*image)->rows;
12213       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12214       switch (rotations % 4)
12215       {
12216         default:
12217         case 0:
12218           break;
12219         case 1:
12220         {
12221           /*
12222             Rotate 90 degrees.
12223           */
12224           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12225             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12226             (int) height-y,x);
12227           break;
12228         }
12229         case 2:
12230         {
12231           /*
12232             Rotate 180 degrees.
12233           */
12234           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12235             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12236           break;
12237         }
12238         case 3:
12239         {
12240           /*
12241             Rotate 270 degrees.
12242           */
12243           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12244             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12245           break;
12246         }
12247       }
12248     }
12249   if (windows->image.orphan != MagickFalse)
12250     return(MagickTrue);
12251   if (normalized_degrees != 0.0)
12252     {
12253       /*
12254         Update image colormap.
12255       */
12256       windows->image.window_changes.width=(int) (*image)->columns;
12257       windows->image.window_changes.height=(int) (*image)->rows;
12258       if (windows->image.crop_geometry != (char *) NULL)
12259         {
12260           /*
12261             Obtain dimensions of image from crop geometry.
12262           */
12263           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12264             &width,&height);
12265           windows->image.window_changes.width=(int) width;
12266           windows->image.window_changes.height=(int) height;
12267         }
12268       XConfigureImageColormap(display,resource_info,windows,*image);
12269     }
12270   else
12271     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12272       {
12273         windows->image.window_changes.width=windows->image.ximage->height;
12274         windows->image.window_changes.height=windows->image.ximage->width;
12275       }
12276   /*
12277     Update image configuration.
12278   */
12279   (void) XConfigureImage(display,resource_info,windows,*image);
12280   return(MagickTrue);
12281 }
12282 
12283 /*
12284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12285 %                                                                             %
12286 %                                                                             %
12287 %                                                                             %
12288 +   X S a v e I m a g e                                                       %
12289 %                                                                             %
12290 %                                                                             %
12291 %                                                                             %
12292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12293 %
12294 %  XSaveImage() saves an image to a file.
12295 %
12296 %  The format of the XSaveImage method is:
12297 %
12298 %      MagickBooleanType XSaveImage(Display *display,
12299 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
12300 %
12301 %  A description of each parameter follows:
12302 %
12303 %    o display: Specifies a connection to an X server; returned from
12304 %      XOpenDisplay.
12305 %
12306 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12307 %
12308 %    o windows: Specifies a pointer to a XWindows structure.
12309 %
12310 %    o image: the image;  returned from
12311 %      ReadImage.
12312 %
12313 */
12314 static MagickBooleanType XSaveImage(Display *display,
12315   XResourceInfo *resource_info,XWindows *windows,Image *image)
12316 {
12317   char
12318     filename[MaxTextExtent],
12319     geometry[MaxTextExtent];
12320 
12321   Image
12322     *save_image;
12323 
12324   ImageInfo
12325     *image_info;
12326 
12327   MagickStatusType
12328     status;
12329 
12330   /*
12331     Request file name from user.
12332   */
12333   if (resource_info->write_filename != (char *) NULL)
12334     (void) CopyMagickString(filename,resource_info->write_filename,
12335       MaxTextExtent);
12336   else
12337     {
12338       char
12339         path[MaxTextExtent];
12340 
12341       int
12342         status;
12343 
12344       GetPathComponent(image->filename,HeadPath,path);
12345       GetPathComponent(image->filename,TailPath,filename);
12346       status=chdir(path);
12347       if (status == -1)
12348         (void) ThrowMagickException(&image->exception,GetMagickModule(),
12349           FileOpenError,"UnableToOpenFile","%s",path);
12350     }
12351   XFileBrowserWidget(display,windows,"Save",filename);
12352   if (*filename == '\0')
12353     return(MagickTrue);
12354   if (IsPathAccessible(filename) != MagickFalse)
12355     {
12356       int
12357         status;
12358 
12359       /*
12360         File exists-- seek user's permission before overwriting.
12361       */
12362       status=XConfirmWidget(display,windows,"Overwrite",filename);
12363       if (status <= 0)
12364         return(MagickTrue);
12365     }
12366   image_info=CloneImageInfo(resource_info->image_info);
12367   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12368   (void) SetImageInfo(image_info,MagickFalse,&image->exception);
12369   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12370       (LocaleCompare(image_info->magick,"JPG") == 0))
12371     {
12372       char
12373         quality[MaxTextExtent];
12374 
12375       int
12376         status;
12377 
12378       /*
12379         Request JPEG quality from user.
12380       */
12381       (void) FormatMagickString(quality,MaxTextExtent,"%lu",image->quality);
12382       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12383         quality);
12384       if (*quality == '\0')
12385         return(MagickTrue);
12386       image->quality=(unsigned long) atol(quality);
12387       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12388     }
12389   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12390       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12391       (LocaleCompare(image_info->magick,"PS") == 0) ||
12392       (LocaleCompare(image_info->magick,"PS2") == 0))
12393     {
12394       char
12395         geometry[MaxTextExtent];
12396 
12397       /*
12398         Request page geometry from user.
12399       */
12400       (void) FormatMagickString(geometry,MaxTextExtent,PSPageGeometry);
12401       if (LocaleCompare(image_info->magick,"PDF") == 0)
12402         (void) FormatMagickString(geometry,MaxTextExtent,PSPageGeometry);
12403       if (image_info->page != (char *) NULL)
12404         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12405       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12406         "Select page geometry:",geometry);
12407       if (*geometry != '\0')
12408         image_info->page=GetPageGeometry(geometry);
12409     }
12410   /*
12411     Apply image transforms.
12412   */
12413   XSetCursorState(display,windows,MagickTrue);
12414   XCheckRefreshWindows(display,windows);
12415   save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12416   if (save_image == (Image *) NULL)
12417     return(MagickFalse);
12418   (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
12419     windows->image.ximage->width,windows->image.ximage->height);
12420   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12421   /*
12422     Write image.
12423   */
12424   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12425   status=WriteImage(image_info,save_image);
12426   if (status != MagickFalse)
12427     image->taint=MagickFalse;
12428   save_image=DestroyImage(save_image);
12429   image_info=DestroyImageInfo(image_info);
12430   XSetCursorState(display,windows,MagickFalse);
12431   return(status != 0 ? MagickTrue : MagickFalse);
12432 }
12433 
12434 /*
12435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12436 %                                                                             %
12437 %                                                                             %
12438 %                                                                             %
12439 +   X S c r e e n E v e n t                                                   %
12440 %                                                                             %
12441 %                                                                             %
12442 %                                                                             %
12443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12444 %
12445 %  XScreenEvent() handles global events associated with the Pan and Magnify
12446 %  windows.
12447 %
12448 %  The format of the XScreenEvent function is:
12449 %
12450 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12451 %
12452 %  A description of each parameter follows:
12453 %
12454 %    o display: Specifies a pointer to the Display structure;  returned from
12455 %      XOpenDisplay.
12456 %
12457 %    o windows: Specifies a pointer to a XWindows structure.
12458 %
12459 %    o event: Specifies a pointer to a X11 XEvent structure.
12460 %
12461 %
12462 */
12463 
12464 #if defined(__cplusplus) || defined(c_plusplus)
12465 extern "C" {
12466 #endif
12467 
12468 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12469 {
12470   register XWindows
12471     *windows;
12472 
12473   windows=(XWindows *) data;
12474   if ((event->type == ClientMessage) &&
12475       (event->xclient.window == windows->image.id))
12476     return(MagickFalse);
12477   return(MagickTrue);
12478 }
12479 
12480 #if defined(__cplusplus) || defined(c_plusplus)
12481 }
12482 #endif
12483 
12484 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12485 {
12486   register int
12487     x,
12488     y;
12489 
12490   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12491   if (event->xany.window == windows->command.id)
12492     return;
12493   switch (event->type)
12494   {
12495     case ButtonPress:
12496     case ButtonRelease:
12497     {
12498       if ((event->xbutton.button == Button3) &&
12499           (event->xbutton.state & Mod1Mask))
12500         {
12501           /*
12502             Convert Alt-Button3 to Button2.
12503           */
12504           event->xbutton.button=Button2;
12505           event->xbutton.state&=(~Mod1Mask);
12506         }
12507       if (event->xbutton.window == windows->backdrop.id)
12508         {
12509           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12510             event->xbutton.time);
12511           break;
12512         }
12513       if (event->xbutton.window == windows->pan.id)
12514         {
12515           XPanImage(display,windows,event);
12516           break;
12517         }
12518       if (event->xbutton.window == windows->image.id)
12519         if (event->xbutton.button == Button2)
12520           {
12521             /*
12522               Update magnified image.
12523             */
12524             x=event->xbutton.x;
12525             y=event->xbutton.y;
12526             if (x < 0)
12527               x=0;
12528             else
12529               if (x >= (int) windows->image.width)
12530                 x=(int) (windows->image.width-1);
12531             windows->magnify.x=windows->image.x+x;
12532             if (y < 0)
12533               y=0;
12534             else
12535              if (y >= (int) windows->image.height)
12536                y=(int) (windows->image.height-1);
12537             windows->magnify.y=windows->image.y+y;
12538             if (windows->magnify.mapped == MagickFalse)
12539               (void) XMapRaised(display,windows->magnify.id);
12540             XMakeMagnifyImage(display,windows);
12541             if (event->type == ButtonRelease)
12542               (void) XWithdrawWindow(display,windows->info.id,
12543                 windows->info.screen);
12544             break;
12545           }
12546       break;
12547     }
12548     case ClientMessage:
12549     {
12550       /*
12551         If client window delete message, exit.
12552       */
12553       if (event->xclient.message_type != windows->wm_protocols)
12554         break;
12555       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12556         break;
12557       if (event->xclient.window == windows->magnify.id)
12558         {
12559           (void) XWithdrawWindow(display,windows->magnify.id,
12560             windows->magnify.screen);
12561           break;
12562         }
12563       break;
12564     }
12565     case ConfigureNotify:
12566     {
12567       if (event->xconfigure.window == windows->magnify.id)
12568         {
12569           unsigned int
12570             magnify;
12571 
12572           /*
12573             Magnify window has a new configuration.
12574           */
12575           windows->magnify.width=(unsigned int) event->xconfigure.width;
12576           windows->magnify.height=(unsigned int) event->xconfigure.height;
12577           if (windows->magnify.mapped == MagickFalse)
12578             break;
12579           magnify=1;
12580           while ((int) magnify <= event->xconfigure.width)
12581             magnify<<=1;
12582           while ((int) magnify <= event->xconfigure.height)
12583             magnify<<=1;
12584           magnify>>=1;
12585           if (((int) magnify != event->xconfigure.width) ||
12586               ((int) magnify != event->xconfigure.height))
12587             {
12588               XWindowChanges
12589                 window_changes;
12590 
12591               window_changes.width=(int) magnify;
12592               window_changes.height=(int) magnify;
12593               (void) XReconfigureWMWindow(display,windows->magnify.id,
12594                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12595                 &window_changes);
12596               break;
12597             }
12598           XMakeMagnifyImage(display,windows);
12599           break;
12600         }
12601       break;
12602     }
12603     case Expose:
12604     {
12605       if (event->xexpose.window == windows->image.id)
12606         {
12607           XRefreshWindow(display,&windows->image,event);
12608           break;
12609         }
12610       if (event->xexpose.window == windows->pan.id)
12611         if (event->xexpose.count == 0)
12612           {
12613             XDrawPanRectangle(display,windows);
12614             break;
12615           }
12616       if (event->xexpose.window == windows->magnify.id)
12617         if (event->xexpose.count == 0)
12618           {
12619             XMakeMagnifyImage(display,windows);
12620             break;
12621           }
12622       break;
12623     }
12624     case KeyPress:
12625     {
12626       char
12627         command[MaxTextExtent];
12628 
12629       KeySym
12630         key_symbol;
12631 
12632       if (event->xkey.window != windows->magnify.id)
12633         break;
12634       /*
12635         Respond to a user key press.
12636       */
12637       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12638         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12639       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12640       break;
12641     }
12642     case MapNotify:
12643     {
12644       if (event->xmap.window == windows->magnify.id)
12645         {
12646           windows->magnify.mapped=MagickTrue;
12647           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12648           break;
12649         }
12650       if (event->xmap.window == windows->info.id)
12651         {
12652           windows->info.mapped=MagickTrue;
12653           break;
12654         }
12655       break;
12656     }
12657     case MotionNotify:
12658     {
12659       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12660       if (event->xmotion.window == windows->image.id)
12661         if (windows->magnify.mapped != MagickFalse)
12662           {
12663             /*
12664               Update magnified image.
12665             */
12666             x=event->xmotion.x;
12667             y=event->xmotion.y;
12668             if (x < 0)
12669               x=0;
12670             else
12671               if (x >= (int) windows->image.width)
12672                 x=(int) (windows->image.width-1);
12673             windows->magnify.x=windows->image.x+x;
12674             if (y < 0)
12675               y=0;
12676             else
12677              if (y >= (int) windows->image.height)
12678                y=(int) (windows->image.height-1);
12679             windows->magnify.y=windows->image.y+y;
12680             XMakeMagnifyImage(display,windows);
12681           }
12682       break;
12683     }
12684     case UnmapNotify:
12685     {
12686       if (event->xunmap.window == windows->magnify.id)
12687         {
12688           windows->magnify.mapped=MagickFalse;
12689           break;
12690         }
12691       if (event->xunmap.window == windows->info.id)
12692         {
12693           windows->info.mapped=MagickFalse;
12694           break;
12695         }
12696       break;
12697     }
12698     default:
12699       break;
12700   }
12701 }
12702 
12703 /*
12704 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12705 %                                                                             %
12706 %                                                                             %
12707 %                                                                             %
12708 +   X S e t C r o p G e o m e t r y                                           %
12709 %                                                                             %
12710 %                                                                             %
12711 %                                                                             %
12712 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12713 %
12714 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12715 %  and translates it to a cropping geometry relative to the image.
12716 %
12717 %  The format of the XSetCropGeometry method is:
12718 %
12719 %      void XSetCropGeometry(Display *display,XWindows *windows,
12720 %        RectangleInfo *crop_info,Image *image)
12721 %
12722 %  A description of each parameter follows:
12723 %
12724 %    o display: Specifies a connection to an X server; returned from
12725 %      XOpenDisplay.
12726 %
12727 %    o windows: Specifies a pointer to a XWindows structure.
12728 %
12729 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12730 %      Image window to crop.
12731 %
12732 %    o image: the image.
12733 %
12734 */
12735 static void XSetCropGeometry(Display *display,XWindows *windows,
12736   RectangleInfo *crop_info,Image *image)
12737 {
12738   char
12739     text[MaxTextExtent];
12740 
12741   int
12742     x,
12743     y;
12744 
12745   MagickRealType
12746     scale_factor;
12747 
12748   unsigned int
12749     height,
12750     width;
12751 
12752   if (windows->info.mapped != MagickFalse)
12753     {
12754       /*
12755         Display info on cropping rectangle.
12756       */
12757       (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
12758         crop_info->width,crop_info->height,crop_info->x,crop_info->y);
12759       XInfoWidget(display,windows,text);
12760     }
12761   /*
12762     Cropping geometry is relative to any previous crop geometry.
12763   */
12764   x=0;
12765   y=0;
12766   width=(unsigned int) image->columns;
12767   height=(unsigned int) image->rows;
12768   if (windows->image.crop_geometry != (char *) NULL)
12769     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12770   else
12771     windows->image.crop_geometry=AcquireString((char *) NULL);
12772   /*
12773     Define the crop geometry string from the cropping rectangle.
12774   */
12775   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12776   if (crop_info->x > 0)
12777     x+=(int) (scale_factor*crop_info->x+0.5);
12778   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12779   if (width == 0)
12780     width=1;
12781   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12782   if (crop_info->y > 0)
12783     y+=(int) (scale_factor*crop_info->y+0.5);
12784   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12785   if (height == 0)
12786     height=1;
12787   (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12788     "%ux%u%+d%+d",width,height,x,y);
12789 }
12790 
12791 /*
12792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12793 %                                                                             %
12794 %                                                                             %
12795 %                                                                             %
12796 +   X T i l e I m a g e                                                       %
12797 %                                                                             %
12798 %                                                                             %
12799 %                                                                             %
12800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12801 %
12802 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12803 %  The load or delete command is chosen from a menu.
12804 %
12805 %  The format of the XTileImage method is:
12806 %
12807 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12808 %        XWindows *windows,Image *image,XEvent *event)
12809 %
12810 %  A description of each parameter follows:
12811 %
12812 %    o tile_image:  XTileImage reads or deletes the tile image
12813 %      and returns it.  A null image is returned if an error occurs.
12814 %
12815 %    o display: Specifies a connection to an X server;  returned from
12816 %      XOpenDisplay.
12817 %
12818 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12819 %
12820 %    o windows: Specifies a pointer to a XWindows structure.
12821 %
12822 %    o image: the image; returned from
12823 %      ReadImage.
12824 %
12825 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
12826 %      the entire image is refreshed.
12827 %
12828 */
12829 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12830   XWindows *windows,Image *image,XEvent *event)
12831 {
12832   static const char
12833     *VerbMenu[] =
12834     {
12835       "Load",
12836       "Next",
12837       "Former",
12838       "Delete",
12839       "Update",
12840       (char *) NULL,
12841     };
12842 
12843   static const ModeType
12844     TileCommands[] =
12845     {
12846       TileLoadCommand,
12847       TileNextCommand,
12848       TileFormerCommand,
12849       TileDeleteCommand,
12850       TileUpdateCommand
12851     };
12852 
12853   char
12854     command[MaxTextExtent],
12855     filename[MaxTextExtent];
12856 
12857   Image
12858     *tile_image;
12859 
12860   int
12861     id,
12862     status,
12863     tile,
12864     x,
12865     y;
12866 
12867   MagickRealType
12868     scale_factor;
12869 
12870   register char
12871     *p,
12872     *q;
12873 
12874   register int
12875     i;
12876 
12877   unsigned int
12878     height,
12879     width;
12880 
12881   /*
12882     Tile image is relative to montage image configuration.
12883   */
12884   x=0;
12885   y=0;
12886   width=(unsigned int) image->columns;
12887   height=(unsigned int) image->rows;
12888   if (windows->image.crop_geometry != (char *) NULL)
12889     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12890   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12891   event->xbutton.x+=windows->image.x;
12892   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12893   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12894   event->xbutton.y+=windows->image.y;
12895   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
12896   /*
12897     Determine size and location of each tile in the visual image directory.
12898   */
12899   width=(unsigned int) image->columns;
12900   height=(unsigned int) image->rows;
12901   x=0;
12902   y=0;
12903   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
12904   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
12905     (event->xbutton.x-x)/width;
12906   if (tile < 0)
12907     {
12908       /*
12909         Button press is outside any tile.
12910       */
12911       (void) XBell(display,0);
12912       return((Image *) NULL);
12913     }
12914   /*
12915     Determine file name from the tile directory.
12916   */
12917   p=image->directory;
12918   for (i=tile; (i != 0) && (*p != '\0'); )
12919   {
12920     if (*p == '\n')
12921       i--;
12922     p++;
12923   }
12924   if (*p == '\0')
12925     {
12926       /*
12927         Button press is outside any tile.
12928       */
12929       (void) XBell(display,0);
12930       return((Image *) NULL);
12931     }
12932   /*
12933     Select a command from the pop-up menu.
12934   */
12935   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
12936   if (id < 0)
12937     return((Image *) NULL);
12938   q=p;
12939   while ((*q != '\n') && (*q != '\0'))
12940     q++;
12941   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
12942   /*
12943     Perform command for the selected tile.
12944   */
12945   XSetCursorState(display,windows,MagickTrue);
12946   XCheckRefreshWindows(display,windows);
12947   tile_image=NewImageList();
12948   switch (TileCommands[id])
12949   {
12950     case TileLoadCommand:
12951     {
12952       /*
12953         Load tile image.
12954       */
12955       XCheckRefreshWindows(display,windows);
12956       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
12957         MaxTextExtent);
12958       (void) CopyMagickString(resource_info->image_info->filename,filename,
12959         MaxTextExtent);
12960       tile_image=ReadImage(resource_info->image_info,&image->exception);
12961       CatchException(&image->exception);
12962       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12963       break;
12964     }
12965     case TileNextCommand:
12966     {
12967       /*
12968         Display next image.
12969       */
12970       XClientMessage(display,windows->image.id,windows->im_protocols,
12971         windows->im_next_image,CurrentTime);
12972       break;
12973     }
12974     case TileFormerCommand:
12975     {
12976       /*
12977         Display former image.
12978       */
12979       XClientMessage(display,windows->image.id,windows->im_protocols,
12980         windows->im_former_image,CurrentTime);
12981       break;
12982     }
12983     case TileDeleteCommand:
12984     {
12985       /*
12986         Delete tile image.
12987       */
12988       if (IsPathAccessible(filename) == MagickFalse)
12989         {
12990           XNoticeWidget(display,windows,"Image file does not exist:",filename);
12991           break;
12992         }
12993       status=XConfirmWidget(display,windows,"Really delete tile",filename);
12994       if (status <= 0)
12995         break;
12996       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
12997       if (status != MagickFalse)
12998         {
12999           XNoticeWidget(display,windows,"Unable to delete image file:",
13000             filename);
13001           break;
13002         }
13003     }
13004     case TileUpdateCommand:
13005     {
13006       ExceptionInfo
13007         *exception;
13008 
13009       int
13010         x_offset,
13011         y_offset;
13012 
13013       PixelPacket
13014         pixel;
13015 
13016       register int
13017         j;
13018 
13019       register PixelPacket
13020         *s;
13021 
13022       /*
13023         Ensure all the images exist.
13024       */
13025       tile=0;
13026       for (p=image->directory; *p != '\0'; p++)
13027       {
13028         q=p;
13029         while ((*q != '\n') && (*q != '\0'))
13030           q++;
13031         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13032         p=q;
13033         if (IsPathAccessible(filename) != MagickFalse)
13034           {
13035             tile++;
13036             continue;
13037           }
13038         /*
13039           Overwrite tile with background color.
13040         */
13041         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13042         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13043         exception=(&image->exception);
13044         (void) GetOneVirtualPixel(image,0,0,&pixel,exception);
13045         for (i=0; i < (int) height; i++)
13046         {
13047           s=GetAuthenticPixels(image,x_offset,y_offset+i,width,1,exception);
13048           if (s == (PixelPacket *) NULL)
13049             break;
13050           for (j=0; j < (int) width; j++)
13051             *s++=pixel;
13052           if (SyncAuthenticPixels(image,exception) == MagickFalse)
13053             break;
13054         }
13055         tile++;
13056       }
13057       windows->image.window_changes.width=(int) image->columns;
13058       windows->image.window_changes.height=(int) image->rows;
13059       XConfigureImageColormap(display,resource_info,windows,image);
13060       (void) XConfigureImage(display,resource_info,windows,image);
13061       break;
13062     }
13063     default:
13064       break;
13065   }
13066   XSetCursorState(display,windows,MagickFalse);
13067   return(tile_image);
13068 }
13069 
13070 /*
13071 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13072 %                                                                             %
13073 %                                                                             %
13074 %                                                                             %
13075 +   X T r a n s l a t e I m a g e                                             %
13076 %                                                                             %
13077 %                                                                             %
13078 %                                                                             %
13079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13080 %
13081 %  XTranslateImage() translates the image within an Image window by one pixel
13082 %  as specified by the key symbol.  If the image has a `montage string the
13083 %  translation is respect to the width and height contained within the string.
13084 %
13085 %  The format of the XTranslateImage method is:
13086 %
13087 %      void XTranslateImage(Display *display,XWindows *windows,
13088 %        Image *image,const KeySym key_symbol)
13089 %
13090 %  A description of each parameter follows:
13091 %
13092 %    o display: Specifies a connection to an X server; returned from
13093 %      XOpenDisplay.
13094 %
13095 %    o windows: Specifies a pointer to a XWindows structure.
13096 %
13097 %    o image: the image;  returned from
13098 %      ReadImage.
13099 %
13100 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13101 %      to trim.
13102 %
13103 */
13104 static void XTranslateImage(Display *display,XWindows *windows,
13105   Image *image,const KeySym key_symbol)
13106 {
13107   char
13108     text[MaxTextExtent];
13109 
13110   int
13111     x,
13112     y;
13113 
13114   unsigned int
13115     x_offset,
13116     y_offset;
13117 
13118   /*
13119     User specified a pan position offset.
13120   */
13121   x_offset=windows->image.width;
13122   y_offset=windows->image.height;
13123   if (image->montage != (char *) NULL)
13124     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13125   switch ((int) key_symbol)
13126   {
13127     case XK_Home:
13128     case XK_KP_Home:
13129     {
13130       windows->image.x=(int) windows->image.width/2;
13131       windows->image.y=(int) windows->image.height/2;
13132       break;
13133     }
13134     case XK_Left:
13135     case XK_KP_Left:
13136     {
13137       windows->image.x-=x_offset;
13138       break;
13139     }
13140     case XK_Next:
13141     case XK_Up:
13142     case XK_KP_Up:
13143     {
13144       windows->image.y-=y_offset;
13145       break;
13146     }
13147     case XK_Right:
13148     case XK_KP_Right:
13149     {
13150       windows->image.x+=x_offset;
13151       break;
13152     }
13153     case XK_Prior:
13154     case XK_Down:
13155     case XK_KP_Down:
13156     {
13157       windows->image.y+=y_offset;
13158       break;
13159     }
13160     default:
13161       return;
13162   }
13163   /*
13164     Check boundary conditions.
13165   */
13166   if (windows->image.x < 0)
13167     windows->image.x=0;
13168   else
13169     if ((int) (windows->image.x+windows->image.width) >
13170         windows->image.ximage->width)
13171       windows->image.x=windows->image.ximage->width-windows->image.width;
13172   if (windows->image.y < 0)
13173     windows->image.y=0;
13174   else
13175     if ((int) (windows->image.y+windows->image.height) >
13176         windows->image.ximage->height)
13177       windows->image.y=windows->image.ximage->height-windows->image.height;
13178   /*
13179     Refresh Image window.
13180   */
13181   (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
13182     windows->image.width,windows->image.height,windows->image.x,
13183     windows->image.y);
13184   XInfoWidget(display,windows,text);
13185   XCheckRefreshWindows(display,windows);
13186   XDrawPanRectangle(display,windows);
13187   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13188   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13189 }
13190 
13191 /*
13192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13193 %                                                                             %
13194 %                                                                             %
13195 %                                                                             %
13196 +   X T r i m I m a g e                                                       %
13197 %                                                                             %
13198 %                                                                             %
13199 %                                                                             %
13200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13201 %
13202 %  XTrimImage() trims the edges from the Image window.
13203 %
13204 %  The format of the XTrimImage method is:
13205 %
13206 %      MagickBooleanType XTrimImage(Display *display,
13207 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
13208 %
13209 %  A description of each parameter follows:
13210 %
13211 %    o display: Specifies a connection to an X server; returned from
13212 %      XOpenDisplay.
13213 %
13214 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13215 %
13216 %    o windows: Specifies a pointer to a XWindows structure.
13217 %
13218 %    o image: the image.
13219 %
13220 */
13221 static MagickBooleanType XTrimImage(Display *display,
13222   XResourceInfo *resource_info,XWindows *windows,Image *image)
13223 {
13224   RectangleInfo
13225     trim_info;
13226 
13227   register int
13228     x,
13229     y;
13230 
13231   unsigned long
13232     background,
13233     pixel;
13234 
13235   /*
13236     Trim edges from image.
13237   */
13238   XSetCursorState(display,windows,MagickTrue);
13239   XCheckRefreshWindows(display,windows);
13240   /*
13241     Crop the left edge.
13242   */
13243   background=XGetPixel(windows->image.ximage,0,0);
13244   trim_info.width=(unsigned long) windows->image.ximage->width;
13245   for (x=0; x < windows->image.ximage->width; x++)
13246   {
13247     for (y=0; y < windows->image.ximage->height; y++)
13248     {
13249       pixel=XGetPixel(windows->image.ximage,x,y);
13250       if (pixel != background)
13251         break;
13252     }
13253     if (y < windows->image.ximage->height)
13254       break;
13255   }
13256   trim_info.x=x;
13257   if (trim_info.x == (int) windows->image.ximage->width)
13258     {
13259       XSetCursorState(display,windows,MagickFalse);
13260       return(MagickFalse);
13261     }
13262   /*
13263     Crop the right edge.
13264   */
13265   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13266   for (x=windows->image.ximage->width-1; x != 0; x--)
13267   {
13268     for (y=0; y < windows->image.ximage->height; y++)
13269     {
13270       pixel=XGetPixel(windows->image.ximage,x,y);
13271       if (pixel != background)
13272         break;
13273     }
13274     if (y < windows->image.ximage->height)
13275       break;
13276   }
13277   trim_info.width=(unsigned long) (x-trim_info.x+1);
13278   /*
13279     Crop the top edge.
13280   */
13281   background=XGetPixel(windows->image.ximage,0,0);
13282   trim_info.height=(unsigned long) windows->image.ximage->height;
13283   for (y=0; y < windows->image.ximage->height; y++)
13284   {
13285     for (x=0; x < windows->image.ximage->width; x++)
13286     {
13287       pixel=XGetPixel(windows->image.ximage,x,y);
13288       if (pixel != background)
13289         break;
13290     }
13291     if (x < windows->image.ximage->width)
13292       break;
13293   }
13294   trim_info.y=y;
13295   /*
13296     Crop the bottom edge.
13297   */
13298   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13299   for (y=windows->image.ximage->height-1; y != 0; y--)
13300   {
13301     for (x=0; x < windows->image.ximage->width; x++)
13302     {
13303       pixel=XGetPixel(windows->image.ximage,x,y);
13304       if (pixel != background)
13305         break;
13306     }
13307     if (x < windows->image.ximage->width)
13308       break;
13309   }
13310   trim_info.height=(unsigned long) y-trim_info.y+1;
13311   if (((unsigned int) trim_info.width != windows->image.width) ||
13312       ((unsigned int) trim_info.height != windows->image.height))
13313     {
13314       /*
13315         Reconfigure Image window as defined by the trimming rectangle.
13316       */
13317       XSetCropGeometry(display,windows,&trim_info,image);
13318       windows->image.window_changes.width=(int) trim_info.width;
13319       windows->image.window_changes.height=(int) trim_info.height;
13320       (void) XConfigureImage(display,resource_info,windows,image);
13321     }
13322   XSetCursorState(display,windows,MagickFalse);
13323   return(MagickTrue);
13324 }
13325 
13326 /*
13327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13328 %                                                                             %
13329 %                                                                             %
13330 %                                                                             %
13331 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13332 %                                                                             %
13333 %                                                                             %
13334 %                                                                             %
13335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13336 %
13337 %  XVisualDirectoryImage() creates a Visual Image Directory.
13338 %
13339 %  The format of the XVisualDirectoryImage method is:
13340 %
13341 %      Image *XVisualDirectoryImage(Display *display,
13342 %        XResourceInfo *resource_info,XWindows *windows)
13343 %
13344 %  A description of each parameter follows:
13345 %
13346 %    o nexus: Method XVisualDirectoryImage returns a visual image
13347 %      directory if it can be created successfully.  Otherwise a null image
13348 %      is returned.
13349 %
13350 %    o display: Specifies a connection to an X server; returned from
13351 %      XOpenDisplay.
13352 %
13353 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13354 %
13355 %    o windows: Specifies a pointer to a XWindows structure.
13356 %
13357 */
13358 static Image *XVisualDirectoryImage(Display *display,
13359   XResourceInfo *resource_info,XWindows *windows)
13360 {
13361 #define TileImageTag  "Scale/Image"
13362 #define XClientName  "montage"
13363 
13364   char
13365     **filelist;
13366 
13367   ExceptionInfo
13368     *exception;
13369 
13370   Image
13371     *images,
13372     *montage_image,
13373     *next_image,
13374     *thumbnail_image;
13375 
13376   ImageInfo
13377     *read_info;
13378 
13379   int
13380     number_files;
13381 
13382   MagickBooleanType
13383     backdrop;
13384 
13385   MagickStatusType
13386     status;
13387 
13388   MontageInfo
13389     *montage_info;
13390 
13391   RectangleInfo
13392     geometry;
13393 
13394   register int
13395     i;
13396 
13397   static char
13398     filename[MaxTextExtent] = "\0",
13399     filenames[MaxTextExtent] = "*";
13400 
13401   XResourceInfo
13402     background_resources;
13403 
13404   /*
13405     Request file name from user.
13406   */
13407   XFileBrowserWidget(display,windows,"Directory",filenames);
13408   if (*filenames == '\0')
13409     return((Image *) NULL);
13410   /*
13411     Expand the filenames.
13412   */
13413   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13414   if (filelist == (char **) NULL)
13415     {
13416       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13417         filenames);
13418       return((Image *) NULL);
13419     }
13420   number_files=1;
13421   filelist[0]=filenames;
13422   status=ExpandFilenames(&number_files,&filelist);
13423   if ((status == MagickFalse) || (number_files == 0))
13424     {
13425       if (number_files == 0)
13426         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13427       else
13428         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13429           filenames);
13430       return((Image *) NULL);
13431     }
13432   /*
13433     Set image background resources.
13434   */
13435   background_resources=(*resource_info);
13436   background_resources.window_id=AcquireString("");
13437   (void) FormatMagickString(background_resources.window_id,MaxTextExtent,
13438     "0x%lx",windows->image.id);
13439   background_resources.backdrop=MagickTrue;
13440   /*
13441     Read each image and convert them to a tile.
13442   */
13443   backdrop=(windows->visual_info->klass == TrueColor) ||
13444     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13445   read_info=CloneImageInfo(resource_info->image_info);
13446   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13447     (void *) NULL);
13448   images=NewImageList();
13449   exception=AcquireExceptionInfo();
13450   XSetCursorState(display,windows,MagickTrue);
13451   XCheckRefreshWindows(display,windows);
13452   for (i=0; i < (long) number_files; i++)
13453   {
13454     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13455     filelist[i]=DestroyString(filelist[i]);
13456     *read_info->magick='\0';
13457     (void) CloneString(&read_info->size,DefaultTileGeometry);
13458     next_image=ReadImage(read_info,exception);
13459     CatchException(exception);
13460     if (next_image != (Image *) NULL)
13461       {
13462         (void) DeleteImageProperty(next_image,"label");
13463         (void) SetImageProperty(next_image,"label",DefaultTileLabel);
13464         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13465           exception);
13466         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13467           geometry.height,exception);
13468         if (thumbnail_image != (Image *) NULL)
13469           {
13470             next_image=DestroyImage(next_image);
13471             next_image=thumbnail_image;
13472           }
13473         if (backdrop)
13474           {
13475             (void) XDisplayBackgroundImage(display,&background_resources,
13476               next_image);
13477             XSetCursorState(display,windows,MagickTrue);
13478           }
13479         AppendImageToList(&images,next_image);
13480         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13481           {
13482             MagickBooleanType
13483               proceed;
13484 
13485             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13486               (MagickSizeType) number_files);
13487             if (proceed == MagickFalse)
13488               break;
13489           }
13490       }
13491   }
13492   exception=DestroyExceptionInfo(exception);
13493   filelist=(char **) RelinquishMagickMemory(filelist);
13494   read_info=DestroyImageInfo(read_info);
13495   if (images == (Image *) NULL)
13496     {
13497       XSetCursorState(display,windows,MagickFalse);
13498       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13499       return((Image *) NULL);
13500     }
13501   /*
13502     Create the Visual Image Directory.
13503   */
13504   montage_info=CloneMontageInfo(resource_info->image_info,(MontageInfo *) NULL);
13505   if (resource_info->font != (char *) NULL)
13506     (void) CloneString(&montage_info->font,resource_info->font);
13507   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13508   montage_image=MontageImageList(resource_info->image_info,montage_info,
13509     GetFirstImageInList(images),&images->exception);
13510   montage_info=DestroyMontageInfo(montage_info);
13511   images=DestroyImageList(images);
13512   XSetCursorState(display,windows,MagickFalse);
13513   if (montage_image == (Image *) NULL)
13514     return(montage_image);
13515   XClientMessage(display,windows->image.id,windows->im_protocols,
13516     windows->im_next_image,CurrentTime);
13517   return(montage_image);
13518 }
13519 
13520 /*
13521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13522 %                                                                             %
13523 %                                                                             %
13524 %                                                                             %
13525 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13526 %                                                                             %
13527 %                                                                             %
13528 %                                                                             %
13529 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13530 %
13531 %  XDisplayBackgroundImage() displays an image in the background of a window.
13532 %
13533 %  The format of the XDisplayBackgroundImage method is:
13534 %
13535 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13536 %        XResourceInfo *resource_info,Image *image)
13537 %
13538 %  A description of each parameter follows:
13539 %
13540 %    o display: Specifies a connection to an X server;  returned from
13541 %      XOpenDisplay.
13542 %
13543 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13544 %
13545 %    o image: the image.
13546 %
13547 */
13548 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13549   XResourceInfo *resource_info,Image *image)
13550 {
13551   char
13552     geometry[MaxTextExtent],
13553     visual_type[MaxTextExtent];
13554 
13555   int
13556     height,
13557     status,
13558     width;
13559 
13560   RectangleInfo
13561     geometry_info;
13562 
13563   static XPixelInfo
13564     pixel;
13565 
13566   static XStandardColormap
13567     *map_info;
13568 
13569   static XVisualInfo
13570     *visual_info = (XVisualInfo *) NULL;
13571 
13572   static XWindowInfo
13573     window_info;
13574 
13575   unsigned long
13576     delay;
13577 
13578   Window
13579     root_window;
13580 
13581   XGCValues
13582     context_values;
13583 
13584   XResourceInfo
13585     resources;
13586 
13587   XWindowAttributes
13588     window_attributes;
13589 
13590   /*
13591     Determine target window.
13592   */
13593   assert(image != (Image *) NULL);
13594   assert(image->signature == MagickSignature);
13595   if (image->debug != MagickFalse)
13596     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13597   resources=(*resource_info);
13598   window_info.id=(Window) NULL;
13599   root_window=XRootWindow(display,XDefaultScreen(display));
13600   if (LocaleCompare(resources.window_id,"root") == 0)
13601     window_info.id=root_window;
13602   else
13603     {
13604       if (isdigit((unsigned char) *resources.window_id) != 0)
13605         window_info.id=XWindowByID(display,root_window,
13606           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13607       if (window_info.id == (Window) NULL)
13608         window_info.id=XWindowByName(display,root_window,resources.window_id);
13609     }
13610   if (window_info.id == (Window) NULL)
13611     {
13612       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13613         resources.window_id);
13614       return(MagickFalse);
13615     }
13616   /*
13617     Determine window visual id.
13618   */
13619   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13620   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13621   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13622   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13623   if (status != 0)
13624     (void) FormatMagickString(visual_type,MaxTextExtent,"0x%lx",
13625       XVisualIDFromVisual(window_attributes.visual));
13626   if (visual_info == (XVisualInfo *) NULL)
13627     {
13628       /*
13629         Allocate standard colormap.
13630       */
13631       map_info=XAllocStandardColormap();
13632       if (map_info == (XStandardColormap *) NULL)
13633         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13634           image->filename);
13635       map_info->colormap=(Colormap) NULL;
13636       pixel.pixels=(unsigned long *) NULL;
13637       /*
13638         Initialize visual info.
13639       */
13640       resources.map_type=(char *) NULL;
13641       resources.visual_type=visual_type;
13642       visual_info=XBestVisualInfo(display,map_info,&resources);
13643       if (visual_info == (XVisualInfo *) NULL)
13644         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13645           resources.visual_type);
13646       /*
13647         Initialize window info.
13648       */
13649       window_info.ximage=(XImage *) NULL;
13650       window_info.matte_image=(XImage *) NULL;
13651       window_info.pixmap=(Pixmap) NULL;
13652       window_info.matte_pixmap=(Pixmap) NULL;
13653     }
13654   /*
13655     Free previous root colors.
13656   */
13657   if (window_info.id == root_window)
13658     (void) XDestroyWindowColors(display,root_window);
13659   /*
13660     Initialize Standard Colormap.
13661   */
13662   resources.colormap=SharedColormap;
13663   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13664   /*
13665     Graphic context superclass.
13666   */
13667   context_values.background=pixel.background_color.pixel;
13668   context_values.foreground=pixel.foreground_color.pixel;
13669   pixel.annotate_context=XCreateGC(display,window_info.id,
13670     (unsigned long) (GCBackground | GCForeground),&context_values);
13671   if (pixel.annotate_context == (GC) NULL)
13672     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13673       image->filename);
13674   /*
13675     Initialize Image window attributes.
13676   */
13677   window_info.name=AcquireString("\0");
13678   window_info.icon_name=AcquireString("\0");
13679   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13680     &resources,&window_info);
13681   /*
13682     Create the X image.
13683   */
13684   window_info.width=(unsigned int) image->columns;
13685   window_info.height=(unsigned int) image->rows;
13686   if ((image->columns != window_info.width) ||
13687       (image->rows != window_info.height))
13688     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13689       image->filename);
13690   (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>",
13691     window_attributes.width,window_attributes.height);
13692   geometry_info.width=window_info.width;
13693   geometry_info.height=window_info.height;
13694   geometry_info.x=window_info.x;
13695   geometry_info.y=window_info.y;
13696   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13697     &geometry_info.width,&geometry_info.height);
13698   window_info.width=(unsigned int) geometry_info.width;
13699   window_info.height=(unsigned int) geometry_info.height;
13700   window_info.x=(int) geometry_info.x;
13701   window_info.y=(int) geometry_info.y;
13702   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13703     window_info.height);
13704   if (status == MagickFalse)
13705     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13706       image->filename);
13707   window_info.x=0;
13708   window_info.y=0;
13709   if (image->debug != MagickFalse)
13710     {
13711       (void) LogMagickEvent(X11Event,GetMagickModule(),
13712         "Image: %s[%lu] %lux%lu ",image->filename,image->scene,
13713         image->columns,image->rows);
13714       if (image->colors != 0)
13715         (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",image->colors);
13716       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13717     }
13718   /*
13719     Adjust image dimensions as specified by backdrop or geometry options.
13720   */
13721   width=(int) window_info.width;
13722   height=(int) window_info.height;
13723   if (resources.backdrop != MagickFalse)
13724     {
13725       /*
13726         Center image on window.
13727       */
13728       window_info.x=(window_attributes.width/2)-
13729         (window_info.ximage->width/2);
13730       window_info.y=(window_attributes.height/2)-
13731         (window_info.ximage->height/2);
13732       width=window_attributes.width;
13733       height=window_attributes.height;
13734     }
13735   if ((resources.image_geometry != (char *) NULL) &&
13736       (*resources.image_geometry != '\0'))
13737     {
13738       char
13739         default_geometry[MaxTextExtent];
13740 
13741       int
13742         flags,
13743         gravity;
13744 
13745       XSizeHints
13746         *size_hints;
13747 
13748       /*
13749         User specified geometry.
13750       */
13751       size_hints=XAllocSizeHints();
13752       if (size_hints == (XSizeHints *) NULL)
13753         ThrowXWindowFatalException(ResourceLimitFatalError,
13754           "MemoryAllocationFailed",image->filename);
13755       size_hints->flags=0L;
13756       (void) FormatMagickString(default_geometry,MaxTextExtent,"%dx%d",
13757         width,height);
13758       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13759         default_geometry,window_info.border_width,size_hints,&window_info.x,
13760         &window_info.y,&width,&height,&gravity);
13761       if (flags & (XValue | YValue))
13762         {
13763           width=window_attributes.width;
13764           height=window_attributes.height;
13765         }
13766       (void) XFree((void *) size_hints);
13767     }
13768   /*
13769     Create the X pixmap.
13770   */
13771   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13772     (unsigned int) height,window_info.depth);
13773   if (window_info.pixmap == (Pixmap) NULL)
13774     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13775       image->filename);
13776   /*
13777     Display pixmap on the window.
13778   */
13779   if (((unsigned int) width > window_info.width) ||
13780       ((unsigned int) height > window_info.height))
13781     (void) XFillRectangle(display,window_info.pixmap,
13782       window_info.annotate_context,0,0,(unsigned int) width,
13783       (unsigned int) height);
13784   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13785     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13786     window_info.width,(unsigned int) window_info.height);
13787   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13788   (void) XClearWindow(display,window_info.id);
13789   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13790   XDelay(display,delay == 0UL ? 10UL : delay);
13791   (void) XSync(display,MagickFalse);
13792   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13793 }
13794 
13795 /*
13796 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13797 %                                                                             %
13798 %                                                                             %
13799 %                                                                             %
13800 +   X D i s p l a y I m a g e                                                 %
13801 %                                                                             %
13802 %                                                                             %
13803 %                                                                             %
13804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13805 %
13806 %  XDisplayImage() displays an image via X11.  A new image is created and
13807 %  returned if the user interactively transforms the displayed image.
13808 %
13809 %  The format of the XDisplayImage method is:
13810 %
13811 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13812 %        char **argv,int argc,Image **image,unsigned long *state)
13813 %
13814 %  A description of each parameter follows:
13815 %
13816 %    o nexus:  Method XDisplayImage returns an image when the
13817 %      user chooses 'Open Image' from the command menu or picks a tile
13818 %      from the image directory.  Otherwise a null image is returned.
13819 %
13820 %    o display: Specifies a connection to an X server;  returned from
13821 %      XOpenDisplay.
13822 %
13823 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13824 %
13825 %    o argv: Specifies the application's argument list.
13826 %
13827 %    o argc: Specifies the number of arguments.
13828 %
13829 %    o image: Specifies an address to an address of an Image structure;
13830 %
13831 */
13832 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13833   char **argv,int argc,Image **image,unsigned long *state)
13834 {
13835 #define MagnifySize  256  /* must be a power of 2 */
13836 #define MagickMenus  10
13837 #define MagickTitle  "Commands"
13838 
13839   static const char
13840     *CommandMenu[] =
13841     {
13842       "File",
13843       "Edit",
13844       "View",
13845       "Transform",
13846       "Enhance",
13847       "Effects",
13848       "F/X",
13849       "Image Edit",
13850       "Miscellany",
13851       "Help",
13852       (char *) NULL
13853     },
13854     *FileMenu[] =
13855     {
13856       "Open...",
13857       "Next",
13858       "Former",
13859       "Select...",
13860       "Save...",
13861       "Print...",
13862       "Delete...",
13863       "New...",
13864       "Visual Directory...",
13865       "Quit",
13866       (char *) NULL
13867     },
13868     *EditMenu[] =
13869     {
13870       "Undo",
13871       "Redo",
13872       "Cut",
13873       "Copy",
13874       "Paste",
13875       (char *) NULL
13876     },
13877     *ViewMenu[] =
13878     {
13879       "Half Size",
13880       "Original Size",
13881       "Double Size",
13882       "Resize...",
13883       "Apply",
13884       "Refresh",
13885       "Restore",
13886       (char *) NULL
13887     },
13888     *TransformMenu[] =
13889     {
13890       "Crop",
13891       "Chop",
13892       "Flop",
13893       "Flip",
13894       "Rotate Right",
13895       "Rotate Left",
13896       "Rotate...",
13897       "Shear...",
13898       "Roll...",
13899       "Trim Edges",
13900       (char *) NULL
13901     },
13902     *EnhanceMenu[] =
13903     {
13904       "Hue...",
13905       "Saturation...",
13906       "Brightness...",
13907       "Gamma...",
13908       "Spiff",
13909       "Dull",
13910       "Contrast Stretch...",
13911       "Sigmoidal Contrast...",
13912       "Normalize",
13913       "Equalize",
13914       "Negate",
13915       "Grayscale",
13916       "Map...",
13917       "Quantize...",
13918       (char *) NULL
13919     },
13920     *EffectsMenu[] =
13921     {
13922       "Despeckle",
13923       "Emboss",
13924       "Reduce Noise",
13925       "Add Noise...",
13926       "Sharpen...",
13927       "Blur...",
13928       "Threshold...",
13929       "Edge Detect...",
13930       "Spread...",
13931       "Shade...",
13932       "Raise...",
13933       "Segment...",
13934       (char *) NULL
13935     },
13936     *FXMenu[] =
13937     {
13938       "Solarize...",
13939       "Sepia Tone...",
13940       "Swirl...",
13941       "Implode...",
13942       "Vignette...",
13943       "Wave...",
13944       "Oil Paint...",
13945       "Charcoal Draw...",
13946       (char *) NULL
13947     },
13948     *ImageEditMenu[] =
13949     {
13950       "Annotate...",
13951       "Draw...",
13952       "Color...",
13953       "Matte...",
13954       "Composite...",
13955       "Add Border...",
13956       "Add Frame...",
13957       "Comment...",
13958       "Launch...",
13959       "Region of Interest...",
13960       (char *) NULL
13961     },
13962     *MiscellanyMenu[] =
13963     {
13964       "Image Info",
13965       "Zoom Image",
13966       "Show Preview...",
13967       "Show Histogram",
13968       "Show Matte",
13969       "Background...",
13970       "Slide Show...",
13971       "Preferences...",
13972       (char *) NULL
13973     },
13974     *HelpMenu[] =
13975     {
13976       "Overview",
13977       "Browse Documentation",
13978       "About Display",
13979       (char *) NULL
13980     },
13981     *ShortCutsMenu[] =
13982     {
13983       "Next",
13984       "Former",
13985       "Open...",
13986       "Save...",
13987       "Print...",
13988       "Undo",
13989       "Restore",
13990       "Image Info",
13991       "Quit",
13992       (char *) NULL
13993     },
13994     *VirtualMenu[] =
13995     {
13996       "Image Info",
13997       "Print",
13998       "Next",
13999       "Quit",
14000       (char *) NULL
14001     };
14002 
14003   static const char
14004     **Menus[MagickMenus] =
14005     {
14006       FileMenu,
14007       EditMenu,
14008       ViewMenu,
14009       TransformMenu,
14010       EnhanceMenu,
14011       EffectsMenu,
14012       FXMenu,
14013       ImageEditMenu,
14014       MiscellanyMenu,
14015       HelpMenu
14016     };
14017 
14018   static CommandType
14019     CommandMenus[] =
14020     {
14021       NullCommand,
14022       NullCommand,
14023       NullCommand,
14024       NullCommand,
14025       NullCommand,
14026       NullCommand,
14027       NullCommand,
14028       NullCommand,
14029       NullCommand,
14030       NullCommand,
14031     },
14032     FileCommands[] =
14033     {
14034       OpenCommand,
14035       NextCommand,
14036       FormerCommand,
14037       SelectCommand,
14038       SaveCommand,
14039       PrintCommand,
14040       DeleteCommand,
14041       NewCommand,
14042       VisualDirectoryCommand,
14043       QuitCommand
14044     },
14045     EditCommands[] =
14046     {
14047       UndoCommand,
14048       RedoCommand,
14049       CutCommand,
14050       CopyCommand,
14051       PasteCommand
14052     },
14053     ViewCommands[] =
14054     {
14055       HalfSizeCommand,
14056       OriginalSizeCommand,
14057       DoubleSizeCommand,
14058       ResizeCommand,
14059       ApplyCommand,
14060       RefreshCommand,
14061       RestoreCommand
14062     },
14063     TransformCommands[] =
14064     {
14065       CropCommand,
14066       ChopCommand,
14067       FlopCommand,
14068       FlipCommand,
14069       RotateRightCommand,
14070       RotateLeftCommand,
14071       RotateCommand,
14072       ShearCommand,
14073       RollCommand,
14074       TrimCommand
14075     },
14076     EnhanceCommands[] =
14077     {
14078       HueCommand,
14079       SaturationCommand,
14080       BrightnessCommand,
14081       GammaCommand,
14082       SpiffCommand,
14083       DullCommand,
14084       ContrastStretchCommand,
14085       SigmoidalContrastCommand,
14086       NormalizeCommand,
14087       EqualizeCommand,
14088       NegateCommand,
14089       GrayscaleCommand,
14090       MapCommand,
14091       QuantizeCommand
14092     },
14093     EffectsCommands[] =
14094     {
14095       DespeckleCommand,
14096       EmbossCommand,
14097       ReduceNoiseCommand,
14098       AddNoiseCommand,
14099       SharpenCommand,
14100       BlurCommand,
14101       ThresholdCommand,
14102       EdgeDetectCommand,
14103       SpreadCommand,
14104       ShadeCommand,
14105       RaiseCommand,
14106       SegmentCommand
14107     },
14108     FXCommands[] =
14109     {
14110       SolarizeCommand,
14111       SepiaToneCommand,
14112       SwirlCommand,
14113       ImplodeCommand,
14114       VignetteCommand,
14115       WaveCommand,
14116       OilPaintCommand,
14117       CharcoalDrawCommand
14118     },
14119     ImageEditCommands[] =
14120     {
14121       AnnotateCommand,
14122       DrawCommand,
14123       ColorCommand,
14124       MatteCommand,
14125       CompositeCommand,
14126       AddBorderCommand,
14127       AddFrameCommand,
14128       CommentCommand,
14129       LaunchCommand,
14130       RegionofInterestCommand
14131     },
14132     MiscellanyCommands[] =
14133     {
14134       InfoCommand,
14135       ZoomCommand,
14136       ShowPreviewCommand,
14137       ShowHistogramCommand,
14138       ShowMatteCommand,
14139       BackgroundCommand,
14140       SlideShowCommand,
14141       PreferencesCommand
14142     },
14143     HelpCommands[] =
14144     {
14145       HelpCommand,
14146       BrowseDocumentationCommand,
14147       VersionCommand
14148     },
14149     ShortCutsCommands[] =
14150     {
14151       NextCommand,
14152       FormerCommand,
14153       OpenCommand,
14154       SaveCommand,
14155       PrintCommand,
14156       UndoCommand,
14157       RestoreCommand,
14158       InfoCommand,
14159       QuitCommand
14160     },
14161     VirtualCommands[] =
14162     {
14163       InfoCommand,
14164       PrintCommand,
14165       NextCommand,
14166       QuitCommand
14167     };
14168 
14169   static CommandType
14170     *Commands[MagickMenus] =
14171     {
14172       FileCommands,
14173       EditCommands,
14174       ViewCommands,
14175       TransformCommands,
14176       EnhanceCommands,
14177       EffectsCommands,
14178       FXCommands,
14179       ImageEditCommands,
14180       MiscellanyCommands,
14181       HelpCommands
14182     };
14183 
14184   char
14185     command[MaxTextExtent],
14186     *cwd,
14187     geometry[MaxTextExtent],
14188     resource_name[MaxTextExtent];
14189 
14190   CommandType
14191     command_type;
14192 
14193   Image
14194     *display_image,
14195     *nexus;
14196 
14197   int
14198     entry,
14199     id;
14200 
14201   KeySym
14202     key_symbol;
14203 
14204   MagickStatusType
14205     context_mask,
14206     status;
14207 
14208   RectangleInfo
14209     geometry_info;
14210 
14211   register int
14212     i;
14213 
14214   static char
14215     working_directory[MaxTextExtent];
14216 
14217   static XPoint
14218     vid_info;
14219 
14220   static XWindowInfo
14221     *magick_windows[MaxXWindows];
14222 
14223   static unsigned int
14224     number_windows;
14225 
14226   struct stat
14227     attributes;
14228 
14229   time_t
14230     timer,
14231     timestamp,
14232     update_time;
14233 
14234   unsigned int
14235     height,
14236     width;
14237 
14238   unsigned long
14239     delay;
14240 
14241   WarningHandler
14242     warning_handler;
14243 
14244   Window
14245     root_window;
14246 
14247   XClassHint
14248     *class_hints;
14249 
14250   XEvent
14251     event;
14252 
14253   XFontStruct
14254     *font_info;
14255 
14256   XGCValues
14257     context_values;
14258 
14259   XPixelInfo
14260     *icon_pixel,
14261     *pixel;
14262 
14263   XResourceInfo
14264     *icon_resources;
14265 
14266   XStandardColormap
14267     *icon_map,
14268     *map_info;
14269 
14270   XVisualInfo
14271     *icon_visual,
14272     *visual_info;
14273 
14274   XWindowChanges
14275     window_changes;
14276 
14277   XWindows
14278     *windows;
14279 
14280   XWMHints
14281     *manager_hints;
14282 
14283   assert(image != (Image **) NULL);
14284   assert((*image)->signature == MagickSignature);
14285   if ((*image)->debug != MagickFalse)
14286     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14287   display_image=(*image);
14288   warning_handler=(WarningHandler) NULL;
14289   windows=XSetWindows((XWindows *) ~0);
14290   if (windows != (XWindows *) NULL)
14291     {
14292       int
14293         status;
14294 
14295       status=chdir(working_directory);
14296       if (status == -1)
14297         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14298           FileOpenError,"UnableToOpenFile","%s",working_directory);
14299       warning_handler=resource_info->display_warnings ?
14300         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14301       warning_handler=resource_info->display_warnings ?
14302         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14303     }
14304   else
14305     {
14306       /*
14307         Allocate windows structure.
14308       */
14309       resource_info->colors=display_image->colors;
14310       windows=XSetWindows(XInitializeWindows(display,resource_info));
14311       if (windows == (XWindows *) NULL)
14312         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14313           (*image)->filename);
14314       /*
14315         Initialize window id's.
14316       */
14317       number_windows=0;
14318       magick_windows[number_windows++]=(&windows->icon);
14319       magick_windows[number_windows++]=(&windows->backdrop);
14320       magick_windows[number_windows++]=(&windows->image);
14321       magick_windows[number_windows++]=(&windows->info);
14322       magick_windows[number_windows++]=(&windows->command);
14323       magick_windows[number_windows++]=(&windows->widget);
14324       magick_windows[number_windows++]=(&windows->popup);
14325       magick_windows[number_windows++]=(&windows->magnify);
14326       magick_windows[number_windows++]=(&windows->pan);
14327       for (i=0; i < (int) number_windows; i++)
14328         magick_windows[i]->id=(Window) NULL;
14329       vid_info.x=0;
14330       vid_info.y=0;
14331     }
14332   /*
14333     Initialize font info.
14334   */
14335   if (windows->font_info != (XFontStruct *) NULL)
14336     (void) XFreeFont(display,windows->font_info);
14337   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14338   if (windows->font_info == (XFontStruct *) NULL)
14339     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14340       resource_info->font);
14341   /*
14342     Initialize Standard Colormap.
14343   */
14344   map_info=windows->map_info;
14345   icon_map=windows->icon_map;
14346   visual_info=windows->visual_info;
14347   icon_visual=windows->icon_visual;
14348   pixel=windows->pixel_info;
14349   icon_pixel=windows->icon_pixel;
14350   font_info=windows->font_info;
14351   icon_resources=windows->icon_resources;
14352   class_hints=windows->class_hints;
14353   manager_hints=windows->manager_hints;
14354   root_window=XRootWindow(display,visual_info->screen);
14355   nexus=NewImageList();
14356   if (display_image->debug != MagickFalse)
14357     {
14358       (void) LogMagickEvent(X11Event,GetMagickModule(),
14359         "Image: %s[%lu] %lux%lu ",display_image->filename,
14360         display_image->scene,display_image->columns,display_image->rows);
14361       if (display_image->colors != 0)
14362         (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",
14363           display_image->colors);
14364       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14365         display_image->magick);
14366     }
14367   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14368     map_info,pixel);
14369   display_image->taint=MagickFalse;
14370   /*
14371     Initialize graphic context.
14372   */
14373   windows->context.id=(Window) NULL;
14374   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14375     resource_info,&windows->context);
14376   (void) CloneString(&class_hints->res_name,"superclass");
14377   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14378   manager_hints->flags=InputHint | StateHint;
14379   manager_hints->input=MagickFalse;
14380   manager_hints->initial_state=WithdrawnState;
14381   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14382     &windows->context);
14383   if (display_image->debug != MagickFalse)
14384     (void) LogMagickEvent(X11Event,GetMagickModule(),
14385       "Window id: 0x%lx (context)",windows->context.id);
14386   context_values.background=pixel->background_color.pixel;
14387   context_values.font=font_info->fid;
14388   context_values.foreground=pixel->foreground_color.pixel;
14389   context_values.graphics_exposures=MagickFalse;
14390   context_mask=(MagickStatusType)
14391     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14392   if (pixel->annotate_context != (GC) NULL)
14393     (void) XFreeGC(display,pixel->annotate_context);
14394   pixel->annotate_context=XCreateGC(display,windows->context.id,
14395     context_mask,&context_values);
14396   if (pixel->annotate_context == (GC) NULL)
14397     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14398       display_image->filename);
14399   context_values.background=pixel->depth_color.pixel;
14400   if (pixel->widget_context != (GC) NULL)
14401     (void) XFreeGC(display,pixel->widget_context);
14402   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14403     &context_values);
14404   if (pixel->widget_context == (GC) NULL)
14405     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14406       display_image->filename);
14407   context_values.background=pixel->foreground_color.pixel;
14408   context_values.foreground=pixel->background_color.pixel;
14409   context_values.plane_mask=context_values.background ^
14410     context_values.foreground;
14411   if (pixel->highlight_context != (GC) NULL)
14412     (void) XFreeGC(display,pixel->highlight_context);
14413   pixel->highlight_context=XCreateGC(display,windows->context.id,
14414     (unsigned long) (context_mask | GCPlaneMask),&context_values);
14415   if (pixel->highlight_context == (GC) NULL)
14416     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14417       display_image->filename);
14418   (void) XDestroyWindow(display,windows->context.id);
14419   /*
14420     Initialize icon window.
14421   */
14422   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14423     icon_resources,&windows->icon);
14424   windows->icon.geometry=resource_info->icon_geometry;
14425   XBestIconSize(display,&windows->icon,display_image);
14426   windows->icon.attributes.colormap=XDefaultColormap(display,
14427     icon_visual->screen);
14428   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14429   (void) CloneString(&class_hints->res_name,"icon");
14430   manager_hints->flags=InputHint | StateHint;
14431   manager_hints->input=MagickFalse;
14432   manager_hints->initial_state=IconicState;
14433   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14434     &windows->icon);
14435   if (display_image->debug != MagickFalse)
14436     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14437       windows->icon.id);
14438   /*
14439     Initialize graphic context for icon window.
14440   */
14441   if (icon_pixel->annotate_context != (GC) NULL)
14442     (void) XFreeGC(display,icon_pixel->annotate_context);
14443   context_values.background=icon_pixel->background_color.pixel;
14444   context_values.foreground=icon_pixel->foreground_color.pixel;
14445   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14446     (unsigned long) (GCBackground | GCForeground),&context_values);
14447   if (icon_pixel->annotate_context == (GC) NULL)
14448     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14449       display_image->filename);
14450   windows->icon.annotate_context=icon_pixel->annotate_context;
14451   /*
14452     Initialize Image window.
14453   */
14454   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14455     &windows->image);
14456   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14457   if (resource_info->use_shared_memory == MagickFalse)
14458     windows->image.shared_memory=MagickFalse;
14459   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14460     {
14461       char
14462         *title;
14463 
14464       title=InterpretImageProperties(resource_info->image_info,display_image,
14465         resource_info->title);
14466       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14467       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14468       title=DestroyString(title);
14469     }
14470   else
14471     {
14472       char
14473         filename[MaxTextExtent];
14474 
14475       /*
14476         Window name is the base of the filename.
14477       */
14478       GetPathComponent(display_image->magick_filename,TailPath,filename);
14479       if (GetImageListLength(display_image) == 1)
14480         (void) FormatMagickString(windows->image.name,MaxTextExtent,
14481           "ImageMagick: %s",filename);
14482       else
14483         (void) FormatMagickString(windows->image.name,MaxTextExtent,
14484           "ImageMagick: %s[%lu of %lu]",filename,display_image->scene,
14485           GetImageListLength(display_image));
14486       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14487     }
14488   if (resource_info->immutable)
14489     windows->image.immutable=MagickTrue;
14490   windows->image.use_pixmap=resource_info->use_pixmap;
14491   windows->image.geometry=resource_info->image_geometry;
14492   (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14493     XDisplayWidth(display,visual_info->screen),
14494     XDisplayHeight(display,visual_info->screen));
14495   geometry_info.width=display_image->columns;
14496   geometry_info.height=display_image->rows;
14497   geometry_info.x=0;
14498   geometry_info.y=0;
14499   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14500     &geometry_info.width,&geometry_info.height);
14501   windows->image.width=(unsigned int) geometry_info.width;
14502   windows->image.height=(unsigned int) geometry_info.height;
14503   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14504     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14505     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14506     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14507   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14508     resource_info,&windows->backdrop);
14509   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14510     {
14511       /*
14512         Initialize backdrop window.
14513       */
14514       windows->backdrop.x=0;
14515       windows->backdrop.y=0;
14516       (void) CloneString(&windows->backdrop.name,"ImageMagick Backdrop");
14517       windows->backdrop.flags=(unsigned long) (USSize | USPosition);
14518       windows->backdrop.width=(unsigned int)
14519         XDisplayWidth(display,visual_info->screen);
14520       windows->backdrop.height=(unsigned int)
14521         XDisplayHeight(display,visual_info->screen);
14522       windows->backdrop.border_width=0;
14523       windows->backdrop.immutable=MagickTrue;
14524       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14525         ButtonReleaseMask;
14526       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14527         StructureNotifyMask;
14528       (void) CloneString(&class_hints->res_name,"backdrop");
14529       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14530       manager_hints->icon_window=windows->icon.id;
14531       manager_hints->input=MagickTrue;
14532       manager_hints->initial_state=resource_info->iconic ? IconicState :
14533         NormalState;
14534       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14535         &windows->backdrop);
14536       if (display_image->debug != MagickFalse)
14537         (void) LogMagickEvent(X11Event,GetMagickModule(),
14538           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14539       (void) XMapWindow(display,windows->backdrop.id);
14540       (void) XClearWindow(display,windows->backdrop.id);
14541       if (windows->image.id != (Window) NULL)
14542         {
14543           (void) XDestroyWindow(display,windows->image.id);
14544           windows->image.id=(Window) NULL;
14545         }
14546       /*
14547         Position image in the center the backdrop.
14548       */
14549       windows->image.flags|=USPosition;
14550       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14551         (windows->image.width/2);
14552       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14553         (windows->image.height/2);
14554     }
14555   if (resource_info->name == (char *) NULL)
14556     (void) CloneString(&class_hints->res_name,resource_info->client_name);
14557   else
14558     (void) CloneString(&class_hints->res_name,resource_info->name);
14559   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14560   manager_hints->icon_window=windows->icon.id;
14561   manager_hints->input=MagickTrue;
14562   manager_hints->initial_state=resource_info->iconic ? IconicState :
14563     NormalState;
14564   if (windows->group_leader.id != (Window) NULL)
14565     {
14566       /*
14567         Follow the leader.
14568       */
14569       manager_hints->flags|=WindowGroupHint;
14570       manager_hints->window_group=windows->group_leader.id;
14571       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14572       if (display_image->debug != MagickFalse)
14573         (void) LogMagickEvent(X11Event,GetMagickModule(),
14574           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14575     }
14576   XMakeWindow(display,
14577     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14578     argv,argc,class_hints,manager_hints,&windows->image);
14579   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14580     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14581   if (windows->group_leader.id != (Window) NULL)
14582     (void) XSetTransientForHint(display,windows->image.id,
14583       windows->group_leader.id);
14584   if (display_image->debug != MagickFalse)
14585     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14586       windows->image.id);
14587   /*
14588     Initialize Info widget.
14589   */
14590   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14591     &windows->info);
14592   (void) CloneString(&windows->info.name,"Info");
14593   (void) CloneString(&windows->info.icon_name,"Info");
14594   windows->info.border_width=1;
14595   windows->info.x=2;
14596   windows->info.y=2;
14597   windows->info.flags|=PPosition;
14598   windows->info.attributes.win_gravity=UnmapGravity;
14599   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14600     StructureNotifyMask;
14601   (void) CloneString(&class_hints->res_name,"info");
14602   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14603   manager_hints->input=MagickFalse;
14604   manager_hints->initial_state=NormalState;
14605   manager_hints->window_group=windows->image.id;
14606   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14607     &windows->info);
14608   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14609     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14610   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14611     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14612   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14613   if (windows->image.mapped != MagickFalse)
14614     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14615   if (display_image->debug != MagickFalse)
14616     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14617       windows->info.id);
14618   /*
14619     Initialize Command widget.
14620   */
14621   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14622     resource_info,&windows->command);
14623   windows->command.data=MagickMenus;
14624   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14625   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.command",
14626     resource_info->client_name);
14627   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14628     resource_name,"geometry",(char *) NULL);
14629   (void) CloneString(&windows->command.name,MagickTitle);
14630   windows->command.border_width=0;
14631   windows->command.flags|=PPosition;
14632   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14633     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14634     OwnerGrabButtonMask | StructureNotifyMask;
14635   (void) CloneString(&class_hints->res_name,"command");
14636   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14637   manager_hints->input=MagickTrue;
14638   manager_hints->initial_state=NormalState;
14639   manager_hints->window_group=windows->image.id;
14640   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14641     &windows->command);
14642   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14643     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14644     HighlightHeight);
14645   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14646     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14647   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14648   if (windows->command.mapped != MagickFalse)
14649     (void) XMapRaised(display,windows->command.id);
14650   if (display_image->debug != MagickFalse)
14651     (void) LogMagickEvent(X11Event,GetMagickModule(),
14652       "Window id: 0x%lx (command)",windows->command.id);
14653   /*
14654     Initialize Widget window.
14655   */
14656   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14657     resource_info,&windows->widget);
14658   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.widget",
14659     resource_info->client_name);
14660   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14661     resource_name,"geometry",(char *) NULL);
14662   windows->widget.border_width=0;
14663   windows->widget.flags|=PPosition;
14664   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14665     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14666     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14667     StructureNotifyMask;
14668   (void) CloneString(&class_hints->res_name,"widget");
14669   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14670   manager_hints->input=MagickTrue;
14671   manager_hints->initial_state=NormalState;
14672   manager_hints->window_group=windows->image.id;
14673   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14674     &windows->widget);
14675   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14676     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14677   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14678     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14679   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14680   if (display_image->debug != MagickFalse)
14681     (void) LogMagickEvent(X11Event,GetMagickModule(),
14682       "Window id: 0x%lx (widget)",windows->widget.id);
14683   /*
14684     Initialize popup window.
14685   */
14686   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14687     resource_info,&windows->popup);
14688   windows->popup.border_width=0;
14689   windows->popup.flags|=PPosition;
14690   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14691     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14692     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14693   (void) CloneString(&class_hints->res_name,"popup");
14694   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14695   manager_hints->input=MagickTrue;
14696   manager_hints->initial_state=NormalState;
14697   manager_hints->window_group=windows->image.id;
14698   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14699     &windows->popup);
14700   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14701     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14702   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14703     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14704   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14705   if (display_image->debug != MagickFalse)
14706     (void) LogMagickEvent(X11Event,GetMagickModule(),
14707       "Window id: 0x%lx (pop up)",windows->popup.id);
14708   /*
14709     Initialize Magnify window and cursor.
14710   */
14711   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14712     resource_info,&windows->magnify);
14713   if (resource_info->use_shared_memory == MagickFalse)
14714     windows->magnify.shared_memory=MagickFalse;
14715   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.magnify",
14716     resource_info->client_name);
14717   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14718     resource_name,"geometry",(char *) NULL);
14719   (void) FormatMagickString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14720     resource_info->magnify);
14721   if (windows->magnify.cursor != (Cursor) NULL)
14722     (void) XFreeCursor(display,windows->magnify.cursor);
14723   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14724     map_info->colormap,resource_info->background_color,
14725     resource_info->foreground_color);
14726   if (windows->magnify.cursor == (Cursor) NULL)
14727     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14728       display_image->filename);
14729   windows->magnify.width=MagnifySize;
14730   windows->magnify.height=MagnifySize;
14731   windows->magnify.flags|=PPosition;
14732   windows->magnify.min_width=MagnifySize;
14733   windows->magnify.min_height=MagnifySize;
14734   windows->magnify.width_inc=MagnifySize;
14735   windows->magnify.height_inc=MagnifySize;
14736   windows->magnify.data=resource_info->magnify;
14737   windows->magnify.attributes.cursor=windows->magnify.cursor;
14738   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14739     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14740     StructureNotifyMask;
14741   (void) CloneString(&class_hints->res_name,"magnify");
14742   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14743   manager_hints->input=MagickTrue;
14744   manager_hints->initial_state=NormalState;
14745   manager_hints->window_group=windows->image.id;
14746   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14747     &windows->magnify);
14748   if (display_image->debug != MagickFalse)
14749     (void) LogMagickEvent(X11Event,GetMagickModule(),
14750       "Window id: 0x%lx (magnify)",windows->magnify.id);
14751   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14752   /*
14753     Initialize panning window.
14754   */
14755   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14756     resource_info,&windows->pan);
14757   (void) CloneString(&windows->pan.name,"Pan Icon");
14758   windows->pan.width=windows->icon.width;
14759   windows->pan.height=windows->icon.height;
14760   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.pan",
14761     resource_info->client_name);
14762   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14763     resource_name,"geometry",(char *) NULL);
14764   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14765     &windows->pan.width,&windows->pan.height);
14766   windows->pan.flags|=PPosition;
14767   windows->pan.immutable=MagickTrue;
14768   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14769     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14770     StructureNotifyMask;
14771   (void) CloneString(&class_hints->res_name,"pan");
14772   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14773   manager_hints->input=MagickFalse;
14774   manager_hints->initial_state=NormalState;
14775   manager_hints->window_group=windows->image.id;
14776   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14777     &windows->pan);
14778   if (display_image->debug != MagickFalse)
14779     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14780       windows->pan.id);
14781   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14782   if (windows->info.mapped != MagickFalse)
14783     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14784   if ((windows->image.mapped == MagickFalse) ||
14785       (windows->backdrop.id != (Window) NULL))
14786     (void) XMapWindow(display,windows->image.id);
14787   /*
14788     Set our progress monitor and warning handlers.
14789   */
14790   if (warning_handler == (WarningHandler) NULL)
14791     {
14792       warning_handler=resource_info->display_warnings ?
14793         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14794       warning_handler=resource_info->display_warnings ?
14795         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14796     }
14797   /*
14798     Initialize Image and Magnify X images.
14799   */
14800   windows->image.x=0;
14801   windows->image.y=0;
14802   windows->magnify.shape=MagickFalse;
14803   width=(unsigned int) display_image->columns;
14804   height=(unsigned int) display_image->rows;
14805   if ((display_image->columns != width) || (display_image->rows != height))
14806     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14807       display_image->filename);
14808   status=XMakeImage(display,resource_info,&windows->image,display_image,
14809     width,height);
14810   if (status == MagickFalse)
14811     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14812       display_image->filename);
14813   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14814     windows->magnify.width,windows->magnify.height);
14815   if (status == MagickFalse)
14816     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14817       display_image->filename);
14818   if (windows->magnify.mapped != MagickFalse)
14819     (void) XMapRaised(display,windows->magnify.id);
14820   if (windows->pan.mapped != MagickFalse)
14821     (void) XMapRaised(display,windows->pan.id);
14822   windows->image.window_changes.width=(int) display_image->columns;
14823   windows->image.window_changes.height=(int) display_image->rows;
14824   (void) XConfigureImage(display,resource_info,windows,display_image);
14825   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14826   (void) XSync(display,MagickFalse);
14827   /*
14828     Respond to events.
14829   */
14830   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14831   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14832   update_time=0;
14833   if (resource_info->update != MagickFalse)
14834     {
14835       MagickBooleanType
14836         status;
14837 
14838       /*
14839         Determine when file data was last modified.
14840       */
14841       status=GetPathAttributes(display_image->filename,&attributes);
14842       if (status != MagickFalse)
14843         update_time=attributes.st_mtime;
14844     }
14845   *state&=(~FormerImageState);
14846   *state&=(~MontageImageState);
14847   *state&=(~NextImageState);
14848   do
14849   {
14850     /*
14851       Handle a window event.
14852     */
14853     if (windows->image.mapped != MagickFalse)
14854       if ((display_image->delay != 0) || (resource_info->update != 0))
14855         {
14856           if (timer < time((time_t *) NULL))
14857             {
14858               if (resource_info->update == MagickFalse)
14859                 *state|=ExitState;
14860               else
14861                 {
14862                   MagickBooleanType
14863                     status;
14864 
14865                   /*
14866                     Determine if image file was modified.
14867                   */
14868                   status=GetPathAttributes(display_image->filename,&attributes);
14869                   if (status != MagickFalse)
14870                     if (update_time != attributes.st_mtime)
14871                       {
14872                         /*
14873                           Redisplay image.
14874                         */
14875                         (void) FormatMagickString(
14876                           resource_info->image_info->filename,MaxTextExtent,
14877                           "%s:%s",display_image->magick,
14878                           display_image->filename);
14879                         nexus=ReadImage(resource_info->image_info,
14880                           &display_image->exception);
14881                         if (nexus != (Image *) NULL)
14882                           {
14883                             nexus=DestroyImage(nexus);
14884                             *state|=NextImageState | ExitState;
14885                           }
14886                       }
14887                   delay=display_image->delay/MagickMax(
14888                     display_image->ticks_per_second,1L);
14889                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14890                 }
14891             }
14892           if (XEventsQueued(display,QueuedAfterFlush) == 0)
14893             {
14894               /*
14895                 Do not block if delay > 0.
14896               */
14897               XDelay(display,SuspendTime << 2);
14898               continue;
14899             }
14900         }
14901     timestamp=time((time_t *) NULL);
14902     (void) XNextEvent(display,&event);
14903     if (windows->image.stasis == MagickFalse)
14904       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14905         MagickTrue : MagickFalse;
14906     if (windows->magnify.stasis == MagickFalse)
14907       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14908         MagickTrue : MagickFalse;
14909     if (event.xany.window == windows->command.id)
14910       {
14911         /*
14912           Select a command from the Command widget.
14913         */
14914         id=XCommandWidget(display,windows,CommandMenu,&event);
14915         if (id < 0)
14916           continue;
14917         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
14918         command_type=CommandMenus[id];
14919         if (id < MagickMenus)
14920           {
14921             /*
14922               Select a command from a pop-up menu.
14923             */
14924             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
14925               command);
14926             if (entry < 0)
14927               continue;
14928             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
14929             command_type=Commands[id][entry];
14930           }
14931         if (command_type != NullCommand)
14932           nexus=XMagickCommand(display,resource_info,windows,command_type,
14933             &display_image);
14934         continue;
14935       }
14936     switch (event.type)
14937     {
14938       case ButtonPress:
14939       {
14940         if (display_image->debug != MagickFalse)
14941           (void) LogMagickEvent(X11Event,GetMagickModule(),
14942             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
14943             event.xbutton.button,event.xbutton.x,event.xbutton.y);
14944         if ((event.xbutton.button == Button3) &&
14945             (event.xbutton.state & Mod1Mask))
14946           {
14947             /*
14948               Convert Alt-Button3 to Button2.
14949             */
14950             event.xbutton.button=Button2;
14951             event.xbutton.state&=(~Mod1Mask);
14952           }
14953         if (event.xbutton.window == windows->backdrop.id)
14954           {
14955             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
14956               event.xbutton.time);
14957             break;
14958           }
14959         if (event.xbutton.window == windows->image.id)
14960           {
14961             switch (event.xbutton.button)
14962             {
14963               case Button1:
14964               {
14965                 if (resource_info->immutable)
14966                   {
14967                     /*
14968                       Select a command from the Virtual menu.
14969                     */
14970                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
14971                       command);
14972                     if (entry >= 0)
14973                       nexus=XMagickCommand(display,resource_info,windows,
14974                         VirtualCommands[entry],&display_image);
14975                     break;
14976                   }
14977                 /*
14978                   Map/unmap Command widget.
14979                 */
14980                 if (windows->command.mapped != MagickFalse)
14981                   (void) XWithdrawWindow(display,windows->command.id,
14982                     windows->command.screen);
14983                 else
14984                   {
14985                     (void) XCommandWidget(display,windows,CommandMenu,
14986                       (XEvent *) NULL);
14987                     (void) XMapRaised(display,windows->command.id);
14988                   }
14989                 break;
14990               }
14991               case Button2:
14992               {
14993                 /*
14994                   User pressed the image magnify button.
14995                 */
14996                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
14997                   &display_image);
14998                 XMagnifyImage(display,windows,&event);
14999                 break;
15000               }
15001               case Button3:
15002               {
15003                 if (resource_info->immutable)
15004                   {
15005                     /*
15006                       Select a command from the Virtual menu.
15007                     */
15008                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15009                       command);
15010                     if (entry >= 0)
15011                       nexus=XMagickCommand(display,resource_info,windows,
15012                         VirtualCommands[entry],&display_image);
15013                     break;
15014                   }
15015                 if (display_image->montage != (char *) NULL)
15016                   {
15017                     /*
15018                       Open or delete a tile from a visual image directory.
15019                     */
15020                     nexus=XTileImage(display,resource_info,windows,
15021                       display_image,&event);
15022                     if (nexus != (Image *) NULL)
15023                       *state|=MontageImageState | NextImageState | ExitState;
15024                     vid_info.x=windows->image.x;
15025                     vid_info.y=windows->image.y;
15026                     break;
15027                   }
15028                 /*
15029                   Select a command from the Short Cuts menu.
15030                 */
15031                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15032                   command);
15033                 if (entry >= 0)
15034                   nexus=XMagickCommand(display,resource_info,windows,
15035                     ShortCutsCommands[entry],&display_image);
15036                 break;
15037               }
15038               case Button4:
15039               {
15040                 /*
15041                   Wheel up.
15042                 */
15043                 XTranslateImage(display,windows,*image,XK_Up);
15044                 break;
15045               }
15046               case Button5:
15047               {
15048                 /*
15049                   Wheel down.
15050                 */
15051                 XTranslateImage(display,windows,*image,XK_Down);
15052                 break;
15053               }
15054               default:
15055                 break;
15056             }
15057             break;
15058           }
15059         if (event.xbutton.window == windows->magnify.id)
15060           {
15061             int
15062               factor;
15063 
15064             static const char
15065               *MagnifyMenu[] =
15066               {
15067                 "2",
15068                 "4",
15069                 "5",
15070                 "6",
15071                 "7",
15072                 "8",
15073                 "9",
15074                 "3",
15075                 (char *) NULL,
15076               };
15077 
15078             static KeySym
15079               MagnifyCommands[] =
15080               {
15081                 XK_2,
15082                 XK_4,
15083                 XK_5,
15084                 XK_6,
15085                 XK_7,
15086                 XK_8,
15087                 XK_9,
15088                 XK_3
15089               };
15090 
15091             /*
15092               Select a magnify factor from the pop-up menu.
15093             */
15094             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15095             if (factor >= 0)
15096               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15097             break;
15098           }
15099         if (event.xbutton.window == windows->pan.id)
15100           {
15101             switch (event.xbutton.button)
15102             {
15103               case Button4:
15104               {
15105                 /*
15106                   Wheel up.
15107                 */
15108                 XTranslateImage(display,windows,*image,XK_Up);
15109                 break;
15110               }
15111               case Button5:
15112               {
15113                 /*
15114                   Wheel down.
15115                 */
15116                 XTranslateImage(display,windows,*image,XK_Down);
15117                 break;
15118               }
15119               default:
15120               {
15121                 XPanImage(display,windows,&event);
15122                 break;
15123               }
15124             }
15125             break;
15126           }
15127         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15128           1L);
15129         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15130         break;
15131       }
15132       case ButtonRelease:
15133       {
15134         if (display_image->debug != MagickFalse)
15135           (void) LogMagickEvent(X11Event,GetMagickModule(),
15136             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15137             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15138         break;
15139       }
15140       case ClientMessage:
15141       {
15142         if (display_image->debug != MagickFalse)
15143           (void) LogMagickEvent(X11Event,GetMagickModule(),
15144             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15145             event.xclient.message_type,event.xclient.format,(unsigned long)
15146             event.xclient.data.l[0]);
15147         if (event.xclient.message_type == windows->im_protocols)
15148           {
15149             if (*event.xclient.data.l == (long) windows->im_update_widget)
15150               {
15151                 (void) CloneString(&windows->command.name,MagickTitle);
15152                 windows->command.data=MagickMenus;
15153                 (void) XCommandWidget(display,windows,CommandMenu,
15154                   (XEvent *) NULL);
15155                 break;
15156               }
15157             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15158               {
15159                 /*
15160                   Update graphic context and window colormap.
15161                 */
15162                 for (i=0; i < (int) number_windows; i++)
15163                 {
15164                   if (magick_windows[i]->id == windows->icon.id)
15165                     continue;
15166                   context_values.background=pixel->background_color.pixel;
15167                   context_values.foreground=pixel->foreground_color.pixel;
15168                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15169                     context_mask,&context_values);
15170                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15171                     context_mask,&context_values);
15172                   context_values.background=pixel->foreground_color.pixel;
15173                   context_values.foreground=pixel->background_color.pixel;
15174                   context_values.plane_mask=context_values.background ^
15175                     context_values.foreground;
15176                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15177                     (unsigned long) (context_mask | GCPlaneMask),
15178                     &context_values);
15179                   magick_windows[i]->attributes.background_pixel=
15180                     pixel->background_color.pixel;
15181                   magick_windows[i]->attributes.border_pixel=
15182                     pixel->border_color.pixel;
15183                   magick_windows[i]->attributes.colormap=map_info->colormap;
15184                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15185                     magick_windows[i]->mask,&magick_windows[i]->attributes);
15186                 }
15187                 if (windows->pan.mapped != MagickFalse)
15188                   {
15189                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15190                       windows->pan.pixmap);
15191                     (void) XClearWindow(display,windows->pan.id);
15192                     XDrawPanRectangle(display,windows);
15193                   }
15194                 if (windows->backdrop.id != (Window) NULL)
15195                   (void) XInstallColormap(display,map_info->colormap);
15196                 break;
15197               }
15198             if (*event.xclient.data.l == (long) windows->im_former_image)
15199               {
15200                 *state|=FormerImageState | ExitState;
15201                 break;
15202               }
15203             if (*event.xclient.data.l == (long) windows->im_next_image)
15204               {
15205                 *state|=NextImageState | ExitState;
15206                 break;
15207               }
15208             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15209               {
15210                 *state|=RetainColorsState;
15211                 break;
15212               }
15213             if (*event.xclient.data.l == (long) windows->im_exit)
15214               {
15215                 *state|=ExitState;
15216                 break;
15217               }
15218             break;
15219           }
15220         if (event.xclient.message_type == windows->dnd_protocols)
15221           {
15222             Atom
15223               selection,
15224               type;
15225 
15226             int
15227               format,
15228               status;
15229 
15230             unsigned char
15231               *data;
15232 
15233             unsigned long
15234               after,
15235               length;
15236 
15237             /*
15238               Display image named by the Drag-and-Drop selection.
15239             */
15240             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15241               break;
15242             selection=XInternAtom(display,"DndSelection",MagickFalse);
15243             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15244               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15245               &length,&after,&data);
15246             if ((status != Success) || (length == 0))
15247               break;
15248             if (*event.xclient.data.l == 2)
15249               {
15250                 /*
15251                   Offix DND.
15252                 */
15253                 (void) CopyMagickString(resource_info->image_info->filename,
15254                   (char *) data,MaxTextExtent);
15255               }
15256             else
15257               {
15258                 /*
15259                   XDND.
15260                 */
15261                 if (strncmp((char *) data, "file:", 5) != 0)
15262                   {
15263                     (void) XFree((void *) data);
15264                     break;
15265                   }
15266                 (void) CopyMagickString(resource_info->image_info->filename,
15267                   ((char *) data)+5,MaxTextExtent);
15268               }
15269             nexus=ReadImage(resource_info->image_info,
15270               &display_image->exception);
15271             CatchException(&display_image->exception);
15272             if (nexus != (Image *) NULL)
15273               *state|=NextImageState | ExitState;
15274             (void) XFree((void *) data);
15275             break;
15276           }
15277         /*
15278           If client window delete message, exit.
15279         */
15280         if (event.xclient.message_type != windows->wm_protocols)
15281           break;
15282         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15283           break;
15284         (void) XWithdrawWindow(display,event.xclient.window,
15285           visual_info->screen);
15286         if (event.xclient.window == windows->image.id)
15287           {
15288             *state|=ExitState;
15289             break;
15290           }
15291         if (event.xclient.window == windows->pan.id)
15292           {
15293             /*
15294               Restore original image size when pan window is deleted.
15295             */
15296             windows->image.window_changes.width=windows->image.ximage->width;
15297             windows->image.window_changes.height=windows->image.ximage->height;
15298             (void) XConfigureImage(display,resource_info,windows,
15299               display_image);
15300           }
15301         break;
15302       }
15303       case ConfigureNotify:
15304       {
15305         if (display_image->debug != MagickFalse)
15306           (void) LogMagickEvent(X11Event,GetMagickModule(),
15307             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15308             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15309             event.xconfigure.y,event.xconfigure.send_event);
15310         if (event.xconfigure.window == windows->image.id)
15311           {
15312             /*
15313               Image window has a new configuration.
15314             */
15315             if (event.xconfigure.send_event != 0)
15316               {
15317                 XWindowChanges
15318                   window_changes;
15319 
15320                 /*
15321                   Position the transient windows relative of the Image window.
15322                 */
15323                 if (windows->command.geometry == (char *) NULL)
15324                   if (windows->command.mapped == MagickFalse)
15325                     {
15326                       windows->command.x=event.xconfigure.x-
15327                         windows->command.width-25;
15328                       windows->command.y=event.xconfigure.y;
15329                       XConstrainWindowPosition(display,&windows->command);
15330                       window_changes.x=windows->command.x;
15331                       window_changes.y=windows->command.y;
15332                       (void) XReconfigureWMWindow(display,windows->command.id,
15333                         windows->command.screen,(unsigned int) (CWX | CWY),
15334                         &window_changes);
15335                     }
15336                 if (windows->widget.geometry == (char *) NULL)
15337                   if (windows->widget.mapped == MagickFalse)
15338                     {
15339                       windows->widget.x=event.xconfigure.x+
15340                         event.xconfigure.width/10;
15341                       windows->widget.y=event.xconfigure.y+
15342                         event.xconfigure.height/10;
15343                       XConstrainWindowPosition(display,&windows->widget);
15344                       window_changes.x=windows->widget.x;
15345                       window_changes.y=windows->widget.y;
15346                       (void) XReconfigureWMWindow(display,windows->widget.id,
15347                         windows->widget.screen,(unsigned int) (CWX | CWY),
15348                         &window_changes);
15349                     }
15350                 if (windows->magnify.geometry == (char *) NULL)
15351                   if (windows->magnify.mapped == MagickFalse)
15352                     {
15353                       windows->magnify.x=event.xconfigure.x+
15354                         event.xconfigure.width+25;
15355                       windows->magnify.y=event.xconfigure.y;
15356                       XConstrainWindowPosition(display,&windows->magnify);
15357                       window_changes.x=windows->magnify.x;
15358                       window_changes.y=windows->magnify.y;
15359                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15360                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15361                         &window_changes);
15362                     }
15363                 if (windows->pan.geometry == (char *) NULL)
15364                   if (windows->pan.mapped == MagickFalse)
15365                     {
15366                       windows->pan.x=event.xconfigure.x+
15367                         event.xconfigure.width+25;
15368                       windows->pan.y=event.xconfigure.y+
15369                         windows->magnify.height+50;
15370                       XConstrainWindowPosition(display,&windows->pan);
15371                       window_changes.x=windows->pan.x;
15372                       window_changes.y=windows->pan.y;
15373                       (void) XReconfigureWMWindow(display,windows->pan.id,
15374                         windows->pan.screen,(unsigned int) (CWX | CWY),
15375                         &window_changes);
15376                     }
15377               }
15378             if ((event.xconfigure.width == (long) windows->image.width) &&
15379                 (event.xconfigure.height == (long) windows->image.height))
15380               break;
15381             windows->image.width=(unsigned int) event.xconfigure.width;
15382             windows->image.height=(unsigned int) event.xconfigure.height;
15383             windows->image.x=0;
15384             windows->image.y=0;
15385             if (display_image->montage != (char *) NULL)
15386               {
15387                 windows->image.x=vid_info.x;
15388                 windows->image.y=vid_info.y;
15389               }
15390             /*
15391               Update pan window configuration.
15392             */
15393             if ((event.xconfigure.width < windows->image.ximage->width) ||
15394                 (event.xconfigure.height < windows->image.ximage->height))
15395               {
15396                 (void) XMapRaised(display,windows->pan.id);
15397                 XDrawPanRectangle(display,windows);
15398               }
15399             else
15400               if (windows->pan.mapped != MagickFalse)
15401                 (void) XWithdrawWindow(display,windows->pan.id,
15402                   windows->pan.screen);
15403             break;
15404           }
15405         if (event.xconfigure.window == windows->magnify.id)
15406           {
15407             unsigned int
15408               magnify;
15409 
15410             /*
15411               Magnify window has a new configuration.
15412             */
15413             windows->magnify.width=(unsigned int) event.xconfigure.width;
15414             windows->magnify.height=(unsigned int) event.xconfigure.height;
15415             if (windows->magnify.mapped == MagickFalse)
15416               break;
15417             magnify=1;
15418             while ((int) magnify <= event.xconfigure.width)
15419               magnify<<=1;
15420             while ((int) magnify <= event.xconfigure.height)
15421               magnify<<=1;
15422             magnify>>=1;
15423             if (((int) magnify != event.xconfigure.width) ||
15424                 ((int) magnify != event.xconfigure.height))
15425               {
15426                 window_changes.width=(int) magnify;
15427                 window_changes.height=(int) magnify;
15428                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15429                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15430                   &window_changes);
15431                 break;
15432               }
15433             if ((windows->magnify.mapped != MagickFalse) &&
15434                 (windows->magnify.stasis != MagickFalse))
15435               {
15436                 status=XMakeImage(display,resource_info,&windows->magnify,
15437                   display_image,windows->magnify.width,windows->magnify.height);
15438                 XMakeMagnifyImage(display,windows);
15439               }
15440             break;
15441           }
15442         if ((windows->magnify.mapped != MagickFalse) &&
15443             (event.xconfigure.window == windows->pan.id))
15444           {
15445             /*
15446               Pan icon window has a new configuration.
15447             */
15448             if (event.xconfigure.send_event != 0)
15449               {
15450                 windows->pan.x=event.xconfigure.x;
15451                 windows->pan.y=event.xconfigure.y;
15452               }
15453             windows->pan.width=(unsigned int) event.xconfigure.width;
15454             windows->pan.height=(unsigned int) event.xconfigure.height;
15455             break;
15456           }
15457         if (event.xconfigure.window == windows->icon.id)
15458           {
15459             /*
15460               Icon window has a new configuration.
15461             */
15462             windows->icon.width=(unsigned int) event.xconfigure.width;
15463             windows->icon.height=(unsigned int) event.xconfigure.height;
15464             break;
15465           }
15466         break;
15467       }
15468       case DestroyNotify:
15469       {
15470         /*
15471           Group leader has exited.
15472         */
15473         if (display_image->debug != MagickFalse)
15474           (void) LogMagickEvent(X11Event,GetMagickModule(),
15475             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15476         if (event.xdestroywindow.window == windows->group_leader.id)
15477           {
15478             *state|=ExitState;
15479             break;
15480           }
15481         break;
15482       }
15483       case EnterNotify:
15484       {
15485         /*
15486           Selectively install colormap.
15487         */
15488         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15489           if (event.xcrossing.mode != NotifyUngrab)
15490             XInstallColormap(display,map_info->colormap);
15491         break;
15492       }
15493       case Expose:
15494       {
15495         if (display_image->debug != MagickFalse)
15496           (void) LogMagickEvent(X11Event,GetMagickModule(),
15497             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15498             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15499             event.xexpose.y);
15500         /*
15501           Refresh windows that are now exposed.
15502         */
15503         if (event.xexpose.window == windows->image.id)
15504           if (windows->image.mapped != MagickFalse)
15505             {
15506               XRefreshWindow(display,&windows->image,&event);
15507               delay=display_image->delay/MagickMax(
15508                 display_image->ticks_per_second,1L);
15509               timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15510               break;
15511             }
15512         if (event.xexpose.window == windows->magnify.id)
15513           if (event.xexpose.count == 0)
15514             if (windows->magnify.mapped != MagickFalse)
15515               {
15516                 XMakeMagnifyImage(display,windows);
15517                 break;
15518               }
15519         if (event.xexpose.window == windows->pan.id)
15520           if (event.xexpose.count == 0)
15521             {
15522               XDrawPanRectangle(display,windows);
15523               break;
15524             }
15525         if (event.xexpose.window == windows->icon.id)
15526           if (event.xexpose.count == 0)
15527             {
15528               XRefreshWindow(display,&windows->icon,&event);
15529               break;
15530             }
15531         break;
15532       }
15533       case KeyPress:
15534       {
15535         int
15536           length;
15537 
15538         /*
15539           Respond to a user key press.
15540         */
15541         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15542           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15543         *(command+length)='\0';
15544         if (display_image->debug != MagickFalse)
15545           (void) LogMagickEvent(X11Event,GetMagickModule(),
15546             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15547             key_symbol,command);
15548         if (event.xkey.window == windows->image.id)
15549           {
15550             command_type=XImageWindowCommand(display,resource_info,windows,
15551               event.xkey.state,key_symbol,&display_image);
15552             if (command_type != NullCommand)
15553               nexus=XMagickCommand(display,resource_info,windows,command_type,
15554                 &display_image);
15555           }
15556         if (event.xkey.window == windows->magnify.id)
15557           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15558         if (event.xkey.window == windows->pan.id)
15559           {
15560             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15561               (void) XWithdrawWindow(display,windows->pan.id,
15562                 windows->pan.screen);
15563             else
15564               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15565                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15566                   "Help Viewer - Image Pan",ImagePanHelp);
15567               else
15568                 XTranslateImage(display,windows,*image,key_symbol);
15569           }
15570         delay=display_image->delay/MagickMax(
15571           display_image->ticks_per_second,1L);
15572         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15573         break;
15574       }
15575       case KeyRelease:
15576       {
15577         /*
15578           Respond to a user key release.
15579         */
15580         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15581           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15582         if (display_image->debug != MagickFalse)
15583           (void) LogMagickEvent(X11Event,GetMagickModule(),
15584             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15585         break;
15586       }
15587       case LeaveNotify:
15588       {
15589         /*
15590           Selectively uninstall colormap.
15591         */
15592         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15593           if (event.xcrossing.mode != NotifyUngrab)
15594             XUninstallColormap(display,map_info->colormap);
15595         break;
15596       }
15597       case MapNotify:
15598       {
15599         if (display_image->debug != MagickFalse)
15600           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15601             event.xmap.window);
15602         if (event.xmap.window == windows->backdrop.id)
15603           {
15604             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15605               CurrentTime);
15606             windows->backdrop.mapped=MagickTrue;
15607             break;
15608           }
15609         if (event.xmap.window == windows->image.id)
15610           {
15611             if (windows->backdrop.id != (Window) NULL)
15612               (void) XInstallColormap(display,map_info->colormap);
15613             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15614               {
15615                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15616                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15617               }
15618             if (((int) windows->image.width < windows->image.ximage->width) ||
15619                 ((int) windows->image.height < windows->image.ximage->height))
15620               (void) XMapRaised(display,windows->pan.id);
15621             windows->image.mapped=MagickTrue;
15622             break;
15623           }
15624         if (event.xmap.window == windows->magnify.id)
15625           {
15626             XMakeMagnifyImage(display,windows);
15627             windows->magnify.mapped=MagickTrue;
15628             (void) XWithdrawWindow(display,windows->info.id,
15629               windows->info.screen);
15630             break;
15631           }
15632         if (event.xmap.window == windows->pan.id)
15633           {
15634             XMakePanImage(display,resource_info,windows,display_image);
15635             windows->pan.mapped=MagickTrue;
15636             break;
15637           }
15638         if (event.xmap.window == windows->info.id)
15639           {
15640             windows->info.mapped=MagickTrue;
15641             break;
15642           }
15643         if (event.xmap.window == windows->icon.id)
15644           {
15645             MagickBooleanType
15646               taint;
15647 
15648             /*
15649               Create an icon image.
15650             */
15651             taint=display_image->taint;
15652             XMakeStandardColormap(display,icon_visual,icon_resources,
15653               display_image,icon_map,icon_pixel);
15654             (void) XMakeImage(display,icon_resources,&windows->icon,
15655               display_image,windows->icon.width,windows->icon.height);
15656             display_image->taint=taint;
15657             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15658               windows->icon.pixmap);
15659             (void) XClearWindow(display,windows->icon.id);
15660             (void) XWithdrawWindow(display,windows->info.id,
15661               windows->info.screen);
15662             windows->icon.mapped=MagickTrue;
15663             break;
15664           }
15665         if (event.xmap.window == windows->command.id)
15666           {
15667             windows->command.mapped=MagickTrue;
15668             break;
15669           }
15670         if (event.xmap.window == windows->popup.id)
15671           {
15672             windows->popup.mapped=MagickTrue;
15673             break;
15674           }
15675         if (event.xmap.window == windows->widget.id)
15676           {
15677             windows->widget.mapped=MagickTrue;
15678             break;
15679           }
15680         break;
15681       }
15682       case MappingNotify:
15683       {
15684         (void) XRefreshKeyboardMapping(&event.xmapping);
15685         break;
15686       }
15687       case NoExpose:
15688         break;
15689       case PropertyNotify:
15690       {
15691         Atom
15692           type;
15693 
15694         int
15695           format,
15696           status;
15697 
15698         unsigned char
15699           *data;
15700 
15701         unsigned long
15702           after,
15703           length;
15704 
15705         if (display_image->debug != MagickFalse)
15706           (void) LogMagickEvent(X11Event,GetMagickModule(),
15707             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15708             event.xproperty.atom,event.xproperty.state);
15709         if (event.xproperty.atom != windows->im_remote_command)
15710           break;
15711         /*
15712           Display image named by the remote command protocol.
15713         */
15714         status=XGetWindowProperty(display,event.xproperty.window,
15715           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15716           AnyPropertyType,&type,&format,&length,&after,&data);
15717         if ((status != Success) || (length == 0))
15718           break;
15719         if (LocaleCompare((char *) data,"-quit") == 0)
15720           {
15721             XClientMessage(display,windows->image.id,windows->im_protocols,
15722               windows->im_exit,CurrentTime);
15723             (void) XFree((void *) data);
15724             break;
15725           }
15726         (void) CopyMagickString(resource_info->image_info->filename,
15727           (char *) data,MaxTextExtent);
15728         (void) XFree((void *) data);
15729         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15730         CatchException(&display_image->exception);
15731         if (nexus != (Image *) NULL)
15732           *state|=NextImageState | ExitState;
15733         break;
15734       }
15735       case ReparentNotify:
15736       {
15737         if (display_image->debug != MagickFalse)
15738           (void) LogMagickEvent(X11Event,GetMagickModule(),
15739             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15740             event.xreparent.window);
15741         break;
15742       }
15743       case UnmapNotify:
15744       {
15745         if (display_image->debug != MagickFalse)
15746           (void) LogMagickEvent(X11Event,GetMagickModule(),
15747             "Unmap Notify: 0x%lx",event.xunmap.window);
15748         if (event.xunmap.window == windows->backdrop.id)
15749           {
15750             windows->backdrop.mapped=MagickFalse;
15751             break;
15752           }
15753         if (event.xunmap.window == windows->image.id)
15754           {
15755             windows->image.mapped=MagickFalse;
15756             break;
15757           }
15758         if (event.xunmap.window == windows->magnify.id)
15759           {
15760             windows->magnify.mapped=MagickFalse;
15761             break;
15762           }
15763         if (event.xunmap.window == windows->pan.id)
15764           {
15765             windows->pan.mapped=MagickFalse;
15766             break;
15767           }
15768         if (event.xunmap.window == windows->info.id)
15769           {
15770             windows->info.mapped=MagickFalse;
15771             break;
15772           }
15773         if (event.xunmap.window == windows->icon.id)
15774           {
15775             if (map_info->colormap == icon_map->colormap)
15776               XConfigureImageColormap(display,resource_info,windows,
15777                 display_image);
15778             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15779               icon_pixel);
15780             windows->icon.mapped=MagickFalse;
15781             break;
15782           }
15783         if (event.xunmap.window == windows->command.id)
15784           {
15785             windows->command.mapped=MagickFalse;
15786             break;
15787           }
15788         if (event.xunmap.window == windows->popup.id)
15789           {
15790             if (windows->backdrop.id != (Window) NULL)
15791               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15792                 CurrentTime);
15793             windows->popup.mapped=MagickFalse;
15794             break;
15795           }
15796         if (event.xunmap.window == windows->widget.id)
15797           {
15798             if (windows->backdrop.id != (Window) NULL)
15799               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15800                 CurrentTime);
15801             windows->widget.mapped=MagickFalse;
15802             break;
15803           }
15804         break;
15805       }
15806       default:
15807       {
15808         if (display_image->debug != MagickFalse)
15809           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15810             event.type);
15811         break;
15812       }
15813     }
15814   }
15815   while (!(*state & ExitState));
15816   if ((*state & ExitState) == 0)
15817     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15818       &display_image);
15819   else
15820     if (resource_info->confirm_edit != MagickFalse)
15821       {
15822         /*
15823           Query user if image has changed.
15824         */
15825         if ((resource_info->immutable == MagickFalse) &&
15826             (display_image->taint != MagickFalse))
15827           {
15828             int
15829               status;
15830 
15831             status=XConfirmWidget(display,windows,"Your image changed.",
15832               "Do you want to save it");
15833             if (status == 0)
15834               *state&=(~ExitState);
15835             else
15836               if (status > 0)
15837                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15838                   &display_image);
15839           }
15840       }
15841   if ((windows->visual_info->klass == GrayScale) ||
15842       (windows->visual_info->klass == PseudoColor) ||
15843       (windows->visual_info->klass == DirectColor))
15844     {
15845       /*
15846         Withdraw pan and Magnify window.
15847       */
15848       if (windows->info.mapped != MagickFalse)
15849         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15850       if (windows->magnify.mapped != MagickFalse)
15851         (void) XWithdrawWindow(display,windows->magnify.id,
15852           windows->magnify.screen);
15853       if (windows->command.mapped != MagickFalse)
15854         (void) XWithdrawWindow(display,windows->command.id,
15855           windows->command.screen);
15856     }
15857   if (windows->pan.mapped != MagickFalse)
15858     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15859   if (resource_info->backdrop == MagickFalse)
15860     if (windows->backdrop.mapped)
15861       {
15862         (void) XWithdrawWindow(display,windows->backdrop.id,
15863           windows->backdrop.screen);
15864         (void) XDestroyWindow(display,windows->backdrop.id);
15865         windows->backdrop.id=(Window) NULL;
15866         (void) XWithdrawWindow(display,windows->image.id,
15867           windows->image.screen);
15868         (void) XDestroyWindow(display,windows->image.id);
15869         windows->image.id=(Window) NULL;
15870       }
15871   XSetCursorState(display,windows,MagickTrue);
15872   XCheckRefreshWindows(display,windows);
15873   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15874     *state&=(~ExitState);
15875   if (*state & ExitState)
15876     {
15877       /*
15878         Free Standard Colormap.
15879       */
15880       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15881       if (resource_info->map_type == (char *) NULL)
15882         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15883       /*
15884         Free X resources.
15885       */
15886       if (resource_info->copy_image != (Image *) NULL)
15887         {
15888           resource_info->copy_image=DestroyImage(resource_info->copy_image);
15889           resource_info->copy_image=NewImageList();
15890         }
15891       DestroyXResources();
15892     }
15893   (void) XSync(display,MagickFalse);
15894   /*
15895     Restore our progress monitor and warning handlers.
15896   */
15897   (void) SetErrorHandler(warning_handler);
15898   (void) SetWarningHandler(warning_handler);
15899   /*
15900     Change to home directory.
15901   */
15902   cwd=getcwd(working_directory,MaxTextExtent);
15903   {
15904     int
15905       status;
15906 
15907     status=chdir(resource_info->home_directory);
15908     if (status == -1)
15909       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
15910         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
15911   }
15912   *image=display_image;
15913   return(nexus);
15914 }
15915 #else
15916 
15917 /*
15918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15919 %                                                                             %
15920 %                                                                             %
15921 %                                                                             %
15922 +   D i s p l a y I m a g e s                                                 %
15923 %                                                                             %
15924 %                                                                             %
15925 %                                                                             %
15926 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15927 %
15928 %  DisplayImages() displays an image sequence to any X window screen.  It
15929 %  returns a value other than 0 if successful.  Check the exception member
15930 %  of image to determine the reason for any failure.
15931 %
15932 %  The format of the DisplayImages method is:
15933 %
15934 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
15935 %        Image *images)
15936 %
15937 %  A description of each parameter follows:
15938 %
15939 %    o image_info: the image info.
15940 %
15941 %    o image: the image.
15942 %
15943 */
15944 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
15945   Image *image)
15946 {
15947   assert(image_info != (const ImageInfo *) NULL);
15948   assert(image_info->signature == MagickSignature);
15949   assert(image != (Image *) NULL);
15950   assert(image->signature == MagickSignature);
15951   if (image->debug != MagickFalse)
15952     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
15953   (void) ThrowMagickException(&image->exception,GetMagickModule(),
15954     MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
15955     image->filename);
15956   return(MagickFalse);
15957 }
15958 
15959 /*
15960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15961 %                                                                             %
15962 %                                                                             %
15963 %                                                                             %
15964 +   R e m o t e D i s p l a y C o m m a n d                                   %
15965 %                                                                             %
15966 %                                                                             %
15967 %                                                                             %
15968 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15969 %
15970 %  RemoteDisplayCommand() encourages a remote display program to display the
15971 %  specified image filename.
15972 %
15973 %  The format of the RemoteDisplayCommand method is:
15974 %
15975 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
15976 %        const char *window,const char *filename,ExceptionInfo *exception)
15977 %
15978 %  A description of each parameter follows:
15979 %
15980 %    o image_info: the image info.
15981 %
15982 %    o window: Specifies the name or id of an X window.
15983 %
15984 %    o filename: the name of the image filename to display.
15985 %
15986 %    o exception: return any errors or warnings in this structure.
15987 %
15988 */
15989 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
15990   const char *window,const char *filename,ExceptionInfo *exception)
15991 {
15992   assert(image_info != (const ImageInfo *) NULL);
15993   assert(image_info->signature == MagickSignature);
15994   assert(filename != (char *) NULL);
15995   (void) window;
15996   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
15997   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
15998     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
15999   return(MagickFalse);
16000 }
16001 #endif

Generated on Thu Jul 2 12:03:19 2009 for MagickCore by  doxygen 1.5.8