########################################################################
##
## Copyright (C) 2014-2024 The Octave Project Developers
##
## See the file COPYRIGHT.md in the top-level directory of this
## distribution or <https://octave.org/copyright/>.
##
## This file is part of Octave.
##
## Octave is free software: you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Octave is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING.  If not, see
## <https://www.gnu.org/licenses/>.
##
########################################################################

## -*- texinfo -*-
## @deftypefn {} {@var{retval} =} genpropdoc (@var{OBJNAME}, @var{FILENAME})
##
## Print FILENAME texinfo source file associated to OBJNAME objects.  This
## function is meant to be run for generating octave documentation
## (see doc/interpreter/graphics_properties.mk).
##
## All the hard coded documentation is written in getdoc function.  See the
## comments in getdoc bellow for instruction on how to document a graphics
## property.
##
## @seealso{}
## @end deftypefn

function genpropdoc (objname, fname = "", props = {})
  objnames = {"root", "figure", "axes", "legend", ...
              "image", "light", "line", "patch", "scatter", "surface", ...
              "text", "uibuttongroup", "uicontextmenu", "uicontrol", ...
              "uipanel", "uimenu", "uipushtool", "uitable", ...
              "uitoggletool", "uitoolbar"
             };

  ## Base properties
  base = getstructure ("base");

  ## Object properties
  if (any (strcmp (objname, objnames)))
    obj = getstructure (objname, base, props);
  else
    error ("genpropdoc: unknown object %s", objname);
  endif

  ## Docstring
  str = printdoc (objname, obj, ! isempty (props));

  if (! isempty (fname))
    fid = fopen (fname,  "w+");
    if (fid < 0)
      error ("genpropdoc: couldn't open %s.", fname);
    endif
  else
    fid = stdout;
  endif

  fprintf (fid, str);

  if (nargin == 2)
    fclose (fid);
  endif

endfunction

function s = getdoc (objname, field, base)
  ## Properties are represented by a struct with fields :
  ##
  ## -"doc": string to be printed verbatim after being expanded
  ##   through expand_doc function.  Special keywords are:
  ##   "__objname__" : replaced by the current object name;
  ##   "__prop__"    : replaced by the current property name;
  ##   "__modemsg__" : replaced by a message explaining that
  ##                   the propmode will be toggled to "manual".
  ##   "__fcnmsg__"  : replaced by a message explaining where to find
  ##                   documentation on the form of a callback function.
  ##   You may also cross reference properties using the label format
  ##   OBJNAMEPROPERTY, e.g., "@xref{XREFaxescolor, , axes color property}."
  ##
  ## -"valid": string that describes valid values for the current property.
  ##   Use "packopt" function to join options with " | " separator
  ##   and "markdef" to mark default among valid values between curly braces.
  ##   If not provided, valid values for radio properties are automatically
  ##   retrieved using set function.
  ##
  ## -"default": string.  If not provided the default value is automatically
  ##   retrieved using get function.
  ##
  ## -"printdefault": a boolean (def. true) that specifies whether the
  ##   default value should be printed.  It is useful for properties
  ##   like root "screendepth" that default to screen dependent values.
  ##
  ## -"category": a string that is used to group properties.  Properties
  ##   without category designations will be defaulted into a "Miscellaneous"
  ##   category.  The "Miscellaneous" and (if it is used) the "Unused"
  ##   categories will be sorted to the end of each document.

  packopt = @(c) strjoin (c, " | ");
  markdef = @(s) ["@{" s "@}"];

  ## Some generic templates:
  valid_color = "colorspec";
  valid_handle = "graphics handle";
  valid_string = "string";
  valid_fcn = packopt ({"string", "function handle"});
  valid_cellstring = packopt ({"string", "cell array of strings"});
  valid_2elvec = "two-element vector";
  valid_3elvec = "three-element vector";
  valid_4elvec = "four-element vector";
  valid_vecmat = packopt ({"vector", "matrix"});
  valid_scalmat = packopt ({"scalar", "matrix"});
  valid_scalmatarr = packopt ({"scalar", "matrix", "array"});

  doc_notimpl = "%s is not yet implemented for __objname__ objects.  \
__prop__ is unused.";
  doc_unused =  "__prop__ is unused.";

  doc_fontangle = "Control whether the font is italic or normal.";
  doc_fontsize = "Size of the font used for text rendering.  \
@xref{XREF__objname__fontunits, , fontunits property}.";
  doc_fontname = "Name of font used for text rendering.  When setting \
this property, the text rendering engine will search for a matching \
font in your system.  If none is found then text is rendered using a \
default sans serif font (same as the default @qcode{\"*\"} value).\n\n\
Programming Note: On systems that don’t use FontConfig natively \
(all but Linux), the font cache is built when Octave is installed.  \
You will need to run @code{system (\"fc-cache -fv\")} manually after \
installing new fonts.";
  doc_fontunits = "Units used to interpret the @qcode{\"fontsize\"} property.";
  doc_fontweight = "Control the variant of the base font used for \
text rendering.";

  ## Initialize structure
  if (isfield (base, field))
    s = base.(field);
  else
    s = struct ("valid", "", "default", "", "doc", "", ...
                "printdefault", true, "category", "Miscellaneous");
  endif

  ## Base properties: Write generic documentation because it will be included
  ## in the list of each graphics object.  If a given graphics object
  ## interprets the property differently than others, then the doc will have
  ## to be rewritten for this object.
  if (strcmp (objname, "base"))
    switch (field)

      case "beingdeleted"
        s.doc  = "Property indicating that a function has initiated deletion \
of the object.  __prop__ is set to true until the object no longer exists.";
        s.category = "Creation/Deletion";

      case "busyaction"
        s.doc = "Define how Octave handles the execution of this object's \
callback properties when it is unable to interrupt another object's \
executing callback.  This is only relevant when the currently executing \
callback object has its @code{interruptible} property set to \
\@qcode{\"off\"}.  The __prop__ property of the interrupting callback object \
indicates whether the interrupting callback is queued (@qcode{\"queue\"} \
(default)) or discarded (@qcode{\"cancel\"}).\n\
@xref{Callbacks, , @w{Callbacks section}}.";
        s.category = "Callback Execution";

      case "buttondownfcn"
        s.doc = "__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Mouse Interaction";

      case "children"
        s.doc = "Graphics handles of the __objname__'s children.";
        s.valid = "vector of graphics handles";
        s.category = "Parent/Children";

      case "clipping"
        s.doc = "If __prop__ is @qcode{\"on\"}, the __objname__ is \
clipped in its parent axes limits.";
        s.category = "Display";

      case "contextmenu"
        s.doc = "Graphics handle of the uicontextmenu object that is \
currently associated to this __objname__ object.";
        s.valid = valid_handle;
        s.category = "Mouse Interaction";

      case "createfcn"
        s.doc = "Callback function executed immediately after __objname__ \
has been created.  Function is set by using default property on root object, \
e.g., @code{set (groot, \"default__objname__createfcn\", \
'disp (\"__objname__ created!\")')}.\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Creation/Deletion";

      case "deletefcn"
        s.doc = "Callback function executed immediately before __objname__ \
is deleted.\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Creation/Deletion";

      case "handlevisibility"
        s.doc = "If __prop__ is @qcode{\"off\"}, the __objname__'s \
handle is not visible in its parent's @qcode{\"children\"} property.";
        s.category = "Parent/Children";

      case "hittest"
        s.doc = "Specify whether __objname__ processes mouse events \
or passes them to ancestors of the object.  When enabled, the object may \
respond to mouse clicks by evaluating the @qcode{\"buttondownfcn\"}, showing \
the uicontextmenu, and eventually becoming the root \
@qcode{\"currentobject\"}.  This property is only relevant when the object \
can accept mouse clicks which is determined by the @qcode{\"pickableparts\"} \
property.  @xref{XREF__objname__pickableparts, , @w{pickableparts property}}.";
        s.category = "Mouse Interaction";

      case "interruptible"
        s.doc = "Specify whether this object's callback functions may be \
interrupted by other callbacks.  By default __prop__ is @qcode{\"on\"} \
and callbacks that make use of @code{drawnow}, @code{figure}, @code{waitfor}, \
@code{getframe} or @code{pause} functions are eventually interrupted.\n\
@xref{Callbacks, , @w{Callbacks section}}.";
        s.category = "Callback Execution";

      case "parent"
        s.doc = "Handle of the parent graphics object.";
        s.valid = valid_handle;
        s.category = "Parent/Children";

      case "pickableparts"
        s.doc = "Specify whether __objname__ will accept mouse clicks.  \
By default, __prop__ is @qcode{\"visible\"} and only visible parts of the \
__objname__ or its children may react to mouse clicks.  When __prop__ is \
@qcode{\"all\"} both visible and invisible parts (or children) may react to \
mouse clicks.  When __prop__ is @qcode{\"none\"} mouse clicks on the object \
are ignored and transmitted to any objects underneath this one.  When an \
object is configured to accept mouse clicks the @qcode{\"hittest\"} property \
will determine how they are processed.  \
@xref{XREF__objname__hittest, , @w{hittest property}}.";
        s.category = "Mouse Interaction";

      case "selected"
        s.doc = "Property indicates whether this __objname__ is selected.";
        s.category = "Mouse Interaction";

      case "selectionhighlight"
        s.doc = "If __prop__ is @qcode{\"on\"}, then the __objname__'s \
selection state is visually highlighted.";
        s.category = "Mouse Interaction";

      case "tag"
        s.doc = "A user-defined string to label the graphics object.";
        s.valid = valid_string;
        s.category = "Object Identification";

      case "type"
        s.doc = "Class name of the graphics object.  __prop__ is \
always @qcode{\"__objname__\"}.";
        s.valid = valid_string;
        s.printdefault = false;
        s.category = "Object Identification";

      case "userdata"
        s.doc = "User-defined data to associate with the graphics object.";
        s.valid = "Any Octave data";
        s.category = "Object Identification";

      case "visible"
        s.doc = "If __prop__ is @qcode{\"off\"}, the __objname__ is \
not rendered on screen.";
        s.category = "Display";
    endswitch

  ## Root properties:
  elseif (strcmp (objname, "root"))
    switch (field)
      ## Overridden shared properties
      case {"beingdeleted", "busyaction", "buttondownfcn", ...
            "clipping", "createfcn", "deletefcn", ...
            "hittest", "interruptible", "pickableparts", "selected", ...
            "selectionhighlight", "uicontextmenu", "visible"}
        s.doc = doc_unused;
        s.category = "Unused";

      case "handlevisibility"
        s.doc = "The root object handle is always visible.  Changing this \
setting to @qcode{\"callback\"} or @qcode{\"off\"} has no effect.";
        ## Use base category.

      case "parent"
        s.doc = "Root object has no parent graphics object.  __prop__ \
is always empty.";
        ## Use base category.

      ## Specific properties
      case "callbackobject"
        s.doc = "Graphics handle of the current object whose callback is \
executing.";
        s.valid = valid_handle;
        s.category = "Callback Execution";

      case "commandwindowsize"
        s.doc = "The number of columns and rows displayed in a newly created \
command window.";
        s.valid = valid_2elvec;
        s.category = "Command Window Display";

      case "currentfigure"
        s.doc = "Graphics handle of the current figure.";
        s.valid = valid_handle;
        s.category = "Object Identification";

      case "diary"
        s.doc = "If __prop__ is @qcode{\"on\"}, the Octave command window \
session is saved to file.  @xref{XREFrootdiaryfile, , @w{diaryfile property}}.";
        s.category = "Command Logging";

      case "diaryfile"
        s.doc = "The name of the diary file.  \
@xref{XREFdiary, , @w{diary function}}.";
        s.valid = valid_string;
        s.category = "Command Logging";

      case "echo"
        s.doc = "Control whether Octave displays commands executed from \
scripts.  @xref{XREFecho, , @w{echo function}}.";
        s.category = "Command Window Display";

      case "fixedwidthfontname"
        s.doc = "Name of the fixed-width font that will be used for \
graphics objects when the @qcode{\"fontname\"} property is set to \
@qcode{\"FixedWidth\"}.";
        s.valid = valid_string;
        s.category = "Command Window Display";

      case "format"
        s.doc = "This property is a wrapper around the @code{format} function.\
  @xref{XREFformat, , @w{format function}}.";
        s.category = "Command Window Display";

      case "formatspacing"
        s.doc = "This property is a wrapper around the @code{format} function.\
  @xref{XREFformat, , @w{format function}}.";
        s.category = "Command Window Display";

      case "monitorpositions"
        s.doc = "Reports the width and height of connected monitors.  Note: \
Octave only partially implements __prop__.  Only information about the primary \
monitor is stored in __prop__ which is the same information stored in the \
@ref{XREFrootscreensize, , @w{@qcode{\"screensize\"} property}}.";
        s.printdefault = false;
        s.valid = valid_4elvec;
        s.category = "Screen Information";

      case "pointerlocation"
        s.doc = [sprintf(doc_notimpl, "Global pointer location \
        tracking"), "  __prop__ for __objname__ objects will always be \
        [0 0]."];
        s.valid = valid_2elvec;
        s.category = "Pointer Information";

      case "pointerwindow"
        s.doc = [sprintf(doc_notimpl, "Pointer window tracking"), ...
        "  __prop__ value for __objname__ objects will always be 0."];
        s.valid = valid_handle;
        s.category = "Pointer Information";

      case "screendepth"
        s.doc = "Color depth in bits per pixel of the display.";
        s.valid = "double";
        s.printdefault = false;
        s.category = "Screen Information";

      case "screenpixelsperinch"
        s.doc = "The screen resolution of the primary display in units of \
pixels per inch.";
        s.valid = "double";
        s.printdefault = false;
        s.category = "Screen Information";

      case "screensize"
        s.doc = "Size of the primary display represented as the four-element \
vector [left, bottom, width, height].";
        s.valid = valid_4elvec;
        s.printdefault = false;
        s.category = "Screen Information";

      case "showhiddenhandles"
        s.doc = "If __prop__ is @qcode{\"on\"}, all graphics objects handles \
are visible in their parents' children list, regardless of the value of their \
@code{handlevisibility} property.";
        s.category = "Parent/Children";

      case "units"
        s.doc = "The unit type used for the \
@ref{XREFrootmonitorpositions, , @qcode{\"monitorpositions\"}}, \
@ref{XREFrootpointerlocation, , @qcode{\"pointerlocation\"}}, and \
@ref{XREFrootscreensize, , @qcode{\"screensize\"}} properties.";
        s.category = "Screen Information";

    endswitch

  ## Figure properties
  elseif (strcmp (objname, "figure"))
    switch (field)
      ## Overridden shared properties
      case {"clipping", "pickableparts"}
        s.doc = doc_unused;
        s.category = "Unused";

      ## Specific properties
      case "alphamap"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Appearance";

      case "closerequestfcn"
        s.doc = "Function that is executed when a figure is deleted.  \
@xref{XREFclosereq, , closereq function}.\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Creation/Deletion";

      case "color"
        s.doc = "Color of the figure background.  \
@xref{Colors, , colorspec}.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "colormap"
        s.doc = "A matrix containing the RGB color map for the current axes.";
        s.valid = "N-by-3 matrix";
        s.category = "Appearance";

      case "currentaxes"
        s.doc = "Handle to the graphics object of the current axes.";
        s.valid = valid_handle;
        s.category = "Object Identification";

      case "currentcharacter"
        s.doc = sprintf (doc_notimpl, "Tracking of the last key pressed");
        s.category = "Object Identification";

      case "currentobject"
        s.doc = "Handle to the most recently active graphics object in the \
figure.";
        s.valid = valid_handle;
        s.category = "Object Identification";

      case "currentpoint"
        s.doc = "A 1-by-2 vector which holds the coordinates of the point \
over which the mouse pointer was when a mouse event occurred.  The X and Y \
coordinates are in units defined by the figure's @code{units} property \
and their origin is the lower left corner of the plotting area.\n\
\n\
Events which set @code{currentpoint} are\n\
@table @asis\n\
@item A mouse button was pressed\n\
always\n\
@item A mouse button was released\n\
only if the figure's callback @code{windowbuttonupfcn} is defined\n\
@item The pointer was moved while pressing the mouse button (drag)\n\
only if the figure's callback @code{windowbuttonmotionfcn} is defined\n\
@end table";
        s.valid = valid_2elvec;
        s.category = "Mouse Interaction";

      case "dockcontrols"
        s.doc = sprintf (doc_notimpl, "Interactive figure docking");
        s.category = "Object Position";

      case "filename"
        s.doc = "The filename used when saving the plot figure.";
        s.valid = valid_string;
        s.category = "Printing/Saving";

      case "graphicssmoothing"
        s.doc = "Use smoothing techniques to reduce the appearance of jagged \
lines.";
        s.category = "Appearance";

      case "innerposition"
        s.doc = "The @qcode{\"innerposition\"} property is the same as the \
@ref{XREFfigureposition, , @w{@qcode{\"position\"} property}}.";
        s.valid = valid_4elvec;
        s.category = "Object Position";

      case "integerhandle"
        s.doc = "Assign the next lowest unused integer as the Figure number.";
        s.category = "Object Identification";

      case "inverthardcopy"
        s.doc = "Replace the figure and axes background color with white when \
printing.";
        s.category = "Printing/Saving";

      case "keypressfcn"
        s.doc = "Callback function executed when a keystroke event \
happens while the figure has focus.  The actual key that was pressed \
can be retrieved using the second argument 'evt' of the function.\
\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Keyboard Interaction";

      case "keyreleasefcn"
        s.doc = "With @code{keypressfcn}, the keyboard callback functions.  \
These callback functions are called when a key is pressed/released \
respectively.  The functions are called with two input arguments.  The first \
argument holds the handle of the calling figure.  The second argument holds \
an event structure which has the following members:\n\
@table @code\n\
@item Character:\n\
The ASCII value of the key\n\
@item Key:\n\
Lowercase value of the key\n\
@item Modifier:\n\
A cell array containing strings representing the modifiers pressed with the \
key.\n\
@end table\
\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Keyboard Interaction";

      case "menubar"
        s.doc = "Control the display of the figure menu bar at the top \
of the figure.";
        s.category = "Mouse Interaction";

      case "name"
        s.doc = "Name to be displayed in the figure title bar.  The name is \
displayed to the right of any title determined by the @code{numbertitle} \
property.";
        s.valid = valid_string;
        s.category = "Appearance";

      case "nextplot"
        s.doc = "__prop__ is used by high level plotting functions to \
decide what to do with axes already present in the figure.  \
@xref{XREFnewplot, , @w{newplot function}}.";
        s.category = "Object Identification";

      case "number"
        s.doc = "Number of the current __objname__.";
        s.valid = "double";
        s.category = "Object Identification";

      case "numbertitle"
        s.doc = "Display @qcode{\"Figure\"} followed by the numerical figure \
handle value in the figure title bar.";
        s.category = "Appearance";

      case "outerposition"
        s.doc = "Specify the position and size of the figure including \
the top menubar and the bottom status bar.  \
The four elements of the vector are the coordinates of the lower left corner \
and width and height of the figure.  \
@xref{XREFfigureunits, , @w{units property}}.";
        s.valid = valid_4elvec;
        s.category = "Object Position";

      case "paperorientation"
        s.doc = "The value for the @code{papersize}, and @code{paperposition} \
properties depends upon __prop__.  The horizontal and vertical values for \
@code{papersize} and @code{paperposition} reverse order \
when __prop__ is switched between @code{\"portrait\"} and \
@code{\"landscape\"}.";
        s.category = "Printing/Saving";

      case "paperposition"
        s.doc = "Vector @code{[left bottom width height]} defining the \
position and size of the figure (in @code{paperunits} units) on the printed \
page.  The position @code{[left bottom]} defines the lower left corner of the \
figure on the page, and the size is defined by @code{[width height]}.  For \
output formats not implicitly rendered on paper, @code{width} and \
@code{height} define the size of the image and the position information is \
ignored.  __modemsg__";
        s.valid = valid_4elvec;
        s.category = "Printing/Saving";

      case "paperpositionmode"
        s.doc = "If __prop__ is set to @qcode{\"auto\"}, the \
@code{paperposition} property is automatically computed: the printed \
figure will have the same size as the on-screen figure and will be centered \
on the output page.  Setting the __prop__ to @code{\"auto\"} does not modify \
the value of the @code{paperposition} property.";
        s.category = "Printing/Saving";

      case "papersize"
        s.doc = "Vector @code{[width height]} defining the size of the \
paper for printing.  Setting the __prop__ property to a value, not associated \
with one of the defined @code{papertypes} and consistent with the setting for \
@code{paperorientation}, forces the @code{papertype} property to the value \
@qcode{\"<custom>\"}.  If __prop__ is set to a value associated with a \
supported @code{papertype} and consistent with the @code{paperorientation}, \
the @code{papertype} value is modified to the associated value.";
        s.valid = valid_2elvec;
        s.category = "Printing/Saving";

      case "papertype"
        s.doc = "Name of the paper used for printed output.  \
Setting __prop__ also changes @code{papersize}, while maintaining consistency \
with the @code{paperorientation} property.";
        s.category = "Printing/Saving";

      case "paperunits"
        s.doc = "The unit used to compute the @code{paperposition} property.  \
The conversion from physical units (e.g., @code{\"inches\"}) is dependent on \
the @code{screenpixelsperinch} property of the root object.";
        s.category = "Printing/Saving";

      case "pointer"
        s.doc = "Name of the mouse pointer shape associated with the canvas \
of the figure.  When __prop__ is @qcode{\"custom\"}, the shape is determined \
by the @code{pointershapecdata} property.\n\n\
__prop__ has no effect when the figure is in zoom, pan, or rotate mode.  \
In this case, Octave automatically uses a pointer shape appropriate \
to the mode.";
        s.category = "Mouse Interaction";

      case "pointershapecdata"
        s.doc = "m-by-m matrix defining a custom pointer.  Each \
element defines a pixel with the element (1,1) representing the \
top-left pixel.  A value of 1 is colored black, a value of 2 is colored white, \
and all other values are rendered as transparent.";
        s.valid = "16-by-16 or 32-by-32 Matrix";
        s.category = "Mouse Interaction";

      case "pointershapehotspot"
        s.doc = "For custom pointers only __prop__ defines the row and \
column of the pixel in @code{pointershapecdata} that is used as the pointer \
location.";
        s.valid = valid_2elvec;
        s.category = "Mouse Interaction";

      case "position"
        s.doc = "Specify the position and size of the figure canvas.  \
The four elements of the vector are the coordinates of the lower left corner \
and width and height of the figure.  \
@xref{XREFfigureunits, , @w{units property}}.";
        s.valid = valid_4elvec;
        s.category = "Object Position";

      case "renderer"
        s.doc = "Rendering engine used for printing when @code{renderermode} \
is @qcode{\"manual\"}.  __modemsg__";
        s.category = "Printing/Saving";

      case "renderermode"
        s.doc = "Control whether the rendering engine used for printing is \
chosen automatically or specified by the @code{renderer} property.  \
@xref{XREFprint, , @w{print function}}.";
        s.category = "Printing/Saving";

      case "resize"
        s.doc = "Control whether the figure can be resized by dragging the \
window borders and corners using a mouse.  When __prop__ is @qcode{\"off\"} \
mouse interactions are disabled but the figure can still be resized by \
changing its @qcode{\"position\"} property.";
        s.category = "Mouse Interaction";

      case "resizefcn"
        s.doc = "__prop__ is deprecated.  Use @code{sizechangedfcn} instead.";
        s.valid = valid_fcn;
        s.category = "Mouse Interaction";

      case "selectiontype"
        s.doc = "Selection type of the latest mouse click.\n\n\
__prop__ may take different values depending on the combination of mouse \
button and keyboard modifier that were used:\n\
@table @code\n\
@item normal:\n\
Left-click.\n\
@item alt:\n\
Right-click or Ctrl+Left-click.\n\
@item extend:\n\
Shift+Left-click, Middle click, or combined Left-click and Right-click.\n\
@item open:\n\
Double Left-click.\n\
@end table";
        s.category = "Mouse Interaction";

      case "sizechangedfcn"
        s.doc = "Callback triggered when the figure window size is changed.\
\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Mouse Interaction";

      case "toolbar"
        s.doc = "Control the display of the toolbar (along the bottom of the \
menubar) and the status bar.  When set to @qcode{\"auto\"}, the display is \
based on the value of the @ref{XREFfiguremenubar,,@qcode{\"menubar\"}} \
property.";
        s.category = "Mouse Interaction";

      case "units"
        s.doc = "The unit used to compute the \
@ref{XREFfigureposition,,position} and \
@ref{XREFfigureouterposition,,outerposition} properties.";
        s.category = "Object Position";

      case "windowbuttondownfcn"
        s.doc = "@xref{XREFfigurewindowbuttonupfcn, , \
@w{windowbuttonupfcn property}}.";
        s.valid = valid_fcn;
        s.category = "Mouse Interaction";

      case "windowbuttonmotionfcn"
        s.doc = "@xref{XREFfigurewindowbuttonupfcn, , \
@w{windowbuttonupfcn property}}.";
        s.valid = valid_fcn;
        s.category = "Mouse Interaction";

      case "windowbuttonupfcn"
        s.doc = "With @code{windowbuttondownfcn} and \
@code{windowbuttonmotionfcn}, the mouse callback functions.  These \
callback functions are called when a mouse button is pressed, dragged, or \
released respectively.  When these callback functions are executed, the \
@code{currentpoint} property holds the current coordinates of the cursor.\
\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Mouse Interaction";

      case "windowkeypressfcn"
        s.doc = "Function that is executed when a key is pressed and \
the figure has focus.\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Keyboard Interaction";

      case "windowkeyreleasefcn"
        s.doc = "Function that is executed when a key is released and \
the figure has focus.\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Keyboard Interaction";

      case "windowscrollwheelfcn"
        s.doc = "Function that is executed when a user manipulates \
the mouse wheel over this figure.  \
The function is called with two input arguments.  The first \
argument holds the handle of the calling figure.  The second argument holds \
an event structure which has the following members:\n\
@table @code\n\
@item VerticalScrollCount:\n\
The number of wheel steps, typically 1 when scrolling down and -1 when \
scrolling up.\n\
@item VerticalScrollAmount:\n\
The number of lines a wheel step should scroll.  This value is always 3.\n\
@item EventName:\n\
The event name which is @qcode{\"WindowScrollWheel\"}.\n\
@end table\
\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Mouse Interaction";

      case "windowstate"
        s.doc = sprintf (doc_notimpl, "Window state adjustment");
        s.category = "Display";

      case "windowstyle"
        s.doc = "The window style of a figure.  One of the following values:\n\
@table @code\n\
@item normal\n\
The window may be unselected and other windows may be shown in front of the \
window.\n\
@item modal\n\
The window will stay on top of all normal figures until it is dismissed.\n\
@item docked\n\
Unimplemented.\n\
@end table\n\
\n\
Changing modes of a visible figure may cause the figure to close and reopen.";
        s.category = "Display";

    endswitch

  ## Axes properties
  elseif (strcmp (objname, "axes") || strcmp (objname, "legend"))
    switch (field)
      ## Overridden shared properties
      case "clipping"
        s.doc = sprintf (doc_notimpl, "Axes-children clipping control");
        ## Use base category.

      ## Specific properties
      case "alim"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Color and Transparency";

      case "alimmode"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Color and Transparency";

      case "alphamap"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Color and Transparency";

      case "alphascale"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Color and Transparency";

      case "ambientlightcolor"
        s.doc = sprintf (doc_notimpl, "Uniform background axes lighting");
        s.category = "Color and Transparency";

      case "box"
        s.doc = "Control whether the __objname__ has a surrounding box.";
        if (strcmp (objname, "legend"))
          s.category = "Legend Appearance";
        else
          s.category = "Axes Box Appearance";
        endif

      case "boxstyle"
        s.doc = "For 3-D axes, control whether the @qcode{\"full\"} \
box is drawn or only the 3 @qcode{\"back\"} axes.";
        s.category = "Axes Box Appearance";

      case "cameraposition"
        s.doc = "Coordinates of the camera position viewing the \
__objname__.  __modemsg__";
        s.valid = valid_3elvec;
        s.category = "Camera and View Controls";

      case "camerapositionmode"
        s.doc = "Current state of the camera position property, whether \
automatically set according to the @ref{XREFview, , view function}, or \
manually set with the \
@ref{XREFaxescameraposition, , @w{@qcode{\"cameraposition\"} property}}.";
        s.category = "Camera and View Controls";

      case "cameratarget"
        s.doc = "Coordinates of the point at which the viewing camera is \
aimed.  __modemsg__";
        s.valid = valid_3elvec;
        s.category = "Camera and View Controls";

      case "cameratargetmode"
        s.doc = "Current state of camera target property, either manually \
set with the \
@ref{XREFaxescameratarget, , @w{@qcode{\"cameratarget\"} property}} or \
automatically positioned at the center of the axes plot area.";
        s.category = "Camera and View Controls";

      case "cameraupvector"
        s.doc = "A 3-element vector defining the upward direction of the \
current view.  Note that the default is [0 1 0] for 2-D plots and [0 0 1] for \
3-D plots.  __modemsg__";
        s.valid = valid_3elvec;
        s.category = "Camera and View Controls";

      case "cameraupvectormode"
        s.doc = "Current state of camera up vector property, set to manual \
when the \
@ref{XREFaxescameraupvector, , @w{@qcode{\"cameraupvector\"} property}} is \
used to change the vector from the 2-D or 3-D default values.";
        s.category = "Camera and View Controls";

      case "cameraviewangle"
        s.doc = "The camera's field of view defined as an angle between 0 \
and 180 degrees.  __modemsg__";
        s.valid = "scalar";
        s.category = "Camera and View Controls";

      case "cameraviewanglemode"
        s.doc = "Current state of the camera view angle property, either \
manually set with the \
@ref{XREFaxescameraviewangle, , @w{@qcode{\"cameraviewangle\"} property}} \
or automatically set by Octave to include all visible objects.";
        s.category = "Camera and View Controls";

      case "clim"
        s.doc = "Define limits for the color axis of __objname__ \
children that have the @qcode{cdata} property.  \
__modemsg__";
        s.valid = valid_2elvec;
        s.category = "Color and Transparency";

      case "climmode"
        s.doc = "Current state of the color limit mode, either \
manually set by the \
@ref{XREFaxesclim, , @w{@qcode{\"clim\"} property}} or automatically set by \
Octave to the minimum and maximum @qcode{cdata} values of __objname__'s \
children.";
        s.category = "Color and Transparency";

      case "clippingstyle"
        s.doc = sprintf (doc_notimpl, "Axes-children clipping control");
        s.category = "Display";

      case "color"
        s.doc = "Color of the __objname__ background.  \
@xref{Colors, , colorspec}.";
        s.valid = valid_color;
        if (strcmp (objname, "legend"))
          s.category = "Legend Appearance";
        else
          s.category = "Axes Box Appearance";
        endif

      case "colormap"
        s.doc = "A matrix containing the RGB color map for this __objname__ \
object.";
        s.valid = "N-by-3 matrix";
        s.category = "Color and Transparency";

      case "colororder"
        s.doc = "RGB values used by plot function for child object coloring.";
        s.valid = "N-by-3 RGB matrix";
        s.category = "Automatic Child Properties";

      case "colororderindex"
        s.doc = "Index of the next color from the \
@ref{XREFaxescolororder, , @w{@qcode{\"colororder\"} property}} to be used \
by Axes-child objects.";
        s.valid = "integer";
        s.category = "Automatic Child Properties";

      case "colorscale"
        s.doc = sprintf (doc_notimpl, "Automatic linear/log color scaling");
        s.category = "Color and Transparency";

      case "currentpoint"
        s.doc = "Matrix @code{[xf, yf, zf; xb, yb, zb]} which holds the \
coordinates (in axes data units) of the point over which the mouse pointer \
was when the mouse button was pressed.  If a mouse callback function is \
defined, @code{currentpoint} holds the pointer coordinates at the time \
the mouse button was pressed.  For 3-D plots, the first row of the returned \
matrix specifies the point nearest to the current camera position and the \
second row the furthest point.  The two points forms a line which is \
perpendicular to the screen.";
        s.valid = "2-by-3 matrix";
        s.category = "Mouse Interaction";

      case "dataaspectratio"
        s.doc = "Specify the relative height and width of the data \
displayed in the axes.  Setting @code{dataaspectratio} to \
@w{@code{[1, 2]}} causes the length of one unit as displayed on the x-axis \
to be the same as the length of 2 units on the y-axis.  \
@xref{XREFdaspect, , daspect function}.  __modemsg__";
        s.valid = valid_3elvec;
        s.category = "Axes Box Appearance";

      case "dataaspectratiomode"
        s.doc = "Current state of the data aspect ratio mode, either \
manually set by the \
@ref{XREFaxesdataaspectratio, , @w{@qcode{\"dataaspectratio\"} property}} or \
automatically set by Octave in combination with other display properties to \
fit the data in the current view.";
        s.category = "Axes Box Appearance";

      case "fontangle"
        s.doc = doc_fontangle;
        s.category = "Text Appearance";

      case "fontname"
        s.doc = doc_fontname;
        s.valid = valid_string;
        s.category = "Text Appearance";

      case "fontsize"
        s.doc = [doc_fontsize, "  __modemsg__"];
        s.valid = "scalar";
        s.category = "Text Appearance";

      case "fontsizemode"
        s.doc = "Current state of the fontsize mode, either manually set by \
the @ref{XREFaxesfontsize, , @w{@qcode{\"fontsize\"} property}} or \
automatically set by Octave to maintain readability.";
        s.category = "Text Appearance";

      case "fontsmoothing"
        s.doc = "Control whether any text associated with __objname__ is \
anti-aliased.";
        s.category = "Text Appearance";

      case "fontunits"
        s.doc = doc_fontunits;
        s.category = "Text Appearance";

      case "fontweight"
        s.doc = doc_fontweight;
        s.category = "Text Appearance";

      case "gridalpha"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Axes Grid Appearance";

      case "gridalphamode"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Axes Grid Appearance";

      case "gridcolor"
        s.doc = "Color of the major grid lines.  \
@xref{Colors, , colorspec}.  __modemsg__";
        s.valid = packopt ({markdef(valid_color), "@qcode{\"none\"}"});
        s.category = "Axes Grid Appearance";

      case "gridcolormode"
        s.doc = "Current state of the gridcolor mode, either manually set by \
the @ref{XREFaxesgridcolor, , @w{@qcode{\"gridcolor\"} property}} or \
automatically set by Octave to the default value.";
        s.category = "Axes Grid Appearance";

      case "gridlinestyle"
        s.doc = "@xref{Line Styles}.";
        s.category = "Axes Grid Appearance";

      case "innerposition"
        s.doc = "The @qcode{\"innerposition\"} property is the same as the \
@ref{XREFaxesposition, , @w{@qcode{\"position\"} property}}.";
        s.valid = valid_4elvec;
        s.category = "Axes Grid Appearance";

      case "interactions"
        s.doc = sprintf (doc_notimpl, "Interaction objects");
        s.category = "Callback Execution";

      case "labelfontsizemultiplier"
        s.doc = "Ratio between the x/y/zlabel fontsize and the tick label \
fontsize.";
        s.category = "Text Appearance";

      case "layer"
        s.doc = "Control whether the axes is drawn below child graphics \
objects (ticks, labels, etc.@: covered by plotted objects) or above.";
        s.category = "Axes Box Appearance";

      case "layout"
        s.doc = sprintf (doc_notimpl, "Tiled and gridded chart layout");
        s.category = "Axes Box Appearance";

      case "legend"
        s.doc = [sprintf(doc_notimpl, "Legend property control"), "  Use \
the @ref{XREFlegend, , legend function} to set legend properties."];
        s.category = "Text Appearance";

      case "linestyleorder"
        s.doc = "List of linestyles to be used in order by axes child \
objects, specified as a cell array of line specification strings.  Note that \
the linestyle is only incremented after cycling through the full \
@ref{XREFaxescolororder, , @qcode{\"colororder\"}} list.  \
@xref{Line Styles}.";
        s.category = "Automatic Child Properties";

      case "linestyleorderindex"
        s.doc = "Index of the next linestyle from the \
@ref{XREFaxeslinestyleorder, , @w{@qcode{\"linestyleorder\"} property}} to \
be used by Axes-child objects.";
        s.valid = "whole number scalar";
        s.category = "Automatic Child Properties";

      case "linewidth"
        s.doc = "Width of the main axes lines.";
        s.valid = "scalar";
        s.category = "Axes Box Appearance";

      case "minorgridalpha"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Axes Grid Appearance";

      case "minorgridalphamode"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Axes Grid Appearance";

      case "minorgridcolor"
        s.doc = "Color of the minor grid lines.  \
@xref{Colors, , colorspec}.  __modemsg__";
        s.valid = packopt ({markdef(valid_color), "@qcode{\"none\"}"});
        s.category = "Axes Grid Appearance";

      case "minorgridcolormode"
        s.doc = "Current state of the minorgridcolor mode, either manually \
set by the \
@ref{XREFaxesminorgridcolor, , @w{@qcode{\"minorgridcolor\"} property}} or \
automatically set by Octave to the default value.";
        s.category = "Axes Grid Appearance";

      case "minorgridlinestyle"
        s.doc = "@xref{Line Styles}.";
        s.category = "Axes Grid Appearance";

      case "mousewheelzoom"
        s.doc = "Fraction of axes limits to zoom for each wheel movement.";
        s.valid = "scalar in the range (0, 1)";
        s.category = "Mouse Interaction";

      case "nextplot"
        s.doc = "__prop__ is used by high level plotting functions to \
decide what to do with graphics objects already present in the axes.  \
@xref{XREFnewplot, , @w{newplot function}}.  The state of __prop__ \
is typically controlled using the @code{hold} function.  \
@xref{XREFhold, , @w{hold function}}.";
        s.category = "Object Identification";

      case "nextseriesindex"
        s.doc = "Current index value into the \
@ref{XREFaxescolororder, , \"colororder\"} and \
@ref{XREFaxeslinestyleorder, , \"linestyleorder\"} \
properties, indicating the item that will be used by the next child object";
        s.valid = "whole number scalar";
        s.category = "Automatic Child Properties";

      case "outerposition"
        s.doc = "Specify the position of the plot including titles, \
axes, and legend.  The four elements of the vector are the \
coordinates of the lower left corner and width and height of the \
plot, in units normalized to the width and height of the plot \
window.  For example, @code{[0.2, 0.3, 0.4, 0.5]} sets the lower \
left corner of the axes at @math{(0.2, 0.3)} and the width and \
height to be 0.4 and 0.5 respectively.  \
@xref{XREFaxesposition, , @w{position property}}.";
        s.valid = valid_4elvec;
        s.category = "Object Position";

      case "plotboxaspectratio"
        s.doc = "@xref{XREFpbaspect, , pbaspect function}.  __modemsg__";
        s.category = "Object Position";

      case "plotboxaspectratiomode"
        s.doc = "Current state of the plot box aspect ratio mode, either \
manually set by the \
@ref{XREFaxesdataaspectratio, , @w{@qcode{\"dataaspectratio\"} property}} or \
automatically set by Octave in combination with other display properties to \
fit the data in the current view.";
        s.category = "Object Position";

      case "position"
        if (strcmp (objname, "legend"))
          s.doc = "Specify the position of the legend excluding its title.  \
The four elements of the vector are the coordinates of the lower left corner \
and width and height of the legend.  Changing this property also \
switches the @qcode{\"location\"} to @qcode{\"none\"}.";
          s.printdefault = false;

        else
          s.doc = "Specify the position of the plot excluding titles, \
axes, and legend.  The four elements of the vector are the \
coordinates of the lower left corner and width and height of the \
plot, in units normalized to the width and height of the plot \
window.  For example, @code{[0.2, 0.3, 0.4, 0.5]} sets the lower \
left corner of the axes at @math{(0.2, 0.3)} and the width and \
height to be 0.4 and 0.5 respectively.  \
@xref{XREFaxesouterposition, , @w{outerposition property}}.";
        endif
        s.valid = valid_4elvec;
        s.category = "Object Position";

      case "positionconstraint"
        s.doc = "Specify which of @qcode{\"innerposition\"} or \
@qcode{\"outerposition\"} properties takes precedence when axes \
annotations extent changes.  \
@xref{XREFaxesinnerposition, , @w{@qcode{\"innerposition\"} property}}, \
and @ref{XREFaxesouterposition, , @w{@qcode{\"outerposition\"} property}}.";
        s.category = "Object Position";

      case "projection"
        s.doc = sprintf (doc_notimpl, "Orthographic/perspective projection \
adjustment");
        s.category = "Camera and View Controls";

      case "sortmethod"
        s.doc = sprintf (doc_notimpl, "Child display order control");
        s.category = "Parent/Children";

      case "tickdir"
        s.doc = "Control whether axes tick marks project @qcode{\"in\"} to \
the plot box or @qcode{\"out\"}.  The value @qcode{\"both\"} will draw tick \
marks both in and out.  The value @qcode{\"none\"} means no tick marks will \
be drawn, although tick labels will still be rendered.  __modemsg__  Note \
that the default is @qcode{\"in\"} for 2-D and @qcode{\"out\"} for 3-D \
plots.";
        s.category = "Axes Box Appearance";

      case "tickdirmode"
        s.doc = "Current state of the tickdir mode, either manually set by \
the @ref{XREFaxestickdir, , @w{@qcode{\"tickdir\"} property}} or \
automatically set to the default for the current view.";
        s.category = "Axes Box Appearance";

      case "ticklabelinterpreter"
        s.doc = "Control the way x/y/zticklabel properties are interpreted.\n\
@xref{Use of the \"interpreter\" Property, , @w{Use of the @qcode{\"interpreter\"} Property}}.";
        s.category = "Text Appearance";

      case "ticklength"
        s.doc = "Two-element vector @code{[2Dlen 3Dlen]} specifying the \
length of the tickmarks relative to the longest visible axis.";
        s.valid = valid_2elvec;
        s.category = "Axes Box Appearance";

      case "tightinset"
        s.doc = "Size of the @code{[left bottom right top]} margins around \
the axes that enclose labels and title annotations.";
        s.valid = valid_4elvec;
        s.printdefault = false;
        s.category = "Text Appearance";

      case "title"
        s.doc = "Graphics handle of the title text object.";
        s.valid = valid_handle;
        s.category = "Object Identification";

      case "titlefontsizemultiplier"
        s.doc = "Ratio between the title fontsize and the tick label \
fontsize.";
        s.valid = "positive scalar";
        s.category = "Text Appearance";

      case "titlefontweight"
        s.doc = "Control variant of base font used for the axes title.";
        s.category = "Text Appearance";

      case "toolbar"
        s.doc = [sprintf(doc_notimpl, "AxesToolbar objects")];
        s.category = "Axes Box Appearance";

      case "units"
        if (strcmp (objname, "legend"))
          s.doc = "Units used to interpret the @qcode{\"position\"} \
property.";
        else
          s.doc = "Units used to interpret the @qcode{\"position\"}, \
@qcode{\"outerposition\"}, and @qcode{\"tightinset\"} properties.";
        endif
        s.category = "Object Position";

      case "view"
        s.doc = "Two-element vector @code{[azimuth elevation]} specifying \
the viewpoint for three-dimensional plots.";
        s.valid = valid_2elvec;
        s.category = "Camera and View Controls";

      case "xaxis"
        s.doc = [sprintf(doc_notimpl, "Axes Ruler objects")];
        s.category = "Axes Box Appearance";

      case "xaxislocation"
        s.doc = "Control the x-axis location.";
        s.category = "Axes Box Appearance";

      case "xcolor"
        s.doc = "Color of the x-axis.  @xref{Colors, , colorspec}.  \
__modemsg__";
        s.valid = packopt ({markdef(valid_color), "@qcode{\"none\"}"});
        s.category = "Axes Box Appearance";

      case "xcolormode"
        s.doc = "Current state of the setting determining the color that is \
applied to the x-axis grid lines.  If set to @qcode{\"auto\"} and/or the \
@ref{XREFaxesgridcolormode, , @w{@qcode{\"gridcolormode\"} property}} is set \
to @qcode{\"manual\"}, the x-axis grid color will be defined by the \
@ref{XREFaxesgridcolor, , @w{@qcode{\"gridcolor\"} property}}.  Otherwise \
the x-axis grid color will be defined by the \
@ref{XREFaxesxcolor, , @w{@qcode{\"xcolor\"} property}}.";
        s.category = "Axes Box Appearance";

      case "xdir"
        s.doc = "Direction of the x axis: @qcode{\"normal\"} is left to \
right in default 2-D and 3-D views.";
        s.category = "Axes Box Appearance";

      case "xgrid"
        s.doc = "Control whether major x grid lines are displayed.";
        s.category = "Axes Grid Appearance";

      case "xlabel"
        s.doc = "Graphics handle of the x label text object.";
        s.valid = valid_handle;
        s.category = "Text Appearance";

      case "xlim"
        s.doc = "Two-element vector @code{[xmin xmax]} specifying the limits \
for the x-axis.  __modemsg__   @xref{XREFxlim, , @w{xlim function}}.";
        s.valid = valid_2elvec;
        s.category = "Axes Box Appearance";

      case "xlimitmethod"
        s.doc = "Method used to determine the x-axis limits when the \
@code{xlimmode} property is @qcode{\"auto\"}.  The default value, \
@qcode{\"tickaligned\"} makes limits align with the closest ticks.  With \
value @qcode{\"tight\"} the limits are adjusted to enclose all the graphics \
objects in the axes, while with value @qcode{\"padded\"}, an additional \
margin of about 7%% of the data extent is added around the objects.  \
@xref{XREFaxis, , @w{axis function}}.";
        s.category = "Axes Box Appearance";

      case "xlimmode"
        s.doc = "Current state of the x-axis limit selection method, either \
manually set with the @ref{XREFaxesxlim, , @w{@qcode{\"xlim\"} property}} \
or automatically set to span the plotted data according to the \
@ref{XREFaxesxlimitmethod, , @w{@qcode{\"xlimitmethod\"} property}}.";
        s.category = "Axes Box Appearance";

      case "xminorgrid"
        s.doc = "Control whether minor x grid lines are displayed.";
        s.category = "Axes Grid Appearance";

      case "xminortick"
        s.doc = "Control whether minor x tick marks are displayed.";
        s.category = "Axes Box Appearance";

      case "xscale"
        s.doc = "Set the x-axis to a linear or logarithmic scale.";
        s.category = "Axes Grid Appearance";

      case "xtick"
        s.doc = "Position of x tick marks.  __modemsg__";
        s.valid = "vector";
        s.printdefault = false;
        s.category = "Axes Box Appearance";

      case "xticklabel"
        s.doc = "Labels of x tick marks.  __modemsg__";
        s.valid = valid_cellstring;
        s.category = "Text Appearance";

      case "xticklabelmode"
        s.doc = "Setting to determine whether the xtick labels are set \
automatically by Octave or manually using the \
@ref{XREFaxesxticklabel, , @w{@qcode{\"xticklabel\"} property}}.";
        s.category = "Text Appearance";

      case "xticklabelrotation"
        s.doc = [sprintf(doc_notimpl, "Axis label rotation")];
        s.category = "Text Appearance";

      case "xtickmode"
        s.doc = "Setting to determine whether the xtick locations and \
spacing are set automatically by Octave or manually using the \
@ref{XREFaxesxtick, , @w{@qcode{\"xtick\"} property}}.";
        s.category = "Axes Box Appearance";

      case "yaxis"
        s.doc = [sprintf(doc_notimpl, "Axes Ruler objects")];
        s.category = "Axes Box Appearance";

      case "yaxislocation"
        s.doc = "Control the y-axis location.";
        s.category = "Axes Box Appearance";

      case "ycolor"
        s.doc = "Color of the y-axis.  @xref{Colors, , colorspec}.";
        s.valid = packopt ({markdef(valid_color), "@qcode{\"none\"}"});
        s.category = "Axes Box Appearance";

      case "ycolormode"
        s.doc = "Current state of the setting determining the color that is \
applied to the y-axis grid lines.  If set to @qcode{\"auto\"} and/or the \
@ref{XREFaxesgridcolormode, , @w{@qcode{\"gridcolormode\"} property}} is set \
to @qcode{\"manual\"}, the y-axis grid color will be defined by the \
@ref{XREFaxesgridcolor, , @w{@qcode{\"gridcolor\"} property}}.  Otherwise \
the y-axis grid color will be defined by the \
@ref{XREFaxesycolor, , @w{@qcode{\"ycolor\"} property}}.";
        s.category = "Axes Box Appearance";

      case "ydir"
        s.doc = "Direction of the y-axis: @qcode{\"normal\"} is bottom \
to top in 2-D and front to back in 3-D default views.";
        s.category = "Axes Box Appearance";

      case "ygrid"
        s.doc = "Control whether major y grid lines are displayed.";
        s.category = "Axes Grid Appearance";

      case "ylabel"
        s.doc = "Graphics handle of the y label text object.";
        s.valid = valid_handle;
        s.category = "Text Appearance";

      case "ylim"
        s.doc = "Two-element vector @code{[ymin ymax]} specifying the limits \
for the y-axis.  __modemsg__  @xref{XREFylim, , @w{ylim function}}.";
        s.valid = valid_2elvec;
        s.category = "Axes Box Appearance";

      case "ylimitmethod"
        s.doc = "Method used to determine the y-axis limits when the \
@code{xlimmode} property is @qcode{\"auto\"}.  The default value, \
@qcode{\"tickaligned\"} makes limits align with the closest ticks.  With \
value @qcode{\"tight\"} the limits are adjusted to enclose all the graphics \
objects in the axes, while with value @qcode{\"padded\"}, an additional \
margin of about 7%% of the data extent is added around the objects.  \
@xref{XREFaxis, , @w{axis function}}.";
        s.category = "Axes Box Appearance";

      case "ylimmode"
        s.doc = "Current state of the y-axis limit selection method, either \
manually set with the @ref{XREFaxesylim, , @w{@qcode{\"ylim\"} property}} \
or automatically set to span the plotted data according to the \
@ref{XREFaxesylimitmethod, , @w{@qcode{\"ylimitmethod\"} property}}.";
        s.category = "Axes Box Appearance";

      case "yminorgrid"
        s.doc = "Control whether minor y grid lines are displayed.";
        s.category = "Axes Grid Appearance";

      case "yminortick"
        s.doc = "Control whether minor y tick marks are displayed.";
        s.category = "Axes Grid Appearance";

      case "yscale"
        s.doc = "Set the y-axis to a linear or logarithmic scale.";
        s.category = "Axes Grid Appearance";

      case "ytick"
        s.doc = "Position of y tick marks.  __modemsg__";
        s.valid = "vector";
        s.printdefault = false;
        s.category = "Axes Box Appearance";

      case "yticklabel"
        s.doc = "Labels of y tick marks.  __modemsg__";
        s.valid = valid_cellstring;
        s.category = "Text Appearance";

      case "yticklabelmode"
        s.doc = "Setting to determine whether the ytick labels are set \
automatically by Octave or manually using the \
@ref{XREFaxesyticklabel, , @w{@qcode{\"yticklabel\"} property}}.";
        s.category = "Text Appearance";

      case "yticklabelrotation"
        s.doc = [sprintf(doc_notimpl, "Axis label rotation")];
        s.category = "Text Appearance";

      case "ytickmode"
        s.doc = "Setting to determine whether the ytick locations and \
spacing are set automatically by Octave or manually using the \
@ref{XREFaxesytick, , @w{@qcode{\"ytick\"} property}}.";
        s.category = "Axes Box Appearance";

      case "zaxis"
        s.doc = [sprintf(doc_notimpl, "Axes Ruler objects")];
        s.category = "Axes Box Appearance";

      case "zcolor"
        s.doc = "Color of the z-axis.  @xref{Colors, , colorspec}.";
        s.valid = packopt ({markdef(valid_color), "@qcode{\"none\"}"});
        s.category = "Axes Box Appearance";

      case "zcolormode"
        s.doc = "Current state of the setting determining the color that is \
applied to the z-axis grid lines.  If set to @qcode{\"auto\"} and/or the \
@ref{XREFaxesgridcolormode, , @w{@qcode{\"gridcolormode\"} property}} is set \
to @qcode{\"manual\"}, the z-axis grid color will be defined by the \
@ref{XREFaxesgridcolor, , @w{@qcode{\"gridcolor\"} property}}.  Otherwise \
the z-axis grid color will be defined by the \
@ref{XREFaxeszcolor, , @w{@qcode{\"zcolor\"} property}}.";
        s.category = "Axes Box Appearance";

      case "zdir"
        s.doc = "Direction of the y-axis: @qcode{\"normal\"} is bottom \
to top in default 3-D views.";
        s.category = "Axes Box Appearance";

      case "zgrid"
        s.doc = "Control whether major z grid lines are displayed.";
        s.category = "Axes Grid Appearance";

      case "zlabel"
        s.doc = "Graphics handle of the z label text object.";
        s.valid = valid_handle;
        s.category = "Text Appearance";

      case "zlim"
        s.doc = "Two-element vector @code{[zmin zmax]} specifying the limits \
for the z-axis.  __modemsg__  @xref{XREFzlim, , @w{zlim function}}.";
        s.valid = valid_2elvec;
        s.category = "Axes Box Appearance";

      case "zlimitmethod"
        s.doc = "Method used to determine the z-axis limits when the \
@code{xlimmode} property is @qcode{\"auto\"}.  The default value, \
@qcode{\"tickaligned\"} makes limits align with the closest ticks.  With \
value @qcode{\"tight\"} the limits are adjusted to enclose all the graphics \
objects in the axes, while with value @qcode{\"padded\"}, an additional \
margin of about 7%% of the data extent is added around the objects.  \
@xref{XREFaxis, , @w{axis function}}.";
        s.category = "Axes Box Appearance";

      case "zlimmode"
        s.doc = "Current state of the z-axis limit selection method, either \
manually set with the @ref{XREFaxeszlim, , @w{@qcode{\"zlim\"} property}} \
or automatically set to span the plotted data according to the \
@ref{XREFaxeszlimitmethod, , @w{@qcode{\"zlimitmethod\"} property}}.";
        s.category = "Axes Box Appearance";

      case "zminorgrid"
        s.doc = "Control whether minor z grid lines are displayed.";
        s.category = "Axes Grid Appearance";

      case "zminortick"
        s.doc = "Control whether minor z tick marks are displayed.";
        s.category = "Axes Box Appearance";

      case "zscale"
        s.doc = "Set the z-axis to a linear or logarithmic scale.";
        s.category = "Axes Grid Appearance";

      case "ztick"
        s.doc = "Position of z tick marks.  __modemsg__";
        s.valid = "vector";
        s.printdefault = false;
        s.category = "Axes Box Appearance";

      case "zticklabel"
        s.doc = "Labels of z tick marks.  __modemsg__";
        s.valid = valid_cellstring;
        s.category = "Text Appearance";

      case "zticklabelmode"
        s.doc = "Setting to determine whether the ztick labels are set \
automatically by Octave or manually using the \
@ref{XREFaxeszticklabel, , @w{@qcode{\"zticklabel\"} property}}.";
        s.category = "Text Appearance";

      case "zticklabelrotation"
        s.doc = [sprintf(doc_notimpl, "Axis label rotation")];
        s.category = "Text Appearance";

      case "ztickmode"
        s.doc = "Setting to determine whether the ztick locations and \
spacing are set automatically by Octave or manually using the \
@ref{XREFaxesztick, , @w{@qcode{\"ztick\"} property}}.";
        s.category = "Axes Box Appearance";

      ## Legend specific properties
      case "autoupdate"
        s.doc = "Control whether the number of legend items is updated \
automatically when objects are added to (or deleted from) the peer axes.\n\
For example:\n\
@example\n\
@group\n\
## Create a single plot with its legend.\n\
figure ();\n\
plot (1:10);\n\
legend (\"Slope 1\");\n\
## Add another plot and specify its displayname so that\n\
## the legend is correctly updated.\n\
hold on;\n\
plot ((1:10) * 2, \"displayname\", \"Slope 2\");\n\
## Stop automatic updates for further plots.\n\
legend (\"autoupdate\", \"off\");\n\
plot ((1:10) * 3);\n\
@end group\n\
@end example";
        s.category = "Layout";

      case "edgecolor"
        s.doc = "Control the color of the legend outline.";
        s.valid = valid_color;
        s.category = "Legend Appearance";

      case "itemhitfcn"
        s.doc = "Callback function which is executed when a legend item \
is clicked.  @xref{Callbacks, , @w{Callbacks section}}.\n\
\n\
The callback function must have the following prototype \
@code{fcn (hlegend, evnt)}, where @code{hlegend} is the legend object handle \
and @code{evnt} is a structure with the following fields:\n\
@table @code\n\
@item Peer\n\
Handle of the plot object to which the clicked item is associated.\n\
@item Region\n\
May be @qcode{\"icon\"} or @qcode{\"label\"} depending on which part of \
the item is clicked.\n\
@item SelectionType\n\
One of @qcode{\"normal\"}, @qcode{\"extend\"}, @qcode{\"open\"}, or \
@qcode{\"alt\"}.  \
@xref{XREFfigureselectiontype, , @w{Figure @qcode{\"selectiontype\"}}}.\n\
@item Source\n\
Handle of the legend object.\n\
@item EventName\n\
Name is @qcode{\"ItemHit\"}.\n\
@end table";
        s.category = "Callback Execution";

      case "location"
        s.doc = "Control the location of the legend.";
        s.category = "Object Position";

      case "numcolumns"
        s.doc = "Control the number of columns used in the layout of the \
legend items.  \
For example:\n\
@example\n\
@group\n\
figure ();\n\
plot (rand (30));\n\
legend (\"numcolumns\", 3);\n\
@end group\n\
@end example\n\
__modemsg__";
        s.valid = "scalar integer";
        s.category = "Layout";

      case "orientation"
        s.doc = "Control whether the legend items are arranged vertically \
(column-wise) or horizontally (row-wise).";
        s.category = "Layout";

      case "string"
        s.doc = "List of labels for the legend items.  For example:\n\
@example\n\
@group\n\
figure ();\n\
plot (rand (20));\n\
## Let legend choose names automatically\n\
hl = legend ();\n\
## Selectively change some names\n\
str = get (hl, \"string\");\n\
str(1:5:end) = \"Garbage\";\n\
set (hl, \"string\", str);\n\
@end group\n\
@end example";
        s.valid = valid_cellstring;
        s.printdefault = false;
        s.category = "Text Appearance";

      case "textcolor"
        s.doc = "Control the color of the text strings for legend items.";
        s.valid = valid_color;
        s.category = "Text Appearance";

      case "textposition"
        s.doc = "Control whether text strings are displayed on the left or \
right of their corresponding icon.";
        s.category = "Text Appearance";

    endswitch

  ## Line properties
  elseif (strcmp (objname, "line"))
    switch (field)
      ## Overridden shared properties
      case "children"
        s.doc = sprintf (doc_notimpl, "Child objects for lines");
        ## Use base category.

      ## Specific properties
      case "color"
        s.doc = "Color of the line object.  @xref{Colors, , colorspec}.";
        s.valid = valid_color;
        s.category = "Line Appearance";

      case "displayname"
        s.doc = "Text for the legend entry corresponding to this line.";
        s.valid = valid_cellstring;
        s.category = "Legend Options";

      case "linestyle"
        s.doc = "@xref{Line Styles}.";
        s.category = "Line Appearance";

      case "linewidth"
        s.doc = "Width of the line object measured in points.";
        s.valid = "scalar";
        s.category = "Line Appearance";

      case "linejoin"
        s.doc = "Control the shape of the junction of line segments.  \
This property currently only affects the printed output.";
        s.category = "Line Appearance";

      case "marker"
        s.doc = "Shape of the marker for each data point.  \
@xref{Marker Styles}.";
        s.category = "Marker Appearance";

      case "markeredgecolor"
        s.doc = "Color of the edge of the markers.  When set to \
@qcode{\"auto\"}, the marker edges have the same color as the line.  If set \
to @qcode{\"none\"}, no marker edges are displayed.  This property can also \
be set to any color.  @xref{Colors, , colorspec}.";
        s.category = "Marker Appearance";

      case "markerfacecolor"
        s.doc = "Color of the face of the markers.  When set to \
@qcode{\"auto\"}, the marker faces have the same color as the line.  If set \
to @qcode{\"none\"}, the marker faces are not displayed.  This property \
can also be set to any color.  @xref{Colors, , colorspec}.";
        s.category = "Marker Appearance";

      case "markersize"
        s.doc = "Size of the markers measured in points.";
        s.valid = "scalar";
        s.category = "Marker Appearance";

      case "xdata"
        s.doc = "Vector of x data to be plotted.";
        s.valid = "vector";
        s.category = "Coordinate Data";

      case "xdatasource"
        s.doc = "Name of a vector in the current base workspace to use as \
x data.";
        s.valid = valid_string;
        s.category = "Coordinate Data";

      case "ydata"
        s.doc = "Vector of y data to be plotted.";
        s.valid = "vector";
        s.category = "Coordinate Data";

      case "ydatasource"
        s.doc = "Name of a vector in the current base workspace to use as \
y data.";
        s.valid = valid_string;
        s.category = "Coordinate Data";

      case "zdata"
        s.doc = "Vector of z data to be plotted.";
        s.valid = "vector";
        s.category = "Coordinate Data";

      case "zdatasource"
        s.doc = "Name of a vector in the current base workspace to use as \
z data.";
        s.valid = valid_string;
        s.category = "Coordinate Data";

    endswitch

  ## Text properties
  elseif (strcmp (objname, "text"))
    switch (field)
      ## Overridden shared properties
      case "children"
        s.doc = ["__objname__ objects have no child objects.  ", doc_unused];
        ## Use base category.

      ## Specific properties
      case "backgroundcolor"
        s.doc = "Color of the background area.  @xref{Colors, , colorspec}.";
        s.valid = valid_color;
        s.category = "Text Box Appearance";

      case "color"
        s.doc = "Color of the text.  @xref{Colors, ,colorspec}.";
        s.valid = valid_color;
        s.category = "Text Appearance";

      case "edgecolor"
        s.doc = "Color of the outline of the background area.  \
@xref{Colors, , colorspec}.";
        s.valid = valid_color;
        s.category = "Text Box Appearance";

      case "editing"
        s.doc = sprintf (doc_notimpl, "Interactive text editing");
        s.category = "Text Appearance";

      case "extent"
        s.doc = "Vector @code{[x0 y0 width height]} indicating the size and \
location of the text string.";
        s.valid = valid_4elvec;
        s.printdefault = false;
        s.category = "Object Position";

      case "fontangle"
        s.doc = doc_fontangle;
        s.category = "Text Appearance";

      case "fontname"
        s.doc = doc_fontname;
        s.valid = valid_string;
        s.category = "Text Appearance";

      case "fontsmoothing"
        s.doc = "Control whether anti-aliasing is used when rendering text.";
        s.category = "Text Appearance";

      case "fontsize"
        s.doc = doc_fontsize;
        s.valid = "scalar";
        s.category = "Text Appearance";

      case "fontunits"
        s.doc = doc_fontunits;
        s.category = "Text Appearance";

      case "fontweight"
        s.doc = doc_fontweight;
        s.category = "Text Appearance";

      case "horizontalalignment"
        s.doc = "Specifies the horizontal location of the point set by the \
@ref{XREFtextposition, , @w{@qcode{\"position\"} property}} relative to the \
text.";
        s.category = "Object Position";

      case "interpreter"
        s.doc = "Control the way the @qcode{\"string\"} property is \
interpreted.\n\
@xref{Use of the \"interpreter\" Property, , @w{Use of the @qcode{\"interpreter\"} Property}}.";
        s.category = "Text Appearance";

      case "linestyle"
        s.doc = "Style of the text box outline.  @xref{Line Styles}.";
        s.category = "Text Box Appearance";

      case "linewidth"
        s.doc = "Width of the text box outline.";
        s.valid = "scalar";
        s.category = "Text Box Appearance";

      case "margin"
        s.doc = "Margins between the borders of the background area \
and the texts.  The value is currently interpreted as pixels, regardless \
of the @qcode{\"fontunits\"} property.";
        s.valid = "scalar";
        s.category = "Text Box Appearance";

      case "position"
        s.doc = "Vector @code{[X0 Y0 Z0]} where X0, Y0, and Z0 indicate the \
position of the text anchor as defined by @code{verticalalignment} and \
@code{horizontalalignment}.";
        s.valid = valid_3elvec;
        s.category = "Object Position";

      case "rotation"
        s.doc = "The angle of rotation for the displayed text, measured in \
degrees.";
        s.valid = "scalar";
        s.category = "Object Position";

      case "string"
        s.doc = "The __objname__ object string content.";
        s.valid = valid_string;
        s.category = "Text Appearance";

      case "units"
        s.doc = "Sets the measurement unit or method applied to the \
@ref{XREFtextposition, , @qcode{\"position\"}} and \
@ref{XREFtextextent, , @qcode{\"extent\"}} properties.  The default \
option @qcode{\"data\"} uses the same units and limits as the data plotted in \
the figure.  The @qcode{\"normalized\"} option applies a unitless 0 to 1 scale \
to the limits along each axis of the displayed data.";
        s.category = "Object Position";

      case "verticalalignment"
        s.doc = "Specifies the vertical location of the point set by the \
@ref{XREFtextposition, , @w{@qcode{\"position\"} property}} relative to the \
text.  Note that @qcode{\"top\"} and @qcode{\"bottom\"} align to the edge of \
the text box while @qcode{\"cap\"} and @qcode{\"baseline\"} refer to the edges \
of the text itself.";
        s.category = "Object Position";

    endswitch

  ## Image properties
  elseif (strcmp (objname, "image"))
    switch (field)
      ## Overridden shared properties
      case "children"
        s.doc = sprintf (doc_notimpl, "Child objects of Images");
        ## Use base category.

      ## Specific properties
      case "alphadata"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.valid = valid_scalmat;
        s.category = "Image Data";

      case "alphadatamapping"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Image Data";

      case "cdata"
        s.doc = "Color data for the image object.  Data is either stored as \
a 2-D matrix where each element's value determines that pixel's color \
according to the current colormap, or as a 3-D array where the third \
dimension contains separate red, blue, and green components for each pixel.  \
For RGB arrays, the values that map to minimum and maximum color value \
depend on the class of @qcode{\"cdata\"}.  Floating point and logical values \
range from 0 to 1 while integer value range from @code{intmin} to \
@code{intmax} for that integer class.";
        s.valid = "array";
        s.category = "Image Data";

      case "cdatamapping"
        s.doc = "Sets the method for mapping data from the \
@ref{XREFimagecdata, , @w{@qcode{\"cdata\"} property}} to the current \
colormap.  @qcode{\"Direct\"} mapping selects the color using the \
@qcode{\"cdata\"} value as an index to the current colormap.  \
@qcode{\"Scaled\"} mapping scales the @qcode{\"cdata\"} values to the range \
specified in the @ref{XREFaxesclim, , @w{@qcode{\"clim\"} axes property}}.";
        s.category = "Image Data";

      case "displayname"
        s.doc = "Text for the legend entry corresponding to this image.";
        s.valid = valid_cellstring;
        s.category = "Legend Options";

      case "xdata"
        s.doc = "Two-element vector @code{[xfirst xlast]} specifying the x \
coordinates of the centers of the first and last columns of the image.\n\
\n\
Setting @code{xdata} to the empty matrix ([]) will restore the default value \
of @code{[1 columns(image)]}.";
        s.valid = valid_2elvec;
        s.category = "Image Data";

      case "ydata"
        s.doc = "Two-element vector @code{[yfirst ylast]} specifying the y \
coordinates of the centers of the first and last rows of the image.\n\
\n\
Setting @code{ydata} to the empty matrix ([]) will restore the default value \
of @code{[1 rows(image)]}.";
        s.valid = valid_2elvec;
        s.category = "Image Data";

    endswitch

  ## Surface properties
  elseif (strcmp (objname, "surface"))
    switch (field)
      ## Overridden shared properties
      case "children"
        s.doc = sprintf (doc_notimpl, "Child objects for Surfaces");
        ## Use base category.

      ## Specific properties
      case "alphadata"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.valid = valid_scalmat;
        s.category = "Color and Transparency";

      case "alphadatamapping"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Color and Transparency";

      case "ambientstrength"
        s.doc = "Strength of the ambient light.  Value between 0.0 and 1.0.";
        s.valid = "scalar";
        s.category = "Lighting";

      case "backfacelighting"
        s.doc = "@qcode{\"lit\"}: The normals are used as is for lighting.  \
@qcode{\"reverselit\"}: The normals are always oriented towards the point of \
view.  @qcode{\"unlit\"}: Faces with normals pointing away from the point of \
view are unlit.";
        s.category = "Lighting";

      case "cdata"
        s.doc = "Color data values for __objname__ vertices. Data is stored \
either as a 2-D matrix the same size as \
@ref{XREFsurfacezdata, , @qcode{\"zdata\"}} where each element's value \
determines that vertex's color according to the current colormap, or as a \
3-D array where the third dimension contains separate red, blue, and green \
components for each vertex.";
        s.valid = "array";
        s.category = "Color and Transparency";

      case "cdatamapping"
        s.doc = "Sets the method for mapping data from the \
@ref{XREFsurfacecdata, , @w{@qcode{\"cdata\"} property}} to the current \
colormap.  @qcode{\"Direct\"} mapping selects the color using the \
@qcode{\"cdata\"} value as an index to the current colormap.  \
@qcode{\"Scale\"} mapping scales the @qcode{\"cdata\"} values to the range \
specified in the @ref{XREFaxesclim, , @w{@qcode{\"clim\"} axes property}}.";
        s.category = "Color and Transparency";

      case "cdatasource"
        s.doc = "The name of a workspace variable that contains data that \
will be used for the \
@ref{XREFsurfacecdata, , @w{@qcode{\"cdata\"} property}}.  Data is \
transferred into @qcode{\"cdata\"} using the \
@ref{XREFrefreshdata, , @w{refreshdata function}}.";
        s.valid = valid_string;
        s.category = "Color and Transparency";

      case "diffusestrength"
        s.doc = "Strength of the diffuse reflection.  Value between 0.0 (no \
diffuse reflection) and 1.0 (full diffuse reflection).";
        s.valid = "scalar";
        s.category = "Lighting";

      case "displayname"
        s.doc = "Text for the legend entry corresponding to this surface.";
        s.category = "Legend Options";

      case "edgealpha"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.valid = "scalar";
        s.category = "Outline Appearance";

      case "edgecolor"
        s.doc = "Color of the edges of the __objname__ object, specified as \
either a valid color specification or one of @qcode{\"none\"}, \
@qcode{\"flat\"}, or @qcode{\"interp\"}.  @qcode{\"flat\"} and \
@qcode{\"interp\"} will set either a single color for each edge or a color \
interpolated between two adjacent vertices using the color value data stored \
in @ref{XREFsurfacecdata, , @qcode{\"cdata\"}}.  \
@xref{Colors, , colorspec}.";
        s.valid = packopt ({valid_color, ...
                            "@qcode{\"none\"}", ...
                            "@qcode{\"flat\"}", ...
                            "@qcode{\"interp\"}"});
        s.category = "Outline Appearance";

      case "edgelighting"
        s.doc = "When set to a value other than @qcode{\"none\"}, the edges \
of the object are drawn with light and shadow effects.  Supported values are \
@qcode{\"none\"} (no lighting effects), @qcode{\"flat\"} (faceted look), and \
@qcode{\"gouraud\"} (linear interpolation of the lighting effects between \
the vertices).  @qcode{\"phong\"} is deprecated and has the same effect as \
@qcode{\"gouraud\"}.";
        s.category = "Outline Appearance";

      case "facealpha"
        s.doc = "Transparency level of the faces of the surface object.  Only \
double values are supported at present where a value of 0 means complete \
transparency and a value of 1 means solid faces without transparency.  Setting \
the property to @qcode{\"flat\"}, @qcode{\"interp\"} or @qcode{\"texturemap\"} \
causes the faces to not being rendered.  Additionally, the faces are not \
sorted from back to front which might lead to unexpected results when \
rendering layered transparent faces.";
        s.valid = packopt ({"scalar", ...
                            "@qcode{\"flat\"}", ...
                            "@qcode{\"interp\"}", ...
                            "@qcode{\"texturemap\"}"});
        s.category = "Faces Appearance";

      case "facecolor"
        s.doc = "Color of the faces of the __objname__ object, specified as \
either a valid color specification or one of @qcode{\"none\"}, \
@qcode{\"flat\"}, or @qcode{\"interp\"}.  @qcode{\"flat\"} and \
@qcode{\"interp\"} will set either a single color for each face or a color \
interpolated across the face's vertices using the color value data stored in \
@ref{XREFsurfacecdata, , @qcode{\"cdata\"}}.  \
@xref{Colors, , colorspec}.";
        s.valid = packopt ({valid_color, ...
                            "@qcode{\"none\"}", ...
                            "@qcode{\"flat\"}", ...
                            "@qcode{\"interp\"}"});
        s.category = "Faces Appearance";

      case "facelighting"
        s.doc = "When set to a value other than @qcode{\"none\"}, the faces \
of the object are drawn with light and shadow effects.  Supported values are \
@qcode{\"none\"} (no lighting effects), @qcode{\"flat\"} (faceted look), and \
@qcode{\"gouraud\"} (linear interpolation of the lighting effects between \
the vertices).  @qcode{\"phong\"} is deprecated and has the same effect as \
@qcode{\"gouraud\"}.";
        s.category = "Faces Appearance";

      case "facenormals"
        s.doc = "Face normals are used for lighting the edges or faces if the \
@code{edgelighting} or @code{facelighting} properties are set to \
@qcode{\"flat\"}.  __modemsg__";
        s.category = "Faces Appearance";

      case "facenormalsmode"
        s.doc = "If this property is set to @qcode{\"auto\"}, \
@code{facenormals} are automatically calculated if the @code{edgelighting} or \
@code{facelighting} property are set to @qcode{\"flat\"} and at least one \
@code{light} object is present and visible in the same axes.";
        s.category = "Faces Appearance";

      case "linestyle"
        s.doc = "@xref{Line Styles}.";
        s.category = "Outline Appearance";

      case "linewidth"
        s.doc = "@xref{XREFlinelinewidth, , @w{line linewidth property}}.";
        s.valid = "scalar";
        s.category = "Outline Appearance";

      case "marker"
        s.doc = "@xref{Marker Styles}.";
        s.category = "Marker Appearance";

      case "markeredgecolor"
        s.doc = "@xref{XREFlinemarkeredgecolor, , \
@w{line markeredgecolor property}}.";
        s.category = "Marker Appearance";

      case "markerfacecolor"
        s.doc = "@xref{XREFlinemarkerfacecolor, , \
@w{line markerfacecolor property}}.";
        s.category = "Marker Appearance";

      case "markersize"
        s.doc = "@xref{XREFlinemarkersize, , \
@w{line markersize property}}.";
        s.valid = "scalar";
        s.category = "Marker Appearance";

      case "meshstyle"
        s.doc = "Specifies whether to display the edges associated with the \
        surface data's rows, columns, or both.";
        s.category = "Outline Appearance";

      case "specularcolorreflectance"
        s.doc = "Reflectance for specular color.  Value between 0.0 (color \
of underlying face) and 1.0 (color of light source).";
        s.valid = "scalar";
        s.category = "Lighting";

      case "specularexponent"
        s.doc = "Exponent for the specular reflection.  The lower the value, \
the more the reflection is spread out.";
        s.valid = "scalar";
        s.category = "Lighting";

      case "specularstrength"
        s.doc = "Strength of the specular reflection.  Value between 0.0 (no \
specular reflection) and 1.0 (full specular reflection).";
        s.valid = "scalar";
        s.category = "Lighting";

      case "vertexnormals"
        s.doc = "Vertex normals are used for lighting the edges or faces if \
the @code{edgelighting} or @code{facelighting} properties are set to \
@qcode{\"gouraud\"}.  __modemsg__";
        s.category = "Lighting";

      case "vertexnormalsmode"
        s.doc = "If this property is set to @qcode{\"auto\"}, \
@code{vertexnormals} are automatically calculated if the @code{edgelighting} \
or @code{facelighting} property are set to @qcode{\"gouraud\"} and at least \
one @code{light} object is present and visible in the same axes.";
        s.category = "Lighting";

      case "xdata"
        s.doc = "Data for the x-coordinate.";
        s.valid = "matrix";
        s.category = "Coordinate Data";

      case "xdatasource"
        s.doc = "The name of a workspace variable that contains data that \
will be used for the \
@ref{XREFsurfacexdata, , @w{@qcode{\"xdata\"} property}}.  Data is \
transferred into @qcode{\"xdata\"} using the \
@ref{XREFrefreshdata, , @w{refreshdata function}}.";
        s.valid = valid_string;
        s.category = "Coordinate Data";

      case "ydata"
        s.doc = "Data for the y-coordinate.";
        s.valid = "matrix";
        s.category = "Coordinate Data";

      case "ydatasource"
        s.doc = "The name of a workspace variable that contains data that \
will be used for the \
@ref{XREFsurfaceydata, , @w{@qcode{\"ydata\"} property}}.  Data is \
transferred into @qcode{\"ydata\"} using the \
@ref{XREFrefreshdata, , @w{refreshdata function}}.";
        s.valid = valid_string;
        s.category = "Coordinate Data";

      case "zdata"
        s.doc = "Data for the z-coordinate.";
        s.valid = "matrix";
        s.category = "Coordinate Data";

      case "zdatasource"
        s.doc = "The name of a workspace variable that contains data that \
will be used for the \
@ref{XREFsurfacezdata, , @w{@qcode{\"zdata\"} property}}.  Data is \
transferred into @qcode{\"zdata\"} using the \
@ref{XREFrefreshdata, , @w{refreshdata function}}.";
        s.valid = valid_string;
        s.category = "Coordinate Data";

    endswitch

  ## Patch properties
  elseif (strcmp (objname, "patch"))
    switch (field)
      ## Overridden shared properties
      case "children"
        s.doc = sprintf (doc_notimpl, "Child objects for Patch objects");
        ## Use base category.

      ## Specific properties
      case "alphadatamapping"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.category = "Color and Transparency";

      case "ambientstrength"
        s.doc = "Strength of the ambient light.  Value between 0.0 and 1.0.";
        s.valid = "scalar";
        s.category = "Lighting";

      case "backfacelighting"
        s.doc =  "@qcode{\"lit\"}: The normals are used as is for lighting.  \
@qcode{\"reverselit\"}: The normals are always oriented towards the point of \
view.  @qcode{\"unlit\"}: Faces with normals pointing away from the point of \
view are unlit.";
        s.category = "Lighting";

      case "cdata"
        s.doc = "Data defining the patch object color relative to its \
x/y/z-coordinate data.  Patch color can be defined using indices into the \
current colormap or as RGB triplets, where the RGB colors are defined along \
the third dimension. These colors can be separately defined for the entire \
patch object, for individual faces, or for individual vertices, and is \
determined by the shape of @qcode{\"cdata\"} as follows:\n\
\n\
If @qcode{\"cdata\"} is a scalar index into the current colormap or a 1-by-1-by-3 \
RGB triplet, it defines the color of all faces and edges.\n\
\n\
If the patch object has N faces, and @qcode{\"cdata\"} is a 1-by-N vector of \
colormap indices or a 1-by-N-by-3 RGB array, it defines the color of each \
face.\n\
\n\
If the patch object has N faces and M vertices per face, and @code{cdata} is \
a M-by-N matrix of colormap indices or a M-by-N-by-3 RGB array, it defines \
the color at each vertex. (The shape of @qcode{\"cdata\"} should match that of \
@ref{XREFpatchxdata,,@qcode{\"xdata\"}}, \
@ref{XREFpatchydata,,@qcode{\"ydata\"}}, and \
@ref{XREFpatchzdata,,@qcode{\"zdata\"}}.)";
        s.valid = valid_scalmatarr;
        s.category = "Color and Transparency";

      case "cdatamapping"
        s.doc = "Sets the method for mapping data from the \
@ref{XREFpatchcdata, , @qcode{\"cdata\"}} or \
@ref{XREFpatchcdata, , @qcode{\"cdata\"}} property to the current \
colormap.  @qcode{\"Direct\"} mapping selects the color using the \
@qcode{\"cdata\"} or @qcode{\"facevertexcdata\"} value as an index to the \
current colormap.  @qcode{\"Scaled\"} mapping scales the @qcode{\"cdata\"} \
or @qcode{\"facevertexcdata\"} values to the range \
specified in the @ref{XREFaxesclim, , @w{@qcode{\"clim\"} axes property}}.";
        s.category = "Color and Transparency";

      case "diffusestrength"
        s.doc = "Strength of the diffuse reflection.  Value between 0.0 (no \
diffuse reflection) and 1.0 (full diffuse reflection).";
        s.valid = "scalar";
        s.category = "Lighting";

      case "displayname"
        s.doc = "Text of the legend entry corresponding to this patch.";
        s.category = "Legend Options";

      case "edgealpha"
        s.doc = sprintf (doc_notimpl, "Transparency");
        s.valid = valid_scalmat;
        s.category = "Outline Appearance";

      case "edgecolor"
        s.doc = "Color of the edges of the __objname__ object, specified as \
either a valid color specification or one of @qcode{\"none\"}, \
@qcode{\"flat\"}, or @qcode{\"interp\"}.  @qcode{\"flat\"} and \
@qcode{\"interp\"} will set either a single color for each edge or a color \
interpolated between edge's vertices using the color value data stored in \
@ref{XREFpatchcdata, , @qcode{\"cdata\"}}.  \
@xref{Colors, , colorspec}.";
        s.valid = packopt ({valid_color, ...
                            "@qcode{\"none\"}", ...
                            "@qcode{\"flat\"}", ...
                            "@qcode{\"interp\"}"});
        s.category = "Outline Appearance";

      case "edgelighting"
        s.doc = "When set to a value other than @qcode{\"none\"}, the edges \
of the object are drawn with light and shadow effects.  Supported values are \
@qcode{\"none\"} (no lighting effects), @qcode{\"flat\"} (faceted look), and \
@qcode{\"gouraud\"} (linear interpolation of the lighting effects between \
the vertices).  @qcode{\"phong\"} is deprecated and has the same effect as \
@qcode{\"gouraud\"}.";
        s.category = "Outline Appearance";

      case "facealpha"
        s.doc = "Transparency level of the faces of the patch object.  Only \
double values are supported at present where a value of 0 means complete \
transparency and a value of 1 means solid faces without transparency.  Setting \
the property to @qcode{\"flat\"} or @qcode{\"interp\"} causes the faces to not \
being rendered.  Additionally, the faces are not sorted from back to front \
which might lead to unexpected results when rendering layered transparent \
faces.";
        s.valid = packopt ({"scalar", ...
                            "@qcode{\"flat\"}", ...
                            "@qcode{\"interp\"}"});
        s.category = "Color and Transparency";

      case "facecolor"
        s.doc = "Color of the faces of the __objname__ object, specified as \
either a valid color specification or one of @qcode{\"none\"}, \
@qcode{\"flat\"}, or @qcode{\"interp\"}.  @qcode{\"flat\"} and \
@qcode{\"interp\"} will set either a single color for each face or a color \
interpolated across the face's vertices using the color value data stored in \
either the @ref{XREFpatchcdata, , @qcode{\"cdata\"}} or \
@ref{XREFpatchfacevertexcdata, , @qcode{\"facevertexcdata\"}} \
properties.  @xref{Colors, , colorspec}.";
        ## Don't provide a default value, and mark colorspec with
        ## braces, this forces the default RGB triplet to be displayed
        s.valid = packopt ({markdef(valid_color), ...
                            "@qcode{\"none\"}", ...
                            "@qcode{\"flat\"}", ...
                            "@qcode{\"interp\"}"});
        s.category = "Color and Transparency";

      case "facelighting"
        s.doc = "When set to a value other than @qcode{\"none\"}, the faces \
of the object are drawn with light and shadow effects.  Supported values are \
@qcode{\"none\"} (no lighting effects), @qcode{\"flat\"} (faceted look), and \
@qcode{\"gouraud\"} (linear interpolation of the lighting effects between \
the vertices).  @qcode{\"phong\"} is deprecated and has the same effect as \
@qcode{\"gouraud\"}.";
        s.category = "Color and Transparency";

      case "facenormals"
        s.doc = "Face normals are used for lighting the edges or faces if the \
@code{edgelighting} or @code{facelighting} properties are set to \
@qcode{\"flat\"}.  __modemsg__";
        s.category = "Lighting";

      case "facenormalsmode"
        s.doc = "If this property is set to @qcode{\"auto\"}, \
@code{facenormals} are automatically calculated if the @code{edgelighting} or \
@code{facelighting} property are set to @qcode{\"flat\"} and at least one \
@code{light} object is present and visible in the same axes.";
        s.category = "Lighting";

      case "faces"
        s.doc = "__objname__ faces connectivity list stored as an M x N \
matrix, with each of the M faces defined by a row of up to N vertices, \
and each element contains the row index of a vertex stored in the \
@ref{XREFpatchvertices, , @w{vertices property}}.  Faces with fewer than N \
vertices use NaN values to fill empty row elements.";
        s.valid = valid_vecmat;
        s.category = "Coordinate Data";

      case "facevertexalphadata"
        s.doc = sprintf (doc_notimpl, "Face-Vertex transparency control");
        s.category = "Color and Transparency";

      case "facevertexcdata"
        s.doc = "Data defining the patch object color relative to its \
face-vertex data.  Patch color can be defined using indices into the \
current colormap or as RGB triplets, where the RGB colors are defined in the \
rows of @qcode{\"facevertexcdata\"}.  These colors can be separately defined \
for the entire patch object, for individual faces, or for individual vertices, \
and is determined by the shape of @qcode{\"facevertexcdata\"} as follows:\n\
\n\
If @code{facevertexcdata} is a scalar index into the current colormap or a \
1-by-3 RGB triplet, it defines the color of all faces and edges.\n\
\n\
If the patch object has N faces, and @code{facevertexcdata} is a N-by-1 column \
vector of indices or a N-by-3 RGB matrix, it defines the color of each one \
of the N faces.\n\
\n\
If the patch object has M vertices, and @code{facevertexcdata} is a M-by-1 \
column vector of indices or a M-by-3 RGB matrix, it defines the color at \
each vertex.";
        s.valid = valid_scalmat;
        s.category = "Color and Transparency";

      case "linestyle"
        s.doc = "@xref{Line Styles}.";
        s.category = "Outline Appearance";

      case "linewidth"
        s.doc = "@xref{XREFlinelinewidth, , @w{line linewidth property}}.";
        s.valid = "scalar";
        s.category = "Outline Appearance";

      case "marker"
        s.doc = "@xref{XREFlinemarker, , @w{line marker property}}.";
        s.category = "Marker Appearance";

      case "markeredgecolor"
        s.doc = "@xref{XREFlinemarkeredgecolor, , \
@w{line markeredgecolor property}}.";
        s.category = "Marker Appearance";

      case "markerfacecolor"
        s.doc = "@xref{XREFlinemarkerfacecolor, , \
@w{line markerfacecolor property}}.";
        s.category = "Marker Appearance";

      case "markersize"
        s.doc = "@xref{XREFlinemarkersize, , @w{line markersize property}}.";
        s.valid = "scalar";
        s.category = "Marker Appearance";

      case "specularcolorreflectance"
        s.doc = "Reflectance for specular color.  Value between 0.0 (color \
of underlying face) and 1.0 (color of light source).";
        s.valid = "scalar";
        s.category = "Lighting";

      case "specularexponent"
        s.doc = "Exponent for the specular reflection.  The lower the value, \
the more the reflection is spread out.";
        s.valid = "scalar";
        s.category = "Lighting";

      case "specularstrength"
        s.doc = "Strength of the specular reflection.  Value between 0.0 (no \
specular reflection) and 1.0 (full specular reflection).";
        s.valid = "scalar";
        s.category = "Lighting";

      case "vertexnormals"
        s.doc = "Vertex normals are used for lighting the edges or faces if \
the @code{edgelighting} or @code{facelighting} properties are set to \
@qcode{\"gouraud\"}.  __modemsg__";
        s.category = "Lighting";

      case "vertexnormalsmode"
        s.doc = "If this property is set to @qcode{\"auto\"}, \
@code{vertexnormals} are automatically calculated if the @code{edgelighting} \
or @code{facelighting} property are set to @qcode{\"gouraud\"} and at least \
one @code{light} object is present and visible in the same axes.";
        s.category = "Lighting";

      case "vertices"
        s.doc = "__objname__ vertex list stored as an N x 3 matrix, with \
each row containing the x, y, and z coordinates of the vector, and used \
with the @ref{XREFpatchfaces, , @w{faces property}} to define patch \
structure.";
        s.valid = valid_vecmat;
        s.category = "Coordinate Data";

      case "xdata"
        s.doc = "__objname__ vertex x-coordinates.";
        s.valid = valid_vecmat;
        s.category = "Coordinate Data";

      case "ydata"
        s.doc = "__objname__ vertex y-coordinates.";
        s.valid = valid_vecmat;
        s.category = "Coordinate Data";

      case "zdata"
        s.doc = "__objname__ vertex z-coordinates.";
        s.valid = valid_vecmat;
        s.category = "Coordinate Data";

    endswitch

  ## Scatter properties
  elseif (strcmp (objname, "scatter"))
    switch (field)
      ## Overridden shared properties
      case "children"
        s.doc = sprintf (doc_notimpl, "Child objects for Scatter plots");
        ## Use base category.

      ## Specific properties
      case "annotation"
        s.doc = sprintf (doc_notimpl, "Legend appearance toggling from within \
        the __objname__ object");
        s.category = "Legend Options";

      case "cdata"
        s.doc = "Data defining the scatter object color.\n\
\n\
If @code{cdata} is a scalar index into the current colormap or a RGB triplet, \
it defines the color of all scatter markers.\n\
\n\
If @code{cdata} is an N-by-1 vector of indices or an N-by-3 (RGB) matrix, \
it defines the color of each one of the N scatter markers.";
        s.valid = valid_scalmat;
        s.category = "Color Data";

      case "cdatamode"
        s.doc = "If @code{cdatamode} is @qcode{\"auto\"}, @code{cdata} is set \
to the color from the @code{colororder} of the ancestor axes corresponding to \
the @code{seriesindex}.";
        s.category = "Color Data";

      case "cdatasource"
        s.doc = "The name of a workspace variable that contains data that \
will be used for the \
@ref{XREFpatchcdata, , @w{@qcode{\"cdata\"} property}}.  Data is \
transferred into @qcode{\"cdata\"} using the \
@ref{XREFrefreshdata, , @w{refreshdata function}}.";
        s.valid = valid_string;
        s.category = "Color Data";

      case "datatiptemplate"
        s.doc = sprintf (doc_notimpl, "Data tip objects");
        s.category = "Mouse Interaction";

      case "displayname"
        s.doc = "Text of the legend entry corresponding to this scatter \
object.";
        s.category = "Legend Options";

      case "latitudedata"
        s.doc = sprintf (doc_notimpl, "Geographic coordinate scatter plotting");
        s.category = "Coordinate Data";

      case "latitudedatasource"
        s.doc = sprintf (doc_notimpl, "Geographic coordinate scatter plotting");
        s.category = "Coordinate Data";

      case "linewidth"
        s.doc = "Line width of the edge of the markers.";
        s.valid = "scalar";
        s.category = "Marker Appearance";

      case "longitudedata"
        s.doc = sprintf (doc_notimpl, "Geographic coordinate scatter plotting");
        s.category = "Coordinate Data";

      case "longitudedatasource"
        s.doc = sprintf (doc_notimpl, "Geographic coordinate scatter plotting");
        s.category = "Coordinate Data";

      case "marker"
        s.doc = "@xref{XREFlinemarker, , @w{line marker property}}.";
        s.category = "Marker Appearance";

      case "markeredgealpha"
        s.doc = "Transparency level of the faces of the markers where a \
value of 0 means complete transparency and a value of 1 means solid faces \
without transparency.  Note that the markers are not sorted from back to \
front which might lead to unexpected results when rendering layered \
transparent markers or in combination with other transparent objects.";
        s.valid = "scalar";
        s.category = "Marker Appearance";

      case "markeredgecolor"
        s.doc = "Color of the edge of the markers.  @qcode{\"none\"} means \
that the edges are transparent and @qcode{\"flat\"} means that the value \
from @code{cdata} is used.  @xref{XREFlinemarkeredgecolor, , \
@w{line markeredgecolor property}}.";
        s.valid = packopt ({markdef("@qcode{\"none\"}"), ...
                            "@qcode{\"flat\"}", ...
                            valid_color});
        s.category = "Marker Appearance";

      case "markerfacealpha"
        s.doc = "Transparency level of the faces of the markers where a \
value of 0 means complete transparency and a value of 1 means solid faces \
without transparency.  Note that the markers are not sorted from back to \
front which might lead to unexpected results when rendering layered \
transparent markers or in combination with other transparent objects.";
        s.valid = "scalar";
        s.category = "Marker Appearance";

      case "markerfacecolor"
        s.doc = "Color of the face of the markers.  @qcode{\"none\"} means \
that the faces are transparent, @qcode{\"flat\"} means that the value from \
@code{cdata} is used, and @qcode{\"auto\"} uses the @code{color} property of \
the ancestor axes.  @xref{XREFlinemarkerfacecolor, , \
@w{line markerfacecolor property}}.";
        s.valid = packopt ({markdef("@qcode{\"none\"}"), ...
                            "@qcode{\"flat\"}", ...
                            "@qcode{\"auto\"}", ...
                            valid_color});
        s.category = "Marker Appearance";

      case "rdata"
        s.doc = sprintf (doc_notimpl, "Polar coordinates for scatter plotting");
        s.category = "Coordinate Data";

      case "rdatasource"
        s.doc = sprintf (doc_notimpl, "Polar coordinates for scatter plotting");
        s.category = "Coordinate Data";

      case "seriesindex"
        s.doc = "Each scatter object in the same axes is assigned an \
incrementing integer.  This corresponds to the index into the \
@code{colororder} of the ancestor axes that is used if @code{cdatamode} is \
set to @qcode{\"auto\"}.";
        s.category = "Color Data";

      case "sizedata"
        s.doc = "Size of the area of the marker.  A scalar value applies to \
all markers.  If @code{cdata} is an N-by-1 vector, it defines the color of \
each one of the N scatter markers.";
        s.valid =  packopt ({"[]", "scalar", "vector"});
        s.category = "Marker Appearance";

      case "sizedatasource"
        s.doc = sprintf (doc_notimpl, "Data from workspace variables");
        s.category = "Marker Appearance";

      case "thetadata"
        s.doc = sprintf (doc_notimpl, "Polar coordinates for scatter plotting");
        s.category = "Coordinate Data";

      case "thetadatasource"
        s.doc = sprintf (doc_notimpl, "Polar coordinates for scatter plotting");
        s.category = "Coordinate Data";

      case "xdata"
        s.doc = "Vector with the x coordinates of the scatter object.";
        s.valid = "vector";
        s.category = "Coordinate Data";

        case "xdatasource"
        s.doc = "The name of a workspace variable that contains data that \
will be used for the \
@ref{XREFscatterxdata, , @w{@qcode{\"xdata\"} property}}.  Data is \
transferred into @qcode{\"xdata\"} using the \
@ref{XREFrefreshdata, , @w{refreshdata function}}.";
        s.valid = valid_string;
        s.category = "Coordinate Data";

      case "ydata"
        s.doc = "Vector with the y coordinates of the scatter object.";
        s.valid = "vector";
        s.category = "Coordinate Data";

      case "ydatasource"
        s.doc = "The name of a workspace variable that contains data that \
will be used for the \
@ref{XREFscatterydata, , @w{@qcode{\"ydata\"} property}}.  Data is \
transferred into @qcode{\"ydata\"} using the \
@ref{XREFrefreshdata, , @w{refreshdata function}}.";
        s.valid = valid_string;
        s.category = "Coordinate Data";

      case "zdata"
        s.doc = "For 3D data, vector with the y coordinates of the scatter \
object.";
        s.valid = packopt ({"[]", "vector"});
        s.category = "Coordinate Data";

      case "zdatasource"
        s.doc = "The name of a workspace variable that contains data that \
will be used for the \
@ref{XREFscatterzdata, , @w{@qcode{\"zdata\"} property}}.  Data is \
transferred into @qcode{\"zdata\"} using the \
@ref{XREFrefreshdata, , @w{refreshdata function}}.";
        s.valid = valid_string;
        s.category = "Coordinate Data";

    endswitch

  ## Light properties
  elseif (strcmp (objname, "light"))
    switch (field)
      ## Overridden shared properties
      case "children"
        s.doc = ["__objname__ objects have no child objects.  ", doc_unused];
        ## Use base category.

      ## Specific properties
      case "color"
        s.doc = "Color of the light source.  @xref{Colors, ,colorspec}.";
        s.valid = valid_color;
        s.category = "Lighting";

      case "position"
        s.doc = "Position of the light source.";
        s.category = "Lighting";

      case "style"
        s.doc = "This string defines whether the light emanates from a \
light source at infinite distance (@qcode{\"infinite\"}) or from a local \
point source (@qcode{\"local\"}).";
        s.category = "Lighting";

    endswitch

  ## uimenu properties
  elseif (strcmp (objname, "uimenu"))
    switch (field)
      ## Overridden shared properties
      case "buttondownfcn"
        s.doc = doc_unused;
        ## Use base category.

      ## Specific properties
      case "accelerator"
        s.doc = "A character that when pressed together with CTRL will \
execute this menu entry (e.g., @qcode{\"x\"} for @code{CTRL+x}).";
        s.valid = "character";
        s.category = "Keyboard Interaction";

      case "callback"
        s.doc = "A string consisting of a valid Octave expression that will \
be executed whenever this menu item is selected.";
        s.valid = "string";
        s.category = "Callback Execution";

      case "checked"
        s.doc = "Sets whether or not a mark appears at this menu entry.";
        s.category = "Menu Options";

      case "enable"
        s.doc = "Sets whether this menu entry is active or is grayed out.";
        s.category = "Menu Options";

      case "foregroundcolor"
        s.doc = "The color value of the text for this menu entry.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "menuselectedfcn"
        s.doc = "Function that is called when this menu item is executed.  \
__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Callback Execution";

      case "position"
        s.doc = "A scalar value containing the relative menu position from \
the left or top depending on the orientation of the menu.";
        s.valid = "scalar";
        s.category = "Object Position";

      case "separator"
        s.doc = "State indicating whether a separator line will be drawn \
above the current menu position.";
        s.category = "Appearance";

      case "text"
        s.doc = "The text for this menu entry.  A @qcode{\"&\"} character \
can be used to mark the \
@ref{XREFuimenuaccelerator, , @w{@qcode{\"accelerator\"} key}}";
        s.valid = "string";
        s.category = "Menu Options";

    endswitch

  ## uicontextmenu properties
  elseif (strcmp (objname, "uicontextmenu"))
    switch (field)
      ## Overridden shared properties
      case "buttondownfcn"
        s.doc = doc_unused;
        ## Use base category.

      ## Specific properties
      case "callback"
        s.doc = "A string consisting of a valid Octave expression that will \
be executed whenever this item is selected.";
        s.valid = "string";
        s.category = "Callback Execution";

      case "position"
        s.doc = sprintf (doc_notimpl, "Manually setting location for \
uicontextmenu to appear");
        s.category = "Object Position";

    endswitch

  ## uipanel properties
  elseif (strcmp (objname, "uipanel"))
    switch (field)
      ## Overridden shared properties

      ## Specific properties
    case "backgroundcolor"
        s.doc = "The color value of the background of this panel.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "bordertype"
        s.doc = "Sets whether or not a line border will surround the panel.";
        s.category = "Appearance";

      case "borderwidth"
        s.doc = "The width of the line border in pixels.";
        s.valid = "whole number scalar";
        s.category = "Appearance";

      case "fontangle"
        s.doc = doc_fontangle;
        s.category = "Text Appearance";

      case "fontname"
        s.doc = doc_fontname;
        s.valid = valid_string;
        s.category = "Text Appearance";

      case "fontsize"
        s.doc = doc_fontsize;
        s.valid = "scalar";
        s.category = "Text Appearance";

      case "fontunits"
        s.doc = doc_fontunits;
        s.category = "Text Appearance";

      case "fontweight"
        s.doc = doc_fontweight;
        s.category = "Text Appearance";

      case "foregroundcolor"
        s.doc = "The color value of the title text for this panel.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "highlightcolor"
        s.doc = "The color value of the line bordering this panel.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "position"
        s.doc = "Size of the panel represented as the four-element vector \
[left, bottom, width, height].";
        s.valid = valid_4elvec;
        s.category = "Object Position";

      case "resizefcn"
        s.doc = "__prop__ is deprecated.  Use @code{sizechangedfcn} instead.";
        s.valid = valid_fcn;
        s.category = "Callback Execution";

      case "shadowcolor"
        s.doc = "The color value of the line surrounding the border line \
around this panel.  @xref{Colors, , colorspec}.";
        s.valid = valid_color;

        s.category = "Appearance";

      case "sizechangedfcn"
        s.doc = "Callback triggered when the panel size is changed.\
\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Callback Execution";

      case "title"
        s.doc = "The text for the panel title.";
        s.valid = "string";
        s.category = "Text Appearance";

      case "titleposition"
        s.doc = "Relative position of the title within the panel.";
        s.category = "Text Appearance";

      case "units"
        s.doc = "Unit of measurement used to interpret the \
@qcode{\"position\"} property.";
        s.category = "Object Position";

    endswitch

  ## uibuttongroup properties
  elseif (strcmp (objname, "uibuttongroup"))
    switch (field)
      ## Overridden shared properties

      ## Specific properties
      case "backgroundcolor"
        s.doc = "The color value of the background of this buttongroup.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "bordertype"
        s.doc = "Sets whether or not a line border will surround the \
buttongroup.";
        s.category = "Appearance";

      case "borderwidth"
        s.doc = "The width of the line border in pixels.";
        s.valid = "whole number scalar";
        s.category = "Appearance";

      case "fontangle"
        s.doc = doc_fontangle;
        s.category = "Text Appearance";

      case "fontname"
        s.doc = doc_fontname;
        s.valid = valid_string;
        s.category = "Text Appearance";

      case "fontsize"
        s.doc = doc_fontsize;
        s.valid = "scalar";
        s.category = "Text Appearance";

      case "fontunits"
        s.doc = doc_fontunits;
        s.category = "Text Appearance";

      case "fontweight"
        s.doc = doc_fontweight;
        s.category = "Text Appearance";

      case "foregroundcolor"
        s.doc = "The color value of the title text for this buttongroup.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "highlightcolor"
        s.doc = "The color value of the line bordering this buttongroup.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "position"
        s.doc = "Size of the buttongroup represented as the four-element \
vector [left, bottom, width, height].";
        s.valid = valid_4elvec;
        s.category = "Object Position";

      case "resizefcn"
        s.doc = "__prop__ is deprecated.  Use @code{sizechangedfcn} instead.";
        s.valid = valid_fcn;
        s.category = "Callback Execution";

      case "selectedobject"
        s.doc = "Graphic handle of the currently selected item in the \
buttongroup.";
        s.category = "Button Group Operation";

      case "selectionchangedfcn"
        s.doc = "Callback triggered when the selected item within the \
buttongroup is changed.\
\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Callback Execution";

      case "shadowcolor"
        s.doc = "The color value of the line surrounding the border line \
around this buttongroup.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "sizechangedfcn"
        s.doc = "Callback triggered when the buttongroup size is changed.\
\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Appearance";

      case "title"
        s.doc = "The text for the buttongroup title.";
        s.valid = "string";
        s.category = "Text Appearance";

      case "titleposition"
        s.doc = "Relative position of the title within the buttongroup.";
        s.category = "Text Appearance";

      case "units"
        s.doc = "Unit of measurement used to interpret the \
@qcode{\"position\"} property.";
        s.category = "Object Position";


    endswitch

  ## uicontrol properties
  elseif (strcmp (objname, "uicontrol"))
    switch (field)
      ## Overridden shared properties

      ## Specific properties
      case "backgroundcolor"
        s.doc = "The color value of the background of this control object.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "callback"
        s.doc = "A string consisting of a valid Octave expression that will \
be executed whenever this control is activated.";
        s.valid = "string";
        s.category = "Callback Execution";

      case "cdata"
        s.doc = "Image data used to represent the control object, stored as \
a M x N x 3 RGB array.";
        s.valid = "array";
        s.category = "Appearance";

      case "enable"
        s.doc = "Sets whether this control object is active or is grayed \
out.";
        s.category = "Control Options";

      case "extent"
        s.doc = "Size of the text string associated to the uicontrol \
returned in the form @code{[0 0 width height]} (the two first elements \
are always zero).\n\n\
For multi-line strings the returned @code{width} and @code{height} \
indicate the size of the rectangle enclosing all lines.";
        s.valid = valid_4elvec;
        s.printdefault = false;
        s.category = "Appearance";

      case "fontangle"
        s.doc = doc_fontangle;
        s.category = "Text Appearance";

      case "fontname"
        s.doc = doc_fontname;
        s.valid = valid_string;
        s.category = "Text Appearance";

      case "fontsize"
        s.doc = doc_fontsize;
        s.valid = "scalar";
        s.category = "Text Appearance";

      case "fontunits"
        s.doc = doc_fontunits;
        s.category = "Text Appearance";

      case "fontweight"
        s.doc = doc_fontweight;
        s.category = "Text Appearance";

      case "foregroundcolor"
        s.doc = "The color value of the text for this control object.  \
@xref{Colors, , colorspec}.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "horizontalalignment"
        s.doc = "Specifies the horizontal justification of the text within \
the uicontrol object.";
        s.category = "Text Appearance";

      case "keypressfcn"
        s.doc = "Function that is executed when a key is pressed and \
the control object has focus.\n\n__fcnmsg__";
        s.valid = valid_fcn;
        s.category = "Callback Execution";

      case "listboxtop"
        s.doc = "The index of the string option that will appear at the top of \
\"listbox\" controls ";
        s.valid = "scalar";
        s.category = "Control Options";

      case "max"
        s.doc = "The maximum control value, whose effect on the control is \
dependent on the control type. For @qcode{\"checkbox\"}, \
@qcode{\"togglebutton\"}, and @qcode{\"radiobutton\"} controls, the \
@qcode{\"max\"} value is assigned to the @qcode{\"value\"} property when the \
control object is selected.  For @qcode{\"slider\"} controls, \
@qcode{\"max\"} defines the maximum value of the slider.  For \
@qcode{\"edit\"} and @qcode{\"listbox\"} controls, if @code{Max - Min > 1}, \
then the control will permit multiple line entries or list item selections, \
respectively.";
        s.valid = "scalar";
        s.category = "Control Options";

      case "min"
        s.doc = "The minimum control value, whose effect on the control is \
dependent on the control type. For @qcode{\"checkbox\"}, \
@qcode{\"togglebutton\"}, and @qcode{\"radiobutton\"} controls, the \
@qcode{\"min\"} value is assigned to the @qcode{\"value\"} property when the \
control object is not selected.  For @qcode{\"slider\"} controls, \
@qcode{\"min\"} defines the minimum value of the slider.  For \
@qcode{\"edit\"} and @qcode{\"listbox\"} controls, if @code{Max - Min > 1}, \
then the control will permit multiple line entries or list item selections, \
respectively.";
        s.valid = "scalar";
        s.category = "Control Options";

      case "position"
        s.doc = "Size of the control object represented as the four-element \
vector [left, bottom, width, height].";
        s.valid = valid_4elvec;
        s.category = "Object Position";

      case "sliderstep"
        s.doc = "The fractional step size, measured relative to the \
@code{Min - Max} span of the slider, that the slider moves when the user \
clicks on the object.  @qcode{\"sliderstep\"} is specified as a two-element \
vector consisting of @code{[minor major]}, where @qcode{\"minor\"} is the \
step size for clicking on the slider arrows, and @qcode{\"major\"} is the \
step size for clicking within the slider bar.";
        s.valid = valid_2elvec;
        s.category = "Control Options";

      case "string"
        s.doc = "The text appearing with the control object.";
        s.valid = "string";
        s.category = "Text Appearance";

      case "style"
        s.doc = "The type of control object created.  For a complete \
description of available control styles, see the \
@ref{XREFuicontrol, , @w{@qcode{\"uicontrol\"} function}}";
        s.category = "Appearance";

      case "tooltipstring"
        s.doc = "A text string that appears in a tooltip when the mouse \
pointer hovers over the control object.";
        s.valid = "string";
        s.category = "Mouse Interaction";

      case "units"
        s.doc = "Unit of measurement used to interpret the \
@qcode{\"position\"} property.";
        s.category = "Object Position";

      case "value"
        s.doc = "A numerical value associated with the current state of the \
control object, with the meaning of the value dependent on the \"style\" of \
the control object.";
        s.valid = "scalar";
        s.category = "Control Options";

      case "verticalalignment"
        s.doc = "Specifies the vertical position of the text in the \
uicontrol object.";
        s.category = "Text Appearance";

    endswitch

  ## uitable Properties
  elseif (strcmp (objname, "uitable"))
    switch (field)
      ## Overridden shared properties

      ## Specific properties
      case "backgroundcolor"
        s.doc = "Color of the background of the table specified as a \
3-element RBG vector.  If __prop__ has multiple rows, the colors cycle \
repeatedly if the \
@ref{XREFuitablerowstriping, , @qcode{\"rowstriping\"} property} is on.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "celleditcallback"
        s.doc = "A string consisting of a valid Octave expression that will \
be executed whenever a table cell is edited.";
        s.valid = "string";
        s.category = "Callback Execution";

      case "cellselectioncallback"
        s.doc = "A string consisting of a valid Octave expression that will \
be executed whenever a table cell is selected.";
        s.valid = "string";
        s.category = "Callback Execution";

      case "columneditable"
        s.doc = "A logical indicator of whether the columns are editable.  \
It consists of either a 1 x N vector of logical values where true or false \
indicate the corresponding column is or is not editable, respectively, or \
an empty logical array indicating that no column is editable.";
        s.valid = "logical row vector";
        s.category = "Table Operation";

      case "columnformat"
        s.doc = "The display format for numeric data in each column.  \
Valid formats include @qcode{\"char\"}, @qcode{\"logical\"}, \
@qcode{\"numeric\"}, or a valid format setting from the \
@ref{XREFformat, , format function}";
        s.category = "Table Data";

      case "columnname"
        s.doc = "Column names specified as either @qcode{\"numbered\"} or a \
1 x N cell string vector containing the names to be used for each column \
heading.";
        s.category = "Table Data";

      case "columnwidth"
        s.doc = "Setting for determining width of each column, valid \
options include:  @qcode{\"auto\"}, @qcode{\"fit\"}, evenly divided \
multiples specified as @qcode{\"1x\"}, @qcode{\"2x\"}, etc., or a 1 x N cell \
vector where each element corresponds to one of N table columns, and \
containing any of the above options or a fixed width specified in pixels.";
        s.category = "Table Data";

      case "data"
        s.doc = "The data contained in the table specified as either a \
2-D numeric, logical, or cell array.";
        s.valid = "matrix";
        s.category = "Table Data";

      case "enable"
        s.doc = "Sets whether this table object is active or is grayed out.";
        s.category = "Table Operation";

      case "extent"
        s.doc = "A for element vector indicating the size of the table.  \
The first two elements of the array are always zero, while the third and \
fourth elements contain the height and width of the table.";
        s.valid = valid_4elvec;
        s.printdefault = false;
        s.category = "Object Position";

      case "fontangle"
        s.doc = doc_fontangle;
        s.category = "Text Appearance";

      case "fontname"
        s.doc = doc_fontname;
        s.valid = valid_string;
        s.category = "Text Appearance";

      case "fontsize"
        s.doc = doc_fontsize;
        s.valid = "scalar";
        s.category = "Text Appearance";

      case "fontunits"
        s.doc = doc_fontunits;
        s.category = "Text Appearance";

      case "fontweight"
        s.doc = doc_fontweight;
        s.category = "Text Appearance";

      case "foregroundcolor"
        s.doc = "Color of the data text in this table.  \
@xref{Colors, , colorspec}.";
        s.valid = valid_color;
        s.category = "Appearance";

      case "keypressfcn"
        s.doc = "A string consisting of a valid Octave expression that will \
be executed whenever a key is pressed in this table object.";
        s.valid = "string";
        s.category = "Callback Execution";

      case "keyreleasefcn"
        s.doc = "A string consisting of a valid Octave expression that will \
be executed whenever a key is released in this table object.";
        s.category = "Callback Execution";

      case "position"
        s.doc = "The position and size of the table.  The four elements of \
the vector are the coordinates of the lower left corner and width and height \
of the figure.  @xref{XREFuitableunits, , @w{units property}}.";
        s.valid = valid_4elvec;
        s.category = "Object Position";

      case "rearrangeablecolumns"
        s.doc = "Indicates whether or not the ability to move columns by \
clicking and dragging the column headers.";
        s.category = "Table Operation";

      case "rowname"
        s.doc = "Row names specified as either @qcode{\"numbered\"} or a \
N x 1 cell string vector containing the names to be used for each row \
heading.";
        s.category = "Table Data";

      case "rowstriping"
        s.doc = "Setting to indicate whether the table background color \
will use different colors for each row.  Colors are drawn from the \
@ref{XREFuitablebackgroundcolor, , @qcode{\"backgroundcolor\"} property} in \
a repeating pattern.";
        s.category = "Appearance";

      case "tooltipstring"
        s.doc = "A text string that appears in a tooltip when the mouse \
pointer hovers over the table object.";
        s.valid = "string";
        s.category = "Mouse Interaction";

      case "units"
        s.doc = "Unit of measurement used to interpret the \
@qcode{\"position\"} property.";
        s.category = "Object Position";

    endswitch

  ## uitoolbar properties
  elseif (strcmp (objname, "uitoolbar"))
    switch (field)
      ## Overridden shared properties
      case "buttondownfcn"
        s.doc = doc_unused;
        ## Use base category.

    endswitch

  ## uipushtool properties
  elseif (strcmp (objname, "uipushtool"))
    switch (field)
      ## Overridden shared properties
      case "buttondownfcn"
        s.doc = doc_unused;
        ## Use base category.

      ## Specific properties
      case "__named_icon__"
        s.doc = "The name of an bundled icon file to use as the image for \
the pushtool object.";
        s.valid = "string";
        s.category = "Appearance";

      case "cdata"
        s.doc = "Image data used to represent the pushtool object, stored as \
a M x N x 3 RGB array.";
        s.valid = "array";
        s.category = "Appearance";

      case "clickedcallback"
        s.doc = "A string consisting of a valid Octave expression that will \
be executed whenever this control object is clicked.";
        s.valid = "string";
        s.category = "Callback Execution";

      case "enable"
              s.doc = "Sets whether this pushtool object is active or is \
grayed out.";
        s.category = "Pushtool Operation";

      case "separator"
        s.doc = "State indicating whether a separator line will be drawn \
next to the current pushtool position.";
        s.category = "Appearance";

      case "tooltipstring"
        s.doc = "A text string that appears in a tooltip when the mouse \
pointer hovers over the pushtool object.";
        s.valid = "string";
        s.category = "Mouse Interaction";

    endswitch

  ## uitoggletool properties
  elseif (strcmp (objname, "uitoggletool"))
    switch (field)
      ## Overridden shared properties
      case "buttondownfcn"
        s.doc = doc_unused;
        ## Use base category.

      ## Specific properties
      case "__named_icon__"
        s.doc = "The name of an bundled icon file to use as the image for \
the toggletool object.";
        s.valid = "string";
        s.category = "Appearance";

      case "cdata"
        s.doc = "Image data used to represent the toggletool object, stored \
as a M x N x 3 RGB array.";
        s.valid = "array";
        s.category = "Appearance";

      case "clickedcallback"
        s.doc = "A string consisting of a valid Octave expression that will \
be executed whenever this control object is clicked.";
        s.valid = "string";
        s.category = "Callback Execution";

      case "enable"
        s.doc = "Sets whether this toggletool object is active or is grayed \
out.";
        s.category = "Toggle Operation";

      case "offcallback"
        s.doc = "A string consisting of a valid Octave expression that will \
be executed whenever this control object is toggled off.";
        s.valid = "string";
        s.category = "Callback Execution";

      case "oncallback"
        s.doc = "A string consisting of a valid Octave expression that will \
be executed whenever this control object is toggled on.";
        s.valid = "string";
        s.category = "Callback Execution";

      case "separator"
        s.doc = "Setting to draw a vertical line to the left of the \
toggletool.";
        s.category = "Appearance";

      case "state"
        s.doc = "The current @qcode{\"on\"} or @qcode{\"off\"} state of the \
toggletool object.";
        s.category = "Toggle Operation";

      case "tooltipstring"
        s.doc = "A text string that appears in a tooltip when the mouse \
pointer hovers over the toggletool object.";
        s.valid = "string";
        s.category = "Mouse Interaction";

    endswitch
  endif

  ## Replace keywords
  if (! isempty (s.doc) && ! strcmp (objname, "base"))
    s.doc = expand_doc (s.doc, field, objname);
  endif

endfunction

function strout = expand_doc (strin, field, objname)
  strout = strrep (strin, "__objname__", objname);
  strout = strrep (strout, "__prop__", ["@code{" field "}"]);

  modemsg = "Setting @code{%s} also forces the @code{%smode} \
property to be set to @qcode{\"manual\"}.";
  modemsg = sprintf (modemsg, field, field);
  strout = strrep (strout, "__modemsg__", modemsg);
  fcnmsg = "For information on how to write graphics listener \
functions see @ref{Callbacks, , @w{Callbacks section}}.";
  strout = strrep (strout, "__fcnmsg__", fcnmsg);
endfunction

function s = getstructure (objname, base = [], props = {})
  hf = [];
  if (! strcmp (objname, "root"))
    ## Use an improbable number to avoid ishghandle to return true for 1
    hf = figure (2265465, "visible", "off");
  endif

  ## Build a default object to extract its properties list and default values.
  if (strcmp (objname, "base"))
    ## Base properties are extracted from hggroup that only have 1 additional
    ## regular (non-hidden) property, "displayname".
    h = hggroup ();
  elseif (strcmp (objname, "root"))
    h = 0;
  elseif (strcmp (objname, "figure"))
    h = hf;
  elseif (strcmp (objname, "legend"))
    line ();
    h = legend ();
    if (isempty (props))
        props = {"autoupdate", "box", "color", "edgecolor", "fontangle", ...
                 "fontname", "fontsize", "fontunits", "fontweight", ...
                 "itemhitfcn", "location", "numcolumns", "orientation", ...
                 "position", "string", "textcolor", "title", "units"};
    endif
  elseif (strcmp (objname, "scatter"))
    ## Make sure to get a scatter object independent of graphics toolkit
    hax = axes (hf);
    h = __go_scatter__ (hax);
  else
    eval (["h = " objname " ();"]);
  endif

  gprop = get (h);
  sprop = set (h);

  if (! isempty (props))
    flds = fieldnames (gprop);
    idx = cellfun (@(s) ! any (strcmp (props, s)), flds);
    gprop = rmfield (gprop, flds(idx));
    flds = fieldnames (sprop);
    idx = cellfun (@(s) ! any (strcmp (props, s)), flds);
    sprop = rmfield (sprop, flds(idx));
  endif

  fields = fieldnames (gprop);
  nf = numel (fields);
  args = cell (2*nf, 1);
  for ii = 1:nf
    field = fields{ii};

    ## Get hard coded documentation
    val = getdoc (objname, field, base);

    ## Extract the default values that are not hard coded in getdoc
    if (isempty (val.default) && val.printdefault)
      val.default = getdefault (h, objname, field);
    endif

    val.isreadonly = ! isfield (sprop, field);

    ## Extract the valid values that are not hard coded in getdoc
    if (! val.isreadonly && isempty (val.valid))
      val.valid = sprop.(field);
      if (! isempty (val.valid) && iscellstr (val.valid))
        ## Add double quotes around string radio properties
        val.valid = cellfun (@(s) ["@qcode{\"" s "\"}"], val.valid,
                             "uniformoutput", false);
        val.valid = strjoin (val.valid, " | ");
      endif
    endif

    args{2*(ii-1)+1} = field;
    args{2*ii} = val;
  endfor

  ## Build struct and remove unused fields in base properties
  s = struct (args{:});

  if (strcmp (objname, "base"))
    s = rmfield (s, "displayname");
  endif

  if (isfigure (hf))
    close (hf)
  endif

endfunction

function def = getdefault (h, objname, field)
  ## This function is meant to be run without initialization file so
  ## that the properties we get are the default.
  def = get (h, field);

  ## Don't print default values for graphics handles
  if (ishghandle (def) && isscalar (def) && def != 0)
    def = "";
  else
    if (ischar (def))
      def = ['@qcode{"' def '"}'];
    else
      if ((isvector (def) && numel (def) < 5) || isempty (def))
        ## Use disp to print the default value for short vectors and
        ## empty values
        str = disp (def);
        str(end) = [];          # remove linefeed
        str = strtrim (str);    # remove leading space

        ## Add [] around vector values
        if (ismatrix (def) && numel (def) > 1)
          str = ["[" str "]"];
          ## Add ";" between columns vector values
          if (rows (def) != 1)
            str = strrep (str, "\n", "; ");
          endif
        endif

        ## Replace texinfo reserved characters
        def = strrep (str, "@", "@@");  # must occur first
        def = strrep (def, "{", "@{");
        def = strrep (def, "}", "@}");

        def = ["@code{" def "}"];
      else
        args = arrayfun (@(x) num2str (x), size (def), "uniformoutput", false);
        def = [strjoin(args, "-by-") " " class(def)];
      endif
    endif
  endif

endfunction

function str = printdoc (objname, obj, is_prop_subset)
  ## File header and beginning of properties table
  str = warn_autogen ();
  if (strcmp (objname, "root"))
    str = sprintf ("%s\n\nProperties of the root graphics object:", str);
  elseif (is_prop_subset)
    ## Do nothing
  else
    str = sprintf ("%s\n\nProperties of @code{%s} objects (@pxref{XREF%s,,%s}):",
                   str, objname, objname, objname);
  endif

  ## Sort fields by category, put the special categories "Miscellaneous" and
  ## "Unused" last if they are used.
  allfields = fieldnames (obj);
  allcategories = cellfun (@(s) obj.(s).category, allfields, "uni", false);
  categories = unique (allcategories);
  idx = strcmp (categories, "Miscellaneous");
  categories = [categories(!idx); categories(idx)];
  idx = strcmp (categories, "Unused");
  categories = [categories(!idx); categories(idx)];

  ## Add links to categories at the top
  str = sprintf ("%s\n\n@subsubheading Categories:\n\n", str);

  for ii = 1:numel (categories);
    str = sprintf ("%s@ref{XREF%scategory%s, , @w{%s}}@: ", str, objname, strrep (categories{ii}, " ", ""), categories{ii});
    if (ii < numel (categories))
      str = sprintf ("%s| ", str);
    endif
  endfor

  idx = [];
  for ii = 1:numel (categories)
    fields = sort (allfields(strcmp (allcategories, categories{ii})));
    nf = numel (fields);
    str = sprintf ("%s\n\n@anchor{XREF%scategory%s}\n", str, ...
                   objname, strrep (categories{ii}, " ", ""));
    str = sprintf ("%s@subsubheading %s\n", str, categories{ii});
    str = sprintf ("%s@prindex %s %s\n", str, ...
                   objname, strrep (categories{ii}, " ", ""));
    str = sprintf ("%s\n@table @asis", str);

    for jj = 1:nf
      field = fields{jj};
      str = sprintf ("%s\n\n", str);

      if (! is_prop_subset)
        ## @anchor: cross reference using XREFobjnamefield label
        ## Concept index: call info from octave with 'doc ("objname field")'
        str = sprintf ("%s@anchor{XREF%s%s}\n@prindex %s %s\n",
                       str, objname, field, objname, field);
      endif

      ## Item
      str = sprintf ("%s@item @code{%s}", str, field);

     ## Mark item read-only if needed
      if (obj.(field).isreadonly)
        str = sprintf ("%s (read-only):", str);
      else
        str = sprintf ("%s:", str);
      endif

      ## Print valid and default values
      tmp = print_options (obj.(field).valid,
                           obj.(field).default);
      if (! isempty (tmp))
        str = sprintf ("%s %s\n", str, tmp);
      else
        str = sprintf ("%s\n", str);
      endif

      ## Print documentation
      str = sprintf ("%s%s\n", str, obj.(field).doc);
    endfor

    ## End of properties table
    str = sprintf ("%s\n@end table", str);
  endfor

endfunction

function str = warn_autogen ()
  str = "@c DO NOT EDIT!  Generated automatically by genpropdoc.m.\n\
\n\
@c Copyright (C) 2014-2024 The Octave Project Developers\n\
@c\n\
@c See the file COPYRIGHT.md in the top-level directory of this\n\
@c distribution or <https://octave.org/copyright/>.\n\
@c\n\
@c This file is part of Octave.\n\
@c\n\
@c Octave is free software: you can redistribute it and/or modify it\n\
@c under the terms of the GNU General Public License as published by\n\
@c the Free Software Foundation, either version 3 of the License, or\n\
@c (at your option) any later version.\n\
@c\n\
@c Octave is distributed in the hope that it will be useful, but\n\
@c WITHOUT ANY WARRANTY; without even the implied warranty of\n\
@c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
@c GNU General Public License for more details.\n\
@c\n\
@c You should have received a copy of the GNU General Public License\n\
@c along with Octave; see the file COPYING.  If not, see\n\
@c <https://www.gnu.org/licenses/>.";
endfunction

function str = print_options (val, default)
  str = "";
  if (! isempty (val))
    tmp = strrep (val, default, ["@{" default "@}"]);
    if (length (tmp) == length (val) && ! isempty (default))
      str = [tmp ", def. " default];
    else
      str = tmp;
    endif
  elseif (! isempty (default))
    str = ["def. " default];
  endif

endfunction
