diff --git a/centrallix-dev-tools/cx.vim b/centrallix-dev-tools/cx.vim index d5a4e38e9..5b9dbe401 100755 --- a/centrallix-dev-tools/cx.vim +++ b/centrallix-dev-tools/cx.vim @@ -26,7 +26,7 @@ syn keyword cxKeywords show_branches show_diagnostics show_root show_root_branch syn keyword cxKeywords tab_revealed_only tab_width target target_key_1 target_key_2 target_key_3 target_key_4 target_key_5 text textcolor textcolorhighlight syn keyword cxKeywords textcolornew title titlebar titlecolor tooltip toplevel tristate type use_having_clause use_3d_lines valign value visible widget_template syn keyword cxKeywords widget_class width windowsize x y -syn keyword cxKeywords param_name fl_height fl_y fl_x display_for min_rowheight max_rowheight initial_selection demand_scrollbar overlap_scrollbar +syn keyword cxKeywords param_name fl_height display_for min_rowheight max_rowheight initial_selection demand_scrollbar overlap_scrollbar syn keyword cxKeywords row_border_radius border_radius diff --git a/centrallix-doc/Widgets/widgets.xml b/centrallix-doc/Widgets/widgets.xml index 6d283f5f2..60dd9edff 100644 --- a/centrallix-doc/Widgets/widgets.xml +++ b/centrallix-doc/Widgets/widgets.xml @@ -1,101 +1,70 @@ - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]> @@ -110,6 +79,8 @@

A "hbox" widget stacks its children horizontally one after the other, while a "vbox" widget stacks its children vertically from the top down. The spacing between children can be set, and the size of each child can be either independently set per-child, or it can be set by virtue of the geometry of the autolayout widget.

+

Blank space can be placed between the children of an autolayout widget by adding an "autolayoutspacer" child. A spacer draws nothing of its own, but it takes part in the layout just like any other child.

+

The autolayout widget supports having multiple rows or columns of widgets. That is, if a "vbox" widget fills up vertically, it can resume adding children to a second vertical column should there be space to the right of the original column of widgets. For this to work, the width of the columns (for a vbox) or the height of the rows (for an hbox) must be specified.

@@ -122,16 +93,16 @@ - Sets the alignment of text can have left (default) right or center. - + Sets the alignment of text can have left (default) right or center. + For an hbox, this is the width to use for the child widgets (unless otherwise specified by the child widget). For a vbox, this refers to the height to use for child widgets unless otherwise specified. This specifies the width of columns for a vbox -- should the first column of widgets fill up, a second column will be started at this offset from the start of the first column. - Height, in pixels, of the autolayout area. If omitted, it defaults to the maximum available height for the given width, without overlapping other visible widgets. If both width and height are unspecified, Centrallix will chose a width and height that maximize the available autolayout area. + Height, in pixels, of the autolayout area. If omitted, it defaults to the maximum available height for the given width, without overlapping other visible widgets. If both width and height are unspecified, Centrallix will choose a width and height that maximize the available autolayout area. Makes the text fill page (justifies text - google it if you need). - + This specifies the height of rows for an hbox -- should the first row of widgets fill up horizontally, a second row will be started beneath the first one, at this vertical offset from the start of the first row. The spacing, in pixels, between widgets. For an hbox, this refers to the horizontal spacing between children. For a vbox, this refers to the vertical spacing. @@ -148,33 +119,29 @@ - + - An optional property, this allows the layout sequence of child widgets to be controlled, and the children are placed in ascending order. If unspecified, this defaults to a value of 100 for the first child encountered, or to N+1 where N is the autolayout sequence of the most recent child widget encountered. - - Height, in pixels, of the child. - - Width, in pixels, of the child. + The vertical flexibility of the spacer, as a weight relative to that of the sibling widgets. Defaults to 5, rather than to the 100 used by most widgets, so a spacer takes a smaller share of the space added or removed when the autolayout widget is resized. Set this to 0 to keep the space at a fixed size. - + The horizontal flexibility of the spacer, as a weight relative to that of the sibling widgets. Defaults to 5, rather than to the 100 used by most widgets, so a spacer takes a smaller share of the space added or removed when the autolayout widget is resized. Set this to 0 to keep the space at a fixed size. - + - + - +// A fixed 8 pixel gap between the children of an hbox. + +gap1 "widget/autolayoutspacer" + + { + + width=8; fl_width=0; + + } + + ]]> + - + + +

Combines the functionality of the textbutton and imagebutton, as well as adding the ability to have both text and image displayed in a button. The deprecated widget/textbutton and widget/imagebutton names are rendered by this widget.

+ +
+ + + +

A Button can be placed inside any visible container, but only nonvisual widgets can be placed within it. Properties that don't apply to the button's type are ignored.

+ +
+ + + + A color, RGB or named, to be used as the button's background.If neither bgcolor nor background are specified, the button is transparent. + + Sets the alignment of text in the button, can have left right or center (default). + + A background image for the button. + + A color that outlines the button. + + A radius that describes the sharpness of the corners of the button (smaller means sharper). + + Determines the look of the outline. One of 'outset' (default), 'solid', or 'none'. An image-only button defaults to 'none', and one that draws no border also draws no padding, so it hugs its image; a button with text always reserves room for its frame whatever the style. + + The ObjectSystem pathname of the image to be shown when the user clicks the button. Defaults to 'pointimage' if not specified, which in turn defaults to 'image'. -

Combines the functionality of the textbutton and imagebutton, as well as adding the ability to have both text and image displayed in a button.

+ A color, RGB or named, to be used for the button's text when it is disabled. + + The ObjectSystem pathname of the image to be shown when the button is disabled. Defaults to 'image' if not specified. + + Whether the button is enabled (can be clicked). Default is 'yes'. Also supports dynamic runclient() expressions allowing the enabled status of the button to follow the value of an expression. -
+ A color, RGB or named, for the text on the button. Default "white". - + A color, RGB or named, for the text's 1-pixel drop-shadow. Default "black". -

A Button can be placed inside any visible container, but only nonvisual widgets can be placed within it. Properties that don't apply to the button's type are ignored.

+ Height, in pixels, of the text button. -
+ The pathname of the image to be shown when the button is "idle". - + Defines the height of image. Defaults to the button's own height for an image-only button. - A color, RGB or named, to be used as the button's background.If neither bgcolor nor background are specified, the button is transparent. + Deprecated spelling of 'spacing'. - The ObjectSystem pathname of the image to be shown when the user clicks the imagebutton. Defaults to 'image' if not specified. + Describes where an image sits relative to the text (top, right, bottom, left). Deprecated: use the matching 'type' value instead. Only consulted when 'type' is absent. - A color, RGB or named, to be used for the button's text when it is disabled. + Defines the width of image. Defaults to the button's own width for an image-only button. - The ObjectSystem pathname of the image to be shown when the imagebutton is disabled. Defaults to 'image' if not specified. + The pathname of the image to be shown when the button is pointed-to. Defaults to the 'image' if not specified. - Whether the button is enabled (can be clicked). Default is 'yes'. Also supports dynamic runclient() expressions allowing the enabled status of the button to follow the value of an expression. + The margin around the image, in pixels, which separates it from the text. Defaults to 0. Spelled 'image_margin' on the deprecated widget/textbutton. - A color, RGB or named, for the text on the button. Default "white". + Whether to keep firing the MouseDown event while the user holds the button down. The Click event still fires once, on release. - A color, RGB or named, for the text's 1-pixel drop-shadow. Default "black". + The text to appear on the button. - Height, in pixels, of the text button. + The text that appears when the cursor hovers over the button. - The pathname of the image to be shown when the button is "idle". + Whether or not the button is tri-state (does not display a raised border until the user points at it). Default is yes. - The pathname of the image to be shown when the button is pointed-to. Defaults to the 'image' if not specified. - - The distance between the image and text if applicable. + There are currently 7 different types: 1) text 2) image 3) topimage (image above text) 4) rightimage 5) leftimage 6) bottomimage 7) textoverimage (text over image background). - The text to appear on the button. + The width, in pixels, of the text button. - Whether or not the button is tri-state (does not display a raised border until the user points at it). Default is yes. + X-coordinate of the upper left corner of the button, relative to its container. - There are currently 7 different types. 1) text 2) image 3) topimage (image above text) 4) rightimage 5) leftimage 6) bottomimage 7) textoverimage (text over image background). + Y-coordinate of the upper left corner of the button, relative to its container. - The width, in pixels, of the text button. + - X-coordinate of the upper left corner of the button, relative to its container. + - Y-coordinate of the upper left corner of the button, relative to its container. + Whether the button is enabled. Can be read and set at runtime. -
- - + - - This event occurs when the user clicks the widget. No parameters are available from this event. + + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. @@ -265,29 +269,26 @@ vbox1 "widget/vbox" This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. This event occurs when the user moves the mouse pointer off of the widget. - + This event occurs when the user releases the mouse button on the widget. - + Called from a connector, this action sets the button's text to the value passed in through connector's "Text" parameter. - + Triggers a Click event on the button with the "from_action" parameter set to 1. - This action causes the button to enter its disabled state, displaying the disable_color, if specified. - ]]> + This action causes the button to enter its enabled state, possibly changing its normal colors. - + + @@ -296,23 +297,23 @@ vbox1 "widget/vbox"
    -
  • Year - Presents twelve months at a time in a low-detail type of setting; when events occur on a day, the day is highlighted, but no more data than that is displayed. If the user points the mouse at a given day, it will display in a popup "tooltip" type of manner the events associated with that day. If more than one year's worth of data is in the objectsource's replica, then multiple years are displayed one after the other. Each year is displayed as four rows of three months each, in traditional calendar format.
  • +
  • Year - Presents twelve months at a time in a low-detail type of setting; when events occur on a day, the day is highlighted, but no more data than that is displayed. If the user points the mouse at a given day, it will display in a popup "tooltip" type of manner the events associated with that day. If more than one year's worth of data is in the objectsource's replica, then multiple years are displayed one after the other. Each year is displayed as four rows of three months each, in traditional calendar format.
  • -
  • Month - Presents one month's worth of data in a medium-detail display. For each month with data in the objectsource, the calendar will display the month as multiple rows, each row containing one week's worth of days. The calendar will attempt to display the various entries for each day, in order of priority, but will limit the amount of data displayed to keep the boxes for the days approximately visually square. Pointing at a day will show the events for the day in a popup, and pointing at an event in the day will show the details for that event.
  • +
  • Month - Presents one month's worth of data in a medium-detail display. For each month with data in the objectsource, the calendar will display the month as multiple rows, each row containing one week's worth of days. The calendar will attempt to display the various entries for each day, in order of priority, but will limit the amount of data displayed to keep the boxes for the days approximately visually square. Pointing at a day will show the events for the day in a popup, and pointing at an event in the day will show the details for that event.
  • -
  • Week - Presents one week's worth of data in a high-detail display. For each week with data in the objectsource, the calendar will display the week as seven days with a large vertical extent capable of displaying all events for the day. The left edge of the display will contain times during the day for the day's events to line up with.
  • +
  • Week - Presents one week's worth of data in a high-detail display. For each week with data in the objectsource, the calendar will display the week as seven days with a large vertical extent capable of displaying all events for the day. The left edge of the display will contain times during the day for the day's events to line up with.
  • -
  • Day - Presents an entire day's worth of data in a high-detail display. For each day with data in the objectsource, the calendar will display the day in full-width, with the day's events listed chronologically from top to bottom.
  • +
  • Day - Presents an entire day's worth of data in a high-detail display. For each day with data in the objectsource, the calendar will display the day in full-width, with the day's events listed chronologically from top to bottom.
-

It should be noted that all of the data for the calendar must fit into the objectsource, or else the calendar will not display all of the available data. In this manner the calendar's display must be controlled via the objectsource's data.

+

It should be noted that all the data for the calendar must fit into the objectsource, or else the calendar will not display all the available data. In this manner the calendar's display must be controlled via the objectsource's data.

-

The calendar widget can be placed inside of any visual container, but because its height can change, it is often placed inside of a scrollpane widget. This widget may not contain other visual widgets.

+

The calendar widget can be placed inside any visual container, but because its height can change, it is often placed inside a scrollpane widget. This widget may not contain other visual widgets.

@@ -335,9 +336,9 @@ vbox1 "widget/vbox" The height of the calendar, in pixels. If not specified, the calendar may grow vertically an indefinite amount in order to display its given data. If specified, however, the calendar will be limited to that size. The minimum priority level for events that will be displayed on the calendar; the values for this integer depend on the nature of the 'priority' field in the data source; only events with priority values numerically higher than or equal to this minimum priority will be displayed. Defaults to 0. - + The color of the words in the calendar. - + The width of the calendar, in pixels. X-coordinate of the upper left corner of the calendar, default is 0. @@ -346,16 +347,12 @@ vbox1 "widget/vbox" - - - + This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. This event occurs when the user moves the mouse pointer while it is over the widget. The event will repeatedly fire each time the pointer moves. - + This event occurs when the user moves the mouse pointer off of the widget. This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. @@ -364,62 +361,80 @@ vbox1 "widget/vbox" - - - +
- + - + - +

A chart widget is used to display charts and graphs of data. It uses the Chart.js library.

- - -

A chart widget is used to display charts and graphs of data. It uses the Chart.js library.

-

The chart widget can be placed inside of any visual container, and will attach itself to any objectsource widget that contains it (whether directly or indirectly). Charts may not contain visual widgets.

+ +

The chart widget can be placed inside any visual container, and will attach itself to any objectsource widget that contains it (whether directly or indirectly). Charts may not contain visual widgets.

+
- Currently supported types are "bar", "line", "scatter", and "pie". Defaults to "bar". Chart.js supports much more than this, so new types could be added with relative ease. - The position of the legend on the chart. May be "top", "botton", "left", or "right". - The name of the ObjectSource widget which will supply data for the chart. We recommend that you do not specify this directly but instead embed the chart widget within a parent ObjectSource, directly or indirectly. If not specified, the chart will look for an ObjectSource in its parents. - Set to false if you want the y axis to start at the lowest data value. Default is true. - The title of the chart. Default is none. + + Currently supported types are "bar", "line", "scatter", and "pie". Defaults to "bar". Chart.js supports much more than this, so new types could be added with relative ease. + + The position of the legend on the chart. May be "top", "bottom", "left", or "right". + + The name of the ObjectSource widget which will supply data for the chart. We recommend that you do not specify this directly but instead embed the chart widget within a parent ObjectSource, directly or indirectly. If not specified, the chart will look for an ObjectSource in its parents. + + Set to false if you want the y-axis to start at the lowest data value. Default is true. + + The title of the chart. Default is none. + A color, RGB or named, of the chart title. + The size, in points, of the title font. + + - Chart.js has limited support for having multiple chart types on the same axes. The outer chart type must still be specified if this is set. + + Chart.js has limited support for having multiple chart types on the same axes. The outer chart type must still be specified if this is set. + A color, RGB or named, for the chart element (ex: line) which represents this series. - Set to false to remove the color fill beneath a line chart. Default is true. + + Specify false to remove the color fill behind a line chart. Default is true. + A label for the data series. - Which column in the data should be used for the x axis? By default, the chart will pick one for you. - Which column in the data should be used for the y axis? By default, the chart will pick one for you. + + Which column in the data should be used for the x-axis? By default, the chart will pick one for you. + + Which column in the data should be used for the y-axis? By default, the chart will pick one for you. + - Which axis is this? Possible values are "x" and "y". Default is "x". - A label for the axis. Default is none. + + Which axis is this? Possible values are "x" and "y". Default is "x". + + A label for the axis. Default is none. + + + + + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. + + This event occurs when the user double-clicks the mouse pointer (equivalent to two Click events occurring on the widget within a required time period) while it is over this widget. + + + - + ]]> +
+ @@ -461,33 +478,29 @@ chart "widget/chart" -

The checkbox widget can be placed inside of any visual container, and will attach itself to any form widget that contains it (whether directly or indirectly). Checkboxes may not contain visual widgets.

+

The checkbox widget can be placed inside any visual container, and will attach itself to any form widget that contains it (whether directly or indirectly). Checkboxes may not contain visual widgets.

Whether this checkbox should be initially checked or not. Default 'no'. - + This allows the checkbox to be changed. Name of objectsource field that should be associated with this checkbox. The name of the form that this checkbox is associated with. - + X-coordinate of the upper left corner of the checkbox, default is 0. Y-coordinate of the upper left corner of the checkbox, default is 0. - - - This event occurs when the user clicks the checkbox. No parameters are available from this event. + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. This event occurs when the user has modified the data value of the checkbox (clicked or unclicked it). @@ -504,18 +517,14 @@ chart "widget/chart" - - This takes a given value and sets the check box value to it. - - - + - + ]]>
- + + @@ -571,82 +581,60 @@ checkbox_test "widget/page" - A background image for the body of the window. + A background image for the body of the window. + + A color, RGB or named, to be used as the window body's background.If neither transparent. - A color, RGB or named, to be used as the window body's background.If neither transparent. - A color that outlines window. - + A radius that describes the sharpness of the corners of the window (smaller means sharper). Determines the look of the outline. - - Decides wither the screen closes widthwise (1) or heightwise (2). - + Acts as a boolean to declare if the window is shaded or not (all but title bar minimized). - A background image for the titlebar of the window. + A background image for the titlebar of the window. + + A color, RGB or named, for the titlebar of the window. - A color, RGB or named, for the titlebar of the window. + Height, in pixels, of the window. - Height, in pixels, of the window. + A pathname to an icon to be used in the upper left corner of the window. If unspecified, the default "CX" ichthus icon is used instead. - A pathname to an icon to be used in the upper left corner of the window. If unspecified, the default "CX" ichthus icon is used instead. + If "yes", the window is modal (and IsModal need not be passed to "Open"). A modal window will force the user to close the window before anything else in the application can be accessed. - If "yes", the window is modal (and IsModal need not be passed to "Open"). A modal window will force the user to close the window before anything else in the application can be accessed. - The placement of the shadow described as a rotational transformation with respect to the window. - + The color of the shadow. A radius that describes the sharpness of the corners of the shadow (smaller means sharper). The placement of the shadow with respect to the window. - Either "dialog" or "window", and determines the style of the window's border. - - The color for the titlebar's text (window title). Default "black". + Either "dialog" or "window", and determines the style of the window's border. - The window's title. + The color for the titlebar's text (window title). Default "black". - Whether the window will have a titlebar (and the close "X" in the upper right corner of the window). Default "yes". - - If "yes", the window will float above all other widgets in the application, otherwise it will be clipped by its own container. + The window's title. - The window is initially visible on screen. The window has an action which can "true". + Whether the window will have a titlebar (and the close "X" in the upper right corner of the window). Default "yes". - Width, in pixels, of the window. + If "yes", the window will float above all other widgets in the application, otherwise it will be clipped by its own container. - X-coordinate of the upper left corner of the window, relative to its container. + The window is initially visible on screen. The window has an action which can "true". - Y-coordinate of the upper left corner of the window, relative to its container. + Width, in pixels, of the window. - - - - - Closes the window. Note that widgets inside the window may choose to veto the Close operation: for example, if there is unsaved data in a form. - - Opens the window. If the parameter IsModal is set to 1, then the window becomes modal (only the window's contents are accessible to the user until the window is closed). If the parameter NoClose is set to 1, then the close button in the upper right corner of the window becomes inactive and the window will only close via the Close, SetVisibility, and ToggleVisibility actions. - - Makes the window relocate to a side using a triangle (pop over). - - Opens a window like a pop-up. - - One parameter, "is_visible", which is set to 0 or 1 to hide or show the window, respectively. - - Turns the gshade property(see above) on. - - If the window is visible, this closes it. If it is closed, this opens it. + X-coordinate of the upper left corner of the window, relative to its container. - Turns the gshade property(see above) off. + Y-coordinate of the upper left corner of the window, relative to its container. - + This event is triggered each time the window is closed (becomes invisible). - + This event is triggered the first time the window is opened. If the window is visible by default, then this event is triggered when the application loads. This event occurs when the user presses the mouse button on the checkbox. This differs from the 'Click' event in that the user must actually press and release the mouse button on the checkbox for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. @@ -661,12 +649,31 @@ checkbox_test "widget/page" This event is triggered each time the window is opened (becomes visible). - + + + Closes the window. Note that widgets inside the window may choose to veto the Close operation: for example, if there is unsaved data in a form. + + Opens the window. Set the "X" and "Y" parameters to the coordinates of the upper-left corner of the window to open it at that location. If the parameter "Center" is set to any value other than no (the default), the window is centered on the specified coordinates instead. Set the "PointAt" parameter with a widget name to make the new window point at that widget. Set the "PointSide" parameter with a value of top, bottom, left, or right to force the window to point from that side. Set the "PointOffset" parameter to the distance in pixels that the window should be from the widget it points at. This action also takes the "IsModal","NoClose", and "Cascade" parameters, described in the SetVisibility action. + + Creates a triangular pointer on the edge of the window to point at a given (X,Y) coordinate. + + Opens a window like a pop-up at the specified "X" and "Y" with the specified "Width" and "Height" (in pixels). Or, set the "PopTo" parameter with a widget name to copy those properties from it. In either case, the "OffsetX" and "OffsetY" parameters move the popup right and down respectively. Finally, specify the "ExtendTo" parameter with a widget name to ensure that the popup extends to that parameter. + + Sets data about how the window displays. Set the "IsVisible" parameter to 0 (hide) or 1 (show). Set the "IsModal" parameter to 1 to make the window a modal (only the window's contents are accessible to the user until it is closed). Set the "NoClose" parameter to 1 to make the close button in the upper right corner of the window inactive (the window can only be closed via the Close, SetVisibility, and ToggleVisibility actions). Set the "Cascade" parameter to 1 to cause multiple appearing windows to autopossition themselves in a cascading pattern. + + Turns the gshade property(see above) on. + + If the window is visible, this closes it. If it is closed, this opens it. + + Turns the gshade property(see above) off. + + + - + ]]> + @@ -706,7 +714,7 @@ MyWindow "widget/childwindow" -

The clock widget can be used inside of any container capable of having visual subwidgets. It may contain no widgets other than any applicable connectors.

+

The clock widget can be used inside any container capable of having visual subwidgets. It may contain no widgets other than any applicable connectors.

@@ -717,9 +725,9 @@ MyWindow "widget/childwindow" An image for the background of the clock widget. Should be an ObjectSystem path. A color, either named or numeric, for the background of the clock. If neither bgcolor nor background are specified, the clock is transparent. - + Acts as a boolean to declare if the type will be bold (larger and thicker). - + A color, either named or numeric, for the foreground text of the clock. A color, either named or numeric, for the foreground text's shadow, if 'shadowed' is enabled. @@ -729,9 +737,9 @@ MyWindow "widget/childwindow" The height of the clock, in pixels. Set to 12 for a 12-hour clock, or to 24 for military (24-hour) time. - + Acts as a boolean to control if the entire clock can be moved. - + Whether or not the seconds should be displayed. Default is 'yes'. Whether or not the clock's text has a shadow. Default is 'no'. @@ -766,11 +774,12 @@ MyWindow "widget/childwindow"
+ -

This widget is used to instantiate a custom component that has already been defined using a widget/component-decl widget, typically inside a ".cmp" file. The instantiation can be either static or dynamic: a static component is rendered along with the component or application that it resides inside, whereas a dynamic component is loaded as needed from the client. Components may also allow multiple instantiation when dynamic, which is especially beneficial with components whose top-level widget happens to be a widget/childwindow.

+

This widget is used to instantiate a custom component that has already been defined using a widget/component-decl widget, typically inside a ".cmp" file. The instantiation can be either static or dynamic: a static component is rendered along with the component or application that it resides inside, whereas a dynamic component is loaded as needed from the client. Components may also allow multiple instantiation when dynamic, which is especially beneficial with components whose top-level widget happens to be a widget/childwindow.

@@ -778,7 +787,7 @@ MyWindow "widget/childwindow"

A component can be either visual or non-visual, and can be placed at almost any point in the application.

-

At the time of writing, only connectors may be placed inside a widget/component. It is planned that it be possible to place widgets inside a widget/component which then appear inside specially designated containers within the component itself. However that capability is not available at present.

+

At the time of writing, only connectors may be placed inside a widget/component. It is planned that it be possible to place widgets inside a widget/component which then appear inside specially designated containers within the component itself. However, that capability is not available at present.

@@ -786,15 +795,15 @@ MyWindow "widget/childwindow" If enabled (dynamic single-instantiation components only), when a component is instantiated a second time, the original component is automatically destroyed so there is at most one instance at a time in existence. Defaults to 'yes'. - Can specify another form to be a child of (if different than the implied form in which this control is nested). + Can specify another form to be a child of (if different from the implied form in which this control is nested). Height, in pixels, of the component. If unset, this defaults to the height of the component's containing widget. Either "static" or "dynamic". A static component is rendered with the application, whereas a dynamic one is loaded as needed from the client. Defaults to 'static'. If enabled (dynamic components only), the component can be instantiated more than once on the client. Defaults to 'no'. - - The path, in the OSML, to the component's definition (.cmp) file. (e.g. /sys/cmp/smart_field.cmp, /sys/cmp/form_controls.cmp, /samples/button.cmp). + + The path, in the OSML, to the component's definition (.cmp) file. (e.g. /sys/cmp/smart_field.cmp, /sys/cmp/form_controls.cmp, /samples/button.cmp). Acts as a boolean to indicate if there are any components on a higher level. @@ -808,47 +817,47 @@ MyWindow "widget/childwindow" - + - Destroys the component. If multiple instances exist, then all instances are destroyed. + - Instantiates the component. + Type of smart field (e.g. label, editbox, checkbox, datetime). - + (e.g. readonly). Must have field be set in order to be "readonly". - + This is something which can accessed as :this_widget:value (returns the value of text which can't be directly accessed?), but can not be set directly through this property -- see SetValue listed under the Action section. - This event is triggered when a dynamic component completes loading from the server. + - + - + (e.g. 1) - + Title to put across the form control bar (Note: the form_controls.cmp allows the user to perform row operations on a given record set). - Type of smart field (e.g. label, editbox, checkbox, datetime). + - (e.g. readonly). Must have field be set in order to be "readonly". + - This is something which can accessed as :this_widget:value (returns the value of text which can't be directly accessed?), but can not be set directly through this property -- see SetValue listed under the Action section. + This is something which can accessed as :this_widget:content (returns the value of text which can't be directly accessed?), but can not be set directly through this property -- see SetValue listed under the Action section. - + - + - (e.g. 1) + - Title to put across the form control bar (Note: the form_controls.cmp allows the user to perform row operations on a given record set). + This event is triggered when a dynamic component completes loading from the server. - + - + - This is something which can accessed as :this_widget:content (returns the value of text which can't be directly accessed?), but can not be set directly through this property -- see SetValue listed under the Action section. + Destroys the component. If the 'Name' or 'Namespace' parameter is given, only the instances matching it are destroyed; otherwise all instances are destroyed. - + Instantiates the component. The optional 'Path' parameter is an absolute path to the component to load, which overrides the path property. The optional 'Name' parameter names the new instance, so that Destroy can select it later. - + @@ -876,6 +885,7 @@ formctl "widget/component"
+ @@ -890,9 +900,11 @@ formctl "widget/component"

Other visual and nonvisual widgets may be placed inside a component-decl, in addition to parameters and declarations of Events and Actions that the component generates and handles.

+

To declare a client-side property on the component's external interface, place a "widget/component-decl-cprop" inside the component at the top level. When the external property is changed, a MODIFY event with the same property name is activated inside the component to update its property so that it will match.

+

To declare that a component generates an Event, place a "widget/component-decl-event" inside the component at the top level. No parameters are needed for that Event. To cause the component to generate the Event, trigger an Action with the same name on the component-decl from inside, and the event will be activated for the containing application or component.

-

Similarly, to declare that a component can receive an Action, place a "widget/component-decl-action" inside the component at the top level. Again, no parameters are needed. The containing application or component can then trigger the Action, which will cause an event to occur inside the component. The event occurs on the component-decl widget (top level of the component), and can be caught with a connector widget.

+

To declare that a component can receive an Action, place a "widget/component-decl-action" inside the component at the top level. Again, no parameters are needed. The containing application or component can then trigger the Action, which will cause an event to occur inside the component. The event occurs on the component-decl widget (top level of the component), and can be caught with a connector widget.

Components can take parameters just like applications can. See the "widget/parameter" widget for details on how to declare parameters on applications and components.

@@ -913,23 +925,54 @@ formctl "widget/component" Indicates if the component is seen (not = -1). - - + + + + + + The name of the client-side property to expose. + + + + + + The name of the event to expose. + + + + + + The name of the action to expose. + + + + + + + + This event occurs before the key press event is fired and can stop the key press event. Passes the key's "Code" and "Name", and "Ctrl" which is 1 if the control key was held down. + + This event occurs when the user presses any key down. Passes the same values as BeforeKeyPress. + + This event occurs when the user presses any key. Passes the same values as BeforeKeyPress. + + This event occurs when the user releases any key. Passes the same values as BeforeKeyPress. + + Occurs when the widget has finished launching. + + + Sends an alert to the screen. - + Loads the widget. - - Allows events to happen. - + + Sets a client property that the component exposes, so that the change is visible outside the component. The "PropertyName" parameter names the property and the "NewValue" parameter holds its new value. The component's event for that property change fires unless the "TriggerEvent" parameter is set to a true value, which suppresses it. + + Fires the event named by the 'EventName' parameter inside the component, as though it had been invoked from outside the component. The remaining parameters are passed along to that event. + - - - - Occurs when the widget has finished launching. - - @@ -983,11 +1026,12 @@ my_button "widget/component-decl"
+ -

Each widget can have events and actions associated with it. The events occur when certain things occur via the user interface, via timers, or even as a result of data being loaded from the server. Actions cause certain things to happen to or within a certain widget, for example causing an HTML layer to reload with a new page, or causing a scrollable area to scroll up or down.

+

Each widget can have events and actions associated with it. The events occur when certain things occur via the user interface, via timers, or even as a result of data being loaded from the server. Actions cause certain things to happen to or within a certain widget, for example causing an HTML layer to reload with a new page, or causing a scrollable area to scroll up or down.

The connector widget allows an event to be linked with an action without actually writing any JavaScript code to do so -- the connector object is created, and given an event to trigger it and an action to perform when it is triggered.

@@ -1007,31 +1051,23 @@ my_button "widget/component-decl" - The name of the action which will be activated on another widget on the page. + The name of the action which will be activated on another widget on the page. - The name of the event that this connector will act on. - - The name of the widget generating the event (defaults to the widget the connector is placed inside of). + The name of the event that this connector will act on. - The name of another widget on the page whose action will be called by this connector (if unspecified, defaults to the widget the connector is placed inside of). + The name of the widget generating the event (default: the connector's parent widget). + + The name of another widget on the page whose action will be called by this connector (default: the connector's parent widget). - - - + ]]>
+ -

The datetime widget displays a calendar and clock. Input is done by typing into the bar or by selecting a date in display pane. When the form goes into query mode two panes will appear, one for the start and one for the end time. Note that the osml format for searching for dates is dd Mon yyyy, whereas the format dates are displayed in the widget is Mon dd, yyyy.

+

The datetime widget displays a calendar and clock. Input is done by typing into the bar or by selecting a date in display pane. When the form goes into query mode two panes will appear, one for the start and one for the end time. Note that the osml format for searching for dates is dd Mon yyyy, whereas the format dates are displayed in the widget is Mon dd, yyyy.

-

This widget can be placed within any visual widget.

+

This widget can be placed within any visual widget.

@@ -1086,14 +1123,14 @@ cn1 "widget/connector" When the user sets the date without selecting a time value (or if date_only is set), use this time as the "default" time. This can be set to "00:00:00" or "23:59:59", for example, to specify a time at the start or end of a day. The foreground color of the bar and pane (named or numeric). - + Not used currently to my knowledge. Name of a form object that is a parent of the datetime widget (not required). Height, in pixels, of the bar. - Used to set the initialdate by a string. + Sets the initial date from the specified string. Default "yes". Specifies that when the datetime widget is in a form that is in search (QBF) mode, the datetime should display both a "start" and an "end" calendar so the user can search for records/objects matching a range of dates. @@ -1109,7 +1146,7 @@ cn1 "widget/connector" - This event occurs when the user clicks the widget. No parameters are available from this event. + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. This event occurs when the user has modified the data value of the widget (clicked or unclicked it). @@ -1128,50 +1165,51 @@ cn1 "widget/connector" This event occurs when the user releases the mouse button on the widget. - + - - Generates an internal data object using the data specified or the current time. - + + Set the time of the clock to a new time, specified using "Value". + - + ]]>
+ -

A dropdown form element widget that allows one of several options to be selected in a visual manner. The options are filled in using one of the child widgets, or via an SQL query to a database defined below.

+

A dropdown form element widget that allows one of several options to be selected in a visual manner. The options are filled in using one of the child widgets, or via an SQL query to a database defined below.

-

This widget can be placed within any visual widget (or within a form widget). It may only contain 'dropdownitem' child objects, although it may of course also contain connectors as needed.

+

This widget can be placed within any visual widget (or within a form widget). It may only contain 'dropdownitem' child objects, although it may of course also contain connectors as needed.

@@ -1182,24 +1220,24 @@ cn1 "widget/connector" Fieldname (from the dataset) to bind this element to. Represents the widget form (groups many widgets into one widget). - + Height, in pixels, of the dropdown. - - The color of the highlighted option when the mouse goes over the item. + + The color of the highlighted option when the mouse goes over the item. The default widget that has focus when the dropdown is first activated. - + If this is set to objectsource then the dropdown acts an objectsource client. - Number of widgets displayed at once in the dropdown box. - + Number of widgets displayed at once in the dropdown box. + Represents the widget object source (transfers data to and from server). - + Width of popup dropdown list. - + If set to yes, this indicates that when the dropdown's form is in search (QBF) mode, the dropdown will allow multiple items to be selected, so the user can search for records/objects that match one of several values. The values will be listed in the dropdown, separated by commas. - The SQL used to retrieve the list of items for the dropdown. It should have between two and five columns, in this order: label, value, selected (0 or 1, whether the item is selected by default), grp (group name), hidden (0 or 1 to hide the item from the dropdown list but still allow it to be a valid value). + The SQL used to retrieve the list of items for the dropdown. It should have between two and five columns, in this order: label, value, selected (0 or 1, whether the item is selected by default), grp (group name), hidden (0 or 1 to hide the item from the dropdown list but still allow it to be a valid value). Width, in pixels, of the dropdown. @@ -1224,18 +1262,18 @@ cn1 "widget/connector" This event occurs when the user has modified the data value of the widget (clicked or unclicked it). - + This event occurs when the data is changed (occurs when key press or button changes things). This event occurs when the edit bar receives keyboard focus (if the user tabs on to it or clicks on it, for instance). - + This event occurs when the edit bar of the dropdown loses keyboard focus (if the user tabs off of it, for instance). This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. This event occurs when the user moves the mouse pointer while it is over the widget. The event will repeatedly fire each time the pointer moves. - This event occurs when the user moves the mouse pointer off of the widget. + This event occurs when the user moves the mouse pointer off of the widget. This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. @@ -1248,18 +1286,18 @@ cn1 "widget/connector" Deletes all the widgets in the dropdown menu. - - This causes the dropdown to display a different Group of items (use the Group parameter). It can also restrict what items are displayed based on a minimum or maximum value (use Min and Max parameters). - This specifies a SQL (use "SQL" parameter) query to use to re-load the contents of the dropdown. It should have between two and five columns, in this order: label, value, selected (0 or 1, whether the item is selected by default), grp (group name), hidden (0 or 1 to hide the item from the dropdown list but still allow it to be a valid value). + This causes the dropdown to display a different Group of items (use the "Group" parameter). It can also restrict what items are displayed based on a minimum or maximum value (use "Min" and "Max" parameters). + + This specifies a SQL (see "SQL" parameter) query to use when reloading the contents of the dropdown. Use the "RowLimit" parameter to specify a maximum number of options that the query should fetch (defaults to 100). + + Changes the child property "Value". - Changes the child property "value". - - + ]]>
+

An editbox form element widget allows the display and data entry of a single line of text (or a numeric value). When the editbox is clicked (and thus receives keyboard focus), the user can type and erase data inside the widget. Data which does not fit will cause the text to scroll. When it receives keyboard focus, the editbox displays a flashing I-beam cursor. The cursor color uses the data focus color for the page (default is '#000080', or dark blue).

-

When an editbox is part of a form and the form goes into query mode the editbox has several additional features. First, you can use the * operator as a wildcard (e.g. *ber matches September, October, November, December). Second you can enter a range if the field is numeric (e.g. 1-100). Thirdly, a list of criteria can be specified, separated by commas.

+

When an editbox is part of a form and the form goes into query mode the editbox has several additional features. First, you can use the * operator as a wildcard (e.g. *ber matches September, October, November, December). Second you can enter a range if the field is numeric (e.g. 1-100). Thirdly, a list of criteria can be specified, separated by commas.

@@ -1353,10 +1392,10 @@ myDropDown2 "widget/dropdown" A background image for the contents of the editbox. - A color, RGB or named, for the editbox contents. If neither bgcolor nor background is specified, the editbox is transparent. + A color, RGB or named, for the editbox contents. If neither bgcolor nor background is specified, the editbox is transparent. A color, RGB or named, editbox's value description. The value description is displayed to the right of the value in the editbox, in parentheses, and in a different color. - + Specifies wither the edit box can be written in, default is true meaning the edit box cannot be written in. The value description to display in the editbox when the editbox is empty. For instance, "optional" or "required" or "type search and press ENTER" might be commonly used empty_description settings. @@ -1365,7 +1404,7 @@ myDropDown2 "widget/dropdown" Height, in pixels, of the editbox. - Number of characters to accept from the user. + The maximum number of characters the editbox should accept from the user. Defaults to -1 (no limit). Set to 'yes' if the user cannot modify the value of the edit box (default 'no'). @@ -1381,28 +1420,24 @@ myDropDown2 "widget/dropdown" - - - + This event occurs before the data changes and can stop the data change event. - + This event occurs before the key press event is fired and can stop the key press event. - - This event occurs when the user clicks the widget. No parameters are available from this event. + + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. This event occurs when the user has modified the data value of the widget (clicked or unclicked it). - This event occurs when the data is changed (occurs when key press or button changes things). + This event occurs when the data is changed (occurs when key press or button changes things). It provides four parameters. "Value" represents the now current value after the modification. "OldValue" representing the previous value. "FromKeyboard" is 1 if the event is caused by the user typing into the editbox with their keyboard. "FromOSRC" is 1 if the event is caused by an OSRC modifying the edit box data. This event occurs when the user presses the escape key. This event occurs when the editbox receives keyboard focus (if the user tabs on to it or clicks on it, for instance). This event occurs when the user presses any key. - + This event occurs when the editbox loses keyboard focus (if the user tabs off of it, for instance). This event occurs when the user releases the mouse button on the widget. @@ -1414,20 +1449,22 @@ myDropDown2 "widget/dropdown" This event occurs when the user moves the mouse pointer off of the widget. This event occurs when the user moves the mouse pointer while it is over the widget. The event will repeatedly fire each time the pointer moves. - + This event occurs when the user presses the return key. - + This event occurs when the user presses the tab key. - + The Enable action allows an edit box to be written in, and takes no parameters x from being written in, and takes no parameters. - - The SetFocus action selects and gives control to an edit box. - The SetValue action modifies the contents of an editbox, and takes a single parameter, 'Value' (string). + The Disable action prevents an edit box from being written in. This action takes no parameters. + + The SetFocus action selects and gives control to an edit box at the specified "X" and "Y" coordinates (even if it is not the target?). + + The SetValue action modifies the contents of an editbox, and takes two parameters. The 'Value' (string) parameter represents the new value of the edit box. The 'Description' parameter is identical to that of the SetValueDescription event. This action modifies the "value description" of the editbox, and takes a single parameter, 'Description' (string). @@ -1435,7 +1472,7 @@ myDropDown2 "widget/dropdown" - + ]]>
+ -

The execmethod widget is a nonvisual widget which is used to call ObjectSystem methods on objects on the server.This widget may become deprecated in the future when more advanced OSML API widgets become available.

+

The execmethod widget is a nonvisual widget which is used to call ObjectSystem methods on objects on the server. This widget may become deprecated in the future when more advanced OSML API widgets become available.

These widgets are used via the activation of their "ExecuteMethod" action.

@@ -1479,29 +1517,29 @@ my_editbox "widget/editbox" -

The execmethod widget, since it is nonvisual, can be placed almost anywhere but is typically placed at the top-level (within an object of type "widget/page") for clarity's sake. It has no effect on its container.These widgets cannot contain visual widgets, and since they have no Events, normally contain no connector widgets either.

+

The execmethod widget, since it is nonvisual, can be placed almost anywhere but is typically placed at the top-level (within an object of type "widget/page") for clarity's sake. It has no effect on its container.These widgets cannot contain visual widgets, and since they have no Events, normally contain no connector widgets either.

- The name of the method to invoke. + The name of the method to invoke. - The ObjectSystem pathname of the object on which the method is to be run. + The ObjectSystem pathname of the object on which the method is to be run. - A string parameter to pass to the method being invoked. + A string parameter to pass to the method being invoked. - Action causes the widget to execute the method on the server. It can take three parameters, which default to those provided in this widget's properties: "Objname", the object path, "Method", the method to invoke, and "Parameter", the parameter to pass to the method being invoked. + Action causes the widget to execute the method on the server. It can take three parameters, which default to those provided in this widget's properties: "Objname", the object path, "Method", the method to invoke, and "Parameter", the parameter to pass to the method being invoked. - + ]]>
- + + -

The form widget is used as a high-level container for form elements. Essentially, a form widget represents a single record of data, or the attributes of a single object in the objectsystem (or of a single query result set object). Form widgets must be used in conjunction with an ObjectSource widget, which does the actual transferring of data to and from the server.

+

The fileupload widget is a nonvisual widget that allows the client to upload files to the server.

-

Forms have five different "modes"of operation, each of which can be specifically allowed or disallowed for the form.

+
+ + + +

The fileupload widget, since it is nonvisual, can be placed almost anywhere but is typically placed at the top-level (within an object of type "widget/page") to improve clarity. It has no effect on its container. This widget does cannot contain any visual widgets.

+ +
+ + + + The Centrallix object system path to the directory where uploaded files should be placed. (Causes undefined behavior if unspecified.) -
    + Set to 1 to allow the user to pass multiple files at once instead of only selecting a single file (default: 0). -
  1. No Data - form inactive/disabled, no data viewed/edited.
  2. + Unused. -
  3. View - data being viewed readonly.
  4. + Unused. -
  5. Modify - existing data being modified.
  6. + + + + + This event occurs when the uploaded file(s) is changed, either because the user selected new file(s) or because the file upload widget was cleared. It provides two parameters: "NewValue" is the new file name, or an empty string ("") if the widget was cleared, and "OldValue" is the value before the data was changed. + + This event occurs when an upload is successfully completed by the user. It provides several parameters called "OrigNameX" and "NewNameX" where X is a number greater than 0 that appear to relate to the files names that the user has uploaded. + + This event occurs when there is an error during a file upload. It provides no parameters. + + This event is registered, but it looks like it never occurs. + + + + + + This action clears whatever file was selected and uploaded by the user. This action causes a DataChange event with the NewValue parameter set to an empty string (""). It takes no parameters. -
  7. Query - query criteria being entered (used for query-by-form applications)
  8. + This action prompts the user to select a file. It takes no parameters. -
  9. New - new object being entered/created.
  10. + This action submits the selected file to be uploaded, possibly causing an UploadComplete, UploadError, or DataChange event. It takes no parameters. + +
    + + + + + + + + +

    The form widget is used as a high-level container for form elements. Essentially, a form widget represents a single record of data, or the attributes of a single object in the object system (or of a single query result set object). Form widgets must be used in conjunction with an ObjectSource widget, which does the actual transferring of data to and from the server.

    + +

    Forms have five different "modes"of operation, each of which can be specifically allowed or disallowed for the form.

    -
+
    +
  1. No Data - form inactive/disabled, no data viewed/edited.
  2. +
  3. View - data being viewed readonly.
  4. +
  5. Modify - existing data being modified.
  6. +
  7. Query - query criteria being entered (used for query-by-form applications)
  8. +
  9. New - new object being entered/created.
  10. +

Occasionally, the user may perform an operation which inherently disregards that the form may contain unsaved data. When this occurs and there is newly created or modified data in the form, the application must ask the user whether the data in the form should be saved or discarded, or whether to simply not even perform the operation in question. Since DHTML does not inherently have a "three-way confirm" message box (with save, discard, and cancel buttons), Centrallix allows a form to specify a "three-way confirm" window. This should be a hidden (visible=no) "widget/htmlwindow" object which may contain any content, but should at least contain three buttons named "_3bConfirmSave", "_3bConfirmDiscard", and "_3bConfirmCancel" directly in the htmlwindow. During a confirm operation, this window will become "application-modal"; that is, no other widgets in the application may be accessed by the user until one of the three buttons is pushed.

-

Several settings on the form widget control what state, or "mode", the form can be in: allow_query, allow_new, allow_modify, allow_view, and allow_nodata. These can beused to constrain a form to perform a specific task, such as only searching, or only creating new records. For example, a form with only allow_search enabled will always return to the search (QBF) mode and will never display the searched-for data that is returned in the objectsource.

+

Several settings on the form widget control what state, or "mode", the form can be in: allow_query, allow_new, allow_modify, allow_view, and allow_nodata. These can be used to constrain a form to perform a specific task, such as only searching, or only creating new records. For example, a form with only allow_search enabled will always return to the search (QBF) mode and will never display the searched-for data that is returned in the objectsource.

@@ -1561,111 +1645,129 @@ mySoundPlayer "widget/execmethod" - Allow deletion of the displayed record. + Allow deletion of the displayed record. - Allow modification of existing records. + Allow modification of existing records. - Allow creation of new records. + Allow creation of new records. - Allow the 'no data' state. + Allow the 'no data' state. - Default "no". If this is set to "yes", then the form will permit its data to be obscured (hidden from the user, via a window closure or tab page switch) and unsaved at the same time. If set to "no", the form will require the user to save or cancel any data modifications before the form may be obscured. + Default "no". If this is set to "yes", then the form will permit its data to be obscured (hidden from the user, via a window closure or tab page switch) and unsaved at the same time. If set to "no", the form will require the user to save or cancel any data modifications before the form may be obscured. - Allow query by form. + Allow query by form. - Allow viewing of existing data. + Allow viewing of existing data. - Default "yes". Whether to automatically place the focus on the first form element in the form when the form transitions to the New, Modify, or Search mode for any reason other than the user manually putting focus on one of the form elements. + Default "yes". Whether to automatically place the focus on the first form element in the form when the form transitions to the New, Modify, or Search mode for any reason other than the user manually putting focus on one of the form elements. - Whether to pop up an OK/Cancel message box asking the user whether he/she is sure the record should be deleted. + Whether to pop up an OK/Cancel message box asking the user whether he/she is sure the record should be deleted. - Set to true when the discard button of the 3 buttom confirm window (3bconfirmwindow) is pressed. - - Can be "save" (default), "nextfield", or "lastsave". Controls what to do when the user presses ENTER while in a form element in this form. "save" means to save the record immediately. "nextfield" means to move to the next form field, as if TAB were pressed instead. "lastsave" means to move to the next form field, but once ENTER is pressed on the very last field, to then save the record. Most people prefer "save" for this, since that is consistent with how normal applications work, but people in finance or accounting work often prefer "lastsave" since it allows them to perform high-speed data entry using a numeric keypad. + Set to true when the discard button of the 3 button confirm window (3bconfirmwindow) is pressed. + + Can be "save" (default), "nextfield", or "lastsave". Controls what to do when the user presses ENTER while in a form element in this form. "save" means to save the record immediately. "nextfield" means to move to the next form field, as if TAB were pressed instead. "lastsave" means to move to the next form field, but once ENTER is pressed on the very last field, to then save the record. Most people prefer "save" for this, since that is consistent with how normal applications work, but people in finance or accounting work often prefer "lastsave" since it allows them to perform high-speed data entry using a numeric keypad. Stores wither this form can be grouped with other forms. - - Enable MultiEnter. - Specifies another form to transfer focus to when the user presses TAB at the end of the form. + Enable MultiEnter. + + Specifies another form to transfer focus to when the user presses TAB at the end of the form. + + Similar to "next_form", but searches for the next form after this one within a given widget or component. Transitioning from one component context to another is permitted (you could specify a "widget/page" widget here, and cause focus to transfer to another form in the same application, regardless of level of component nesting; this may or may not be desired behavior). - Similar to "next_form", but searches for the next form after this one within a given widget or component. Transitioning from one component context to another is permitted (you could specify a "widget/page" widget here, and cause focus to transfer to another form in the same application, regardless of level of component nesting; this may or may not be desired behavior). + Represents the widget object source (transfers data to and from server). - Represents the widget object source (transfers data to and from server). - - Allow changes. + Allow changes. - How to react to a tab in a control. + How to react to a tab in a control. - When tabbing between form elements, if this is set to "yes", do not transfer focus to form elements that are not currently visible to the user. + When tabbing between form elements, if this is set to "yes", do not transfer focus to form elements that are not currently visible to the user. The name of the window to use for all 3-way confirm operations (save/discard/cancel). + + + Whether the form has a status or modifications that can be discarded / canceled. + + True if the form is in a state where the data can be edited (but isn't currently being edited). + + True if the form is in a state where a new record can be created. + + Whether the form can be placed into "QBF" (query by form) mode so that the user can enter query criteria. + + Whether the form is in "QBF" (query by form) mode so that the query can actually be run. + + Whether the form has data in it that can be saved with the Save action. + + The number of the last record in the query results, starting with '1'. Null if no data is available or if the last record has not yet been determined. + + The current record in the data source being viewed, starting with '1'. Null if no data is available. + + + - Fieldname (from the dataset) to bind this element to. + Field name (from the dataset) to bind this element to. - - - + This event occurs just before the form is enabled after a save. - + This event occurs when a child controller changes its data. - + This event occurs when the delete function is successfully completed. - + This event occurs when it is confirmed that the method search for object is available. - + This event occurs when the save action was successful. - + This event occurs after the discard action is performed. - + This event occurs at the end of the change mode action (always with status change event). - + This event occurs if and only if there was a mode change and the new mode is the 'Modify' state (always occurs with both a statusChange event and a ModeChange Event). - + This event occurs if and only if there was a mode change and the new mode is the 'New' state (always occurs with both a statusChange event and a ModeChange Event). - - This event occurs if and only if there was a mode change and the new mode is the 'NoData' state (always occurs with both a statusChange event and a ModeChange Event. - - This event occurs if and only if there was a mode change and the new mode is the 'Query' state (always occurs with both a statusChange event and a ModeChange Event. - + + This event occurs if and only if there was a mode change and the new mode is the 'NoData' state (always occurs with both a statusChange event and a ModeChange Event.) + + This event occurs if and only if there was a mode change and the new mode is the 'Query' state (always occurs with both a statusChange event and a ModeChange Event.) + The even occurs when the form changes modes or when a child control changes its data. - + This event occurs if and only if there was a mode change and the new mode is the 'View' state (always occurs with both a statusChange event and a ModeChange Event. - + - Clears the form to a 'no data'state. + Clears the form to a 'no data' -state. Specify 1 for the 'force' parameter to clear data even if it has not been saved. + + Deletes the current object displayed. - Deletes the current object displayed. + Cancels an edit of form contents. Specify 1 for the 'FromKeyboard' or 'FromOSRC' parameters to indicate that the action is caused by a keyboard input or an osrc, respectively (both default to 0). - Cancels an edit of form contents. - - Prevents interaction with the entire form. + Prevents interaction with the entire form. Specify any value other than no for the 'Enabled' parameter to enable it instead. Allows editing of form's contents. - - Allows interaction with the form. + + Allows interaction with the form. Specify 0 or no for the 'Enabled' parameter to disable it instead. Moves the form to the first object in the objectsource. Moves the form to the last object in the objectsource. - Allows creation of new form contents. + Allows creation of new form contents. Specify the 'Multi' parameter to set the MultiEnter field. Moves the form to the next object in the objectsource. @@ -1673,46 +1775,25 @@ mySoundPlayer "widget/execmethod" Allows entering of query criteria. - the query and returns data. - + Executes the query and returns data. + Either executes a query or allows one to be made depending on the previous state. Saves the form's contents. - - Modifies a field and puts the form into a new modify or search mode if appropriate. - - Attemps to save data, if there is an error it returns false. - - Debugging tool to test the 3bcomfirm method (only use if you know what this method is). - - Changes the form to view mode. - - - - - - - Whether the form has a status or modifications that can be discarded / canceled. - - True if the form is in a state where the data can be edited (but isn't currently being edited). - - True if the form is in a state where a new record can be created. - Whether the form can be placed into "QBF" (query by form) mode so that the user can enter query criteria. + Modifies the specified 'Field' to the specified 'Value', then puts the form into a new modify or search mode, if appropriate. - Whether the form is in "QBF" (query by form) mode so that the query can actually be run. + Attempts to save data, if there is an error it returns false. Specify the 'Target' parameter to set a target page other than the one containing the form widget. Specify any truthy value for the 'NewPage' parameter to allow launching a new page. - Whether the form has data in it that can be saved with the Save action. - - The number of the last record in the query results, starting with '1'. Null if no data is available or if the last record has not yet been determined. + Debugging tool to test the 3bcomfirm method (only use if you know what this method is). - The current record in the data source being viewed, starting with '1'. Null if no data is available. + Changes the form to view mode. - + - + ]]>
+ -

Many times with multi-mode forms like those offered by Centrallix, the end-user can become confused as to what the form is currently "doing" (for instance, is a blank form with a blinking cursor in a "new" state or "enter query" state?). Centrallix helps to address this issue using the form status widget. A form status widget displays the current mode of operation that a form is in, as well as whether the form is busy processing a query, save, or delete operation. This clear presentation of the form's mode is intended to clear up any confusion created by a multi-mode form. This widget is a special-case form element.

+

Many times with multimodal forms like those offered by Centrallix, the end-user can become confused as to what the form is currently "doing" (for instance, is a blank form with a blinking cursor in a "new" state or "enter query" state?). Centrallix helps to address this issue using the form status widget. A form status widget displays the current mode of operation that a form is in, as well as whether the form is busy processing a query, save, or delete operation. This clear presentation of the form's mode is intended to clear up any confusion created by a multimodal form. This widget is a special-case form element.

@@ -1745,7 +1827,7 @@ form1 "widget/form" The name of the form that this instance of form status corresponds to. - + Sets the style of the form widget. Can be "small", "large", or "largeflat". Allows you to set x position in pixels relative to the window. @@ -1754,12 +1836,14 @@ form1 "widget/form" - + + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. + This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. This event occurs when the user moves the mouse pointer while it is over the widget. The event will repeatedly fire each time the pointer moves. @@ -1772,32 +1856,29 @@ form1 "widget/form" - - - + ]]>
+ @@ -1808,35 +1889,35 @@ formstatus "widget/formstatus" -

The frameset can either be a top-level widget, or can be contained within a frameset (for subframes).The frameset widget should not be used anywhere else in an application. The frameset should contain only other framesets and/or pages.

+

The frameset can either be a top-level widget, or can be contained within a frameset (for subframes).The frameset widget should not be used anywhere else in an application. The frameset should contain only other framesets and/or pages.

- Number of pixels wide the border(s) between the frame(s) are. Can be set to zero. + Number of pixels wide the border(s) between the frame(s) are. Can be set to zero. - Whether the frames are arranged in rows or columns. Set this attribute to "rows" or "columns"respectively. + Whether the frames are arranged in rows or columns. Set this attribute to "rows" or "columns"respectively. - Title of the frameset for an html page. + Title of the frameset for an html page. - + + + Large this frame will be.Can either be expressed as an integer, which represents "50%". - Large this frame will be.Can either be expressed as an integer, which represents "50%". + Width, in pixels, of the margins of the frame. - Width, in pixels, of the margins of the frame. - - + - + ]]>
+ @@ -1906,78 +1988,91 @@ BigFrameset "widget/frameset" + See "widget/autolayout" + + + - + + -

The hints widget stores default values and other component modifying properties.

+

The hints widget stores default values and properties that affect data in other widgets, such as input validation data.

-

The hints widget can be placed inside of any visual component. Hints do not contain visual widgets.

+

The hints widget can be placed inside any visual component. Hints do not contain visual widgets.

Defines a set of acceptable characters (e.g. allowchars="0123456789"). - + Defines a set of unacceptable characters. Defines a condition that the parent value must meet to be valid. - + A string or integer that specifies the initial, default, starting value for the component which containing this widget/hints widget. A list of string values that give all the possible values of th the parent. - + A sql query that is used to get the required value list. - + The way in which data is presented to the user. A numeric identification for a group of attributes. A string identification for a group of attributes. - + Determines rows of the widget. - + Length in characters that can be entered into this field. - + A string or integer that stores the maximum value of the attributes of its parent widget. - + A string or integer that stores the minimum value of the attributes of its parent widget. - + If the field is a bitmask, determines which ones are read only. - - Optional: contains a combination of 1 or more items following set {readonly, alwaysdef} separated by a comma if multiple items are chosen. + + +

Optional. Contains one or more style hint items, separated by commas. Most items have a negating counterpart which prevents the item's effect, even if it is specified elsewhere (e.g. "allownull" explicitly restores the default behavior of allowing null values, even if "notnull" is specified elsewhere).

+

The valid items are listed below, each with its negating item. Although the parser accepts all of them, only the items shown in bold appear to have any implemented effect.

+
    +
  • bitmask / nobitmask
  • +
  • list / nolist
  • +
  • buttons / nobuttons
  • +
  • notnull / allownull
  • +
  • strnull / nostrnull
  • +
  • grouped / nogrouped
  • +
  • readonly / modifiable
  • +
  • hidden / visible
  • +
  • password / nopassword
  • +
  • multiline / singleline
  • +
  • highlight / nohighlight
  • +
  • uppercase / mixedcase
  • +
  • lowercase / mixedcase
  • +
  • tabpage / notabpage
  • +
  • sepwindow / nosepwindow
  • +
  • alwaysdef / noalwaysdef
  • +
  • createonly / nocreateonly
  • +
  • multiselect / singleselect
  • +
  • key / notkey
  • +
  • applyonchange / applyonmodify
  • +
+
Number of characters in the field shown to the user at once. -
- - - +} ]]>
+ -

The HTML area widget provides a way to insert a plain HTML document into a Centrallix generated page, either in-flow (static) or in its own separate layer that can be reloaded at will (dynamic). The HTML document can either be given in a property of the widget or can be referenced so that the HTML is read from an external document.

+

The HTML area widget provides a way to insert a plain HTML document into a Centrallix generated page, either in-flow (static) or in its own separate layer that can be reloaded at will (dynamic). The HTML document can either be given in a property of the widget or can be referenced so that the HTML is read from an external document.

The HTML area widget also can act as a mini-browser -- clicking on hyper-text links in the visible document will by default cause the link to be followed, and the new document to be displayed in the HTML area (if the HTML area is dynamic).

@@ -2031,36 +2127,28 @@ f_trx_mod "widget/checkbox" - Static contents for the HTML area. Usually used in lieu of "source" (see below). + Static contents for the HTML area. Usually used in lieu of "source" (see below). - HTML source to be inserted at the end of the HTML document. + HTML source to be inserted at the end of the HTML document. - height, in pixels, of the HTML area as a whole. + height, in pixels, of the HTML area as a whole. - Either "static" or "dynamic", and determines whether the HTML area is in-flow ("static") can be positioned and reloaded at-will. + Either "static" or "dynamic", and determines whether the HTML area is in-flow ("static") can be positioned and reloaded at-will. - HTML source to be inserted at the beginning of the HTML document. + HTML source to be inserted at the beginning of the HTML document. - The objectsystem path or URL containing the document to be loaded into the HTML as local server. + The object system path or URL containing the document to be loaded into the HTML as local server. - Width, in pixels, of the HTML area as a whole. + Width, in pixels, of the HTML area as a whole. - Dynamic HTML areas, the x coordinate on the container of its upper left corner. + Dynamic HTML areas, the x coordinate on the container of its upper left corner. - Dynamic HTML areas, the y coordinate on the container of its upper left corner. + Dynamic HTML areas, the y coordinate on the container of its upper left corner. - - - - - Loadpage action takes two parameters. "Source" contains the URL for the new page to be loaded into the HTML area.The optional parameter "Transition" indicates the type of fade to be used between one page and the next.Currently supported values are "pixelate", "rlwipe", and "lrwipe". - - - - + This event occurs when the user presses the mouse button on the checkbox. This differs from the 'Click' event in that the user must actually press and release the mouse button on the checkbox for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. This event occurs when the user moves the mouse pointer while it is over the checkbox. The event will repeatedly fire each time the pointer moves. @@ -2073,9 +2161,21 @@ f_trx_mod "widget/checkbox" + + + Loads a new page into the HTML widget using two parameters. The "Source" parameter specifies the URL for the new page to be loaded into the HTML area and the optional "Mode" parameter can specify a new value for the mode property. The optional parameter "Transition" indicates the type of fade to be used between one page and the next. This currently supports: rlwipe, lrwipe, and pixelate. + + Appends content from the specified "Text" string parameter to the end of the content in an HTML widget. If the "ContentType" string parameter is specified with a value that differs from the current content type, all HTML content in the widget is overwritten. + + Replaces the current HTML content with the specified "Value" (or clears it if no Value is specified), and sets the current content type using the "ContentType" string parameter, if it is specified (otherwise, the previous content type is preserved). + + Causes plain text content in the widget to be escaped properly so it can be shown using HTML, protecting from cross-site scripting attacks. This action takes no parameters. + + + - + ]]>
- + + @@ -2116,8 +2217,8 @@ HTMLArea "widget/html" - - Determines wither the image can be stretched or must stay its original aspect ration. + + Specifies whether the image can be stretched or must stay its original aspect ratio. If using a form for the image path, the name of the column in the datasource you want to reference. @@ -2126,7 +2227,7 @@ HTMLArea "widget/html" Height, in pixels, of the image. The words that come up if the image object cannot find its source and display an image. - + Width, in pixels, of the image. X-coordinate of the upper left corner of the image, relative to its container. @@ -2134,10 +2235,22 @@ HTMLArea "widget/html" Y-coordinate of the upper left corner of the image, relative to its container. - + + + + Determines how much the image grows or shrinks compared to the original. + + An OSML pathname for the location of the image (such as a png or jpg file). + + Distance it is along the x-axis from its parent widget. + + Distance it is along the y-axis from its parent widget. + + + - This event occurs when the user clicks the checkbox. No parameters are available from this event. + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. This event occurs when the user presses the mouse button on the checkbox. This differs from the 'Click' event in that the user must actually press and release the mouse button on the checkbox for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. @@ -2150,42 +2263,27 @@ HTMLArea "widget/html" This event occurs when the user releases the mouse button on the checkbox. - - - - Determines how much the image grows or shrinks compared to the original. - - An OSML pathname for the location of the image (such as a png or jpg file). - Distance it is along the x axis from its parent widget. - - Distance it is along the y axis from its parent widget. - - - - - Displays the image to user. - + + Displays the new image from the provided "Source" path to user. Specify 1 or yes for the "LinkApp" parameter to pass the full akey session link on in the url request. + Reloads the image with new offset values. - + Reloads the image with a new scale value. - + - - - -

The ImageButton widget provides a clickable button that is comprised of a set of two or three images.The first image is shown normally when the button is idle, the second when the button is pointed-to, and the third image is shown when the button is actually clicked. This provides a "tri-state" appearance much like that provided by buttons in modern user interfaces, although the button can be two-state, with just an "unclicked" and "clicked" version of the image.

+ -

The images are automatically swapped out when the appropriate mouse events occur on the button.

+ -

ImageButtons can also be disabled, and a "disabled" image is displayed at that time.

+

Deprecated. This name is rendered by widget/button and is kept only so that existing applications keep working. New applications should use widget/button, which documents the properties, events, actions, and client properties that an imagebutton accepts.

-

Unlike the textbutton, there is no 'tristate' property for an imagebutton; to make an imagebutton tri-state (different image when idle vs. when pointed to), use a 'pointimage', otherwise do not specify 'pointimage'.

+

An imagebutton is a widget/button whose 'type' defaults to 'image'. Thus, this button is borderless and text-free, its appearance comes from the image. The properties for widget/button apply here, so an existing imagebutton can be given 'text', a 'border_style', or an explicit 'type' without being renamed (although it should be).

@@ -2195,67 +2293,11 @@ HTMLArea "widget/html" - - - The ObjectSystem pathname of the image to be shown when the user clicks the imagebutton. Defaults to 'image' if not specified. - - The ObjectSystem pathname of the image to be shown when the imagebutton is disabled. Defaults to 'image' if not specified. - - Height, in pixels, of the image button. - - The pathname of the image to be shown when the button is "idle". - - The pathname of the image to be shown when the button is pointed-to. Defaults to the 'image' if not specified. - - Whether to repeat the click event multiple times while the user holds down the button. - - The text that appears when the courser hovers over the image button. - - Width, in pixels, of the image button. - - X-coordinate of the upper left corner of the button, relative to its container. - - Y-coordinate of the upper left corner of the button, relative to its container. - - - - - - - - This event occurs when the user clicks the button. No parameters are available from this event. - - This event occurs when the user presses the mouse button on the checkbox. This differs from the 'Click' event in that the user must actually press and release the mouse button on the checkbox for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. - - This event occurs when the user moves the mouse pointer while it is over the checkbox. The event will repeatedly fire each time the pointer moves. - - This event occurs when the user moves the mouse pointer off of the checkbox. - - This event occurs when the user first moves the mouse pointer over the checkbox. It will not occur again until the user moves the mouse off of the checkbox and then back over it again. - - This event occurs when the user releases the mouse button on the checkbox. - - - - - - This action causes the image button to enter its 'disabled' state, displaying the 'disabledimage' if specified. - - This action causes the image button to enter its 'enabled' state, possibly changing its appearance if a 'disabledimage' was explicitly specified. - - - - - - Whether or not the imagebutton should be enabled. Defaults to 'yes'. Can be an expression that if uses runclient() will cause the enabled status of the imagebutton to 'follow' the value of that expression while the application is running. - - + - + ]]>
+ @@ -2297,7 +2340,7 @@ MyButton "widget/imagebutton" -

The label widget can be placed inside of any visual container, and will attach itself to any form widget that contains it (whether directly or indirectly). Labels may not contain visual widgets.

+

The label widget can be placed inside any visual container. It will attach itself to any form widget that contains it (whether directly or indirectly). Labels may not contain visual widgets.

@@ -2306,7 +2349,7 @@ MyButton "widget/imagebutton" Describes how the text should align in the label (e.g. right). It determines if the text can wrap around something. - + The color (named or #numeric) of the text in the label when the user clicks on the label. The color (named or #numeric) of the text in the label. @@ -2318,17 +2361,17 @@ MyButton "widget/imagebutton" The name of the form that this label is associated with. The height of the label. - + If text is no longer able to display shows an ellipsis. - + The color (named or #numeric) of the text in the label when the user hovers the mouse over the label. - (e.g. bold). + 'bold' for bold text, 'italic' for italic text. The text that the label is to display. The text displayed when the courser hovers over the widget. - + Describes how the text should align vertically within the label (e.g. top, middle, or bottom). The width of the label. @@ -2339,58 +2382,110 @@ MyButton "widget/imagebutton" - + This property allows a runclient() expression to dynamically supply the text to display in the label. + + - This event occurs when the user clicks the checkbox. No parameters are available from this event. + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. This event occurs when the user has modified the data value of the checkbox (clicked or unclicked it). - This event occurs when the user presses the mouse button on the checkbox. This differs from the 'Click' event in that the user must actually press and release the mouse button on the checkbox for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. + This event occurs when the user presses the mouse button on the checkbox. This differs from the 'Click' event in that the user must actually press and release the mouse button on the checkbox for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. + + This event occurs when the user moves the mouse pointer while it is over the checkbox. The event will repeatedly fire each time the pointer moves. + + This event occurs when the user moves the mouse pointer off of the checkbox. + + This event occurs when the user first moves the mouse pointer over the checkbox. It will not occur again until the user moves the mouse off of the checkbox and then back over it again. + + This event occurs when the user releases the mouse button on the checkbox. + + + + + + This sets the value property to the specified "Value". + + + + + + + + + +
+ + + + + + +

The map widget is a visual widget for displaying geographical maps, which are pulled from an object source.

+ +
+ + + +

The map widget is a visual that contains Centrallix objects.

+ +
+ + - This event occurs when the user moves the mouse pointer while it is over the checkbox. The event will repeatedly fire each time the pointer moves. + Width (in pixels) of the map. - This event occurs when the user moves the mouse pointer off of the checkbox. + X-coordinate of the upper left corner of the map (in pixels), relative to its container. - This event occurs when the user first moves the mouse pointer over the checkbox. It will not occur again until the user moves the mouse off of the checkbox and then back over it again. + Y-coordinate of the upper left corner of the map (in pixels), relative to its container. - This event occurs when the user releases the mouse button on the checkbox. + Height (in pixels) of the map. - + A Centrallix object source path that we open to get the objects that will be rendered in the map widget. - + Not implemented yet: Specify 1 to enable something... (default: 0) - Sets the value property to the given parameter. - - + Not implemented yet: Specify 1 to enable something... (default: 0) - + - This property allows a runclient() expression to dynamically supply the text to display in the label. - - + - + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. - This event occurs when the user double-clicks the mouse pointer (equivalent to two Click events occurring on the widget within a required time period) while it is over this widget. -$Version=2$ + The RightClick event occurs when a user clicks on the widget with the relevant button on their mouse or mouse pad. -// Here is a label. + This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. -f_trx_mod_l "widget/label" { width=86; text="User Edit?"; align=right; font_size=16; } + This event occurs when the user moves the mouse pointer while it is over the widget (or content inside the widget). The event will repeatedly fire each time the pointer moves. + This event occurs when the user moves the mouse pointer off of the widget. + This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. - ]]> + This event occurs when the user releases the mouse button on the widget. - +
+ @@ -2405,7 +2500,7 @@ f_trx_mod_l "widget/label" { width=86; text="User Edit?"; align=right; font_size -

Menus can be placed inside of any visual container. However, be aware that the menu will be clipped by its container, so placing them at the top-level can be of an advantage. Menu widgets contain menuitem widgets, which are also described in this section.

+

Menus can be placed inside any visual container. However, be aware that the menu will be clipped by its container, so menus are usually placed inside top level widgets (e.g. page). Menu widgets contain menuitem widgets, which are also described here.

@@ -2420,7 +2515,7 @@ f_trx_mod_l "widget/label" { width=86; text="User Edit?"; align=right; font_size A color, RGB or named, to be used as the menu's background. If neither bgcolor nor background are specified, the menu is transparent. The width of the data elements in the menu. - + Either "horizontal" or "vertical" (default), and determines whether the menu is a drop-down/popup (vertical) or a menubar (horizontal). A color for the menu's text. @@ -2430,15 +2525,15 @@ f_trx_mod_l "widget/label" { width=86; text="User Edit?"; align=right; font_size A background image for a menu item that is highlighted (pointed at). A color, RGB or named, to be used as a highlighted (pointed-at) item's background. If neither highlight_bgcolor nor highlight_background are specified, the standard color or background is used instead. - - Default "no". Popup menus disappear after an item on them is selected, whereas fixed menus remain visible (such as for menubars). + + Default "no". Popup menus disappear after an item on them is selected, whereas fixed menus remain visible (such as for menu bars). Height, in pixels, of the menu items in a menu. - + Determines how far the shadow translates from the menu. - + Describes in a ratio how much larger or smaller the size of the shadow is compared to the window. - + Width, in pixels, of the menu. For menus with a direction of 'vertical', an unspecified width is determined dynamically based on the menu contents. X-coordinate of the upper left corner of the menu, relative to its container. @@ -2454,58 +2549,62 @@ f_trx_mod_l "widget/label" { width=86; text="User Edit?"; align=right; font_size Optionally, a checkbox can be displayed to the left of the menu item when 'checked' is specified. In this case, the 'value' can also be a runclient() expression that controls whether the menu item is checked. Optionally, the pathname of an image file to display to the left of the menu item. - + The text to appear on the menu item. - If set to "yes", then the menu item will be displayed on the righthand side of a horizontal menu bar (e.g., for having "File" "Edit" "Tools" on the left, and "Help" on the far right). - + If set to "yes", then the menu item will be displayed on the right hand side of a horizontal menu bar (e.g., for having "File" "Edit" "Tools" on the left, and "Help" on the far right). + The 'value' of the menu item, passed to the Selected event, below. If not specified, it defaults to the name of the widget (not its label). - - - + The text to appear on the menu title. - + - This event occurs when the an item in the menu which is not a submenu is changed. - - This event occurs when the menu which previously was not clicked is clicked. - - This event occurs when the menu which previously was clicked is unselected. - - This event occurs when the user presses the mouse button on the checkbox. This differs from the 'Click' event in that the user must actually press and release the mouse button on the checkbox for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. + This event occurs when an item in the menu which is not a submenu is changed. - This event occurs when the user moves the mouse pointer while it is over the checkbox. The event will repeatedly fire each time the pointer moves. + This event occurs when the menu which previously was not clicked is clicked. - This event occurs when the user moves the mouse pointer off of the checkbox. + This event occurs when the menu which previously was clicked is unselected. - This event occurs when the user first moves the mouse pointer over the checkbox. It will not occur again until the user moves the mouse off of the checkbox and then back over it again. + This event occurs when the user presses the mouse button on the checkbox. This differs from the 'Click' event in that the user must actually press and release the mouse button on the checkbox for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. - This event occurs when the user releases the mouse button on the checkbox. + This event occurs when the user moves the mouse pointer while it is over the checkbox. The event will repeatedly fire each time the pointer moves. + + This event occurs when the user moves the mouse pointer off of the checkbox. + + This event occurs when the user first moves the mouse pointer over the checkbox. It will not occur again until the user moves the mouse off of the checkbox and then back over it again. + + This event occurs when the user releases the mouse button on the checkbox. + + This event fires when a menu item is selected. It can be placed in the menu itself, or inside the menu item widget. When on a menu item, it only fires when that item is selected. When on a menu, it passes the selected item's value as a parameter named 'Item' (string). This event also provides the "Value" and "Label" parameters to represent what was selected. - This event fires when a menu item is selected. It can be placed in the menu itself, or inside the menu item widget. When on a menu item, it only fires when that item is selected. When on a menu, it passes the selected item's value as a parameter named 'Item' (string). + This event occurs when an item in the menu is selected. It provides the same parameters as the Select event. + + This event occurs when the dropdown appears. It provides two parameters, "X" and "Y", which are the (integer) positions (in pixels) on the page for the menu has appeared. + + This event occurs when the dropdown disappears, and it provides no parameters. - This action causes a popup-type menu to become visible and appear at a selected (x,y) position on the page. When the user selects an item on the menu or clicks elsewhere on the page, the menu then disappears. Takes two parameters - X and Y, the (integer) positions on the page for the menu to appear. + This action causes a popup-type menu to become visible and appear at a selected (x,y) position on the page. When the user selects an item on the menu or clicks elsewhere on the page, the menu then disappears. Takes two parameters - "X" and "Y", which are the (integer) positions (in pixels) on the page for the menu to appear. - + ]]>
+ + + + + +

The objcanvas widget is a visual widget for displaying advanced canvas graphics.

+ +
+ + + +

The objcanvas widget is a visual that contains Centrallix objects. I honestly don't really understand how to use it or what it does (-Israel).

+ +
+ + + + Width (in pixels) of the objcanvas. + + X-coordinate of the upper left corner of the objcanvas (in pixels), relative to its container. + + Y-coordinate of the upper left corner of the objcanvas (in pixels), relative to its container. + + Height (in pixels) of the objcanvas. + + A Centrallix object source path that we open to get the objects that will be rendered in the objcanvas widget. + + Not implemented yet: Specify 1 to enable something... (default: 0) + + Not implemented yet: Specify 1 to enable something... (default: 0) + + + + + + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. + + This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. + + This event occurs when the user moves the mouse pointer while it is over the widget (or content inside the widget). The event will repeatedly fire each time the pointer moves. + + This event occurs when the user moves the mouse pointer off of the widget. + + This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. + + This event occurs when the user releases the mouse button on the widget. + + + +
+ + -

The objectsource (osrc) widget lies at the core of Centrallix's ability to dynamically exchange data between the server and client. This widget implements a form of "replication" by maintaining a replica of a small segment of data in the user agent.

+

The objectsource (osrc) widget lies at the core of Centrallix's ability to dynamically exchange data between the server and client. This widget implements a form of "replication" by maintaining a replica of a small segment of data in the user agent.

-

Both form and dynamic table widgets interact with the objectsource nonvisual widget to acquire data, update data, create data, and delete data. In fact, it is possible for more than one form and/or table to be connected with a given objectsource, to perform a variety of functions.

+

Both form and dynamic table widgets interact with the objectsource nonvisual widget to acquire data, update data, create data, and delete data. In fact, it is possible for more than one form and/or table to be connected with a given objectsource, to perform a variety of functions.

-

Objectsources offer synchronization with other objectsources via the Sync and DoubleSync actions (see below) or rule-based connnectivity (see widget/rule). These actions allow the application to contain multiple objectsources with primary key / foreign key relationships, and to have those objectsources automatically stay in synchronization with each other based on those relationships.

+

Object sources offer synchronization with other object sources via the Sync and DoubleSync actions (see below) or rule-based connectivity (see widget/rule). These actions allow the application to contain multiple object sources with primary key / foreign key relationships, and to have those object sources automatically stay in synchronization with each other based on those relationships.

An objectsource may also be used to run a query which does not return any rows, such as an insert, update, or delete query. Under normal data maintenance conditions such queries are not needed as the objectsource handles those operations internally, however.

@@ -2561,27 +2712,27 @@ myMenu "widget/menu" - One of oneachreveal, never, onload, onfirstreveal. (note: this autoquery setting is different from, but related to, the "widget/rule" "autoquery"). "onload" means that the osrc should run its query automatically when the .app containing this osrc is loaded by the user. "onfirstreveal" means that the osrc should run its query automatically when the data (e.g., a table or form) is first displayed to the user (e.g., the containing childwindow becomes visible or tabpage is selected). "oneachreveal" means to do so *each* time the data is displayed to the user. "never" means that the osrc should never query automatically by itself, but it may be triggered by a connector (QueryParam, QueryText, etc.) or by a widget/rule of type osrc_relationship. Important Note: If you expect to normally trigger the osrc via a relationship or via QueryParam, it is often *best* to set autoquery to 'never'. Otherwise, unexpected results can sometimes occur. + One of oneachreveal, never, onload, onfirstreveal. (note: this autoquery setting is different from, but related to, the "widget/rule" "autoquery"). "onload" means that the osrc should run its query automatically when the .app containing this osrc is loaded by the user. "onfirstreveal" means that the osrc should run its query automatically when the data (e.g., a table or form) is first displayed to the user (e.g., the containing childwindow becomes visible or tabpage is selected). "oneachreveal" means to do so *each* time the data is displayed to the user. "never" means that the osrc should never query automatically by itself, but it may be triggered by a connector (QueryParam, QueryText, etc.) or by a widget/rule of type osrc_relationship. Important Note: If you expect to normally trigger the osrc via a relationship or via QueryParam, it is often *best* to set autoquery to 'never'. Otherwise, unexpected results can sometimes occur. If inserts and deletes are to function, the ObjectSystem pathname in which those inserts and deletes should occur. This should be one of the objects specified in the FROM clause of the SQL statement. Set of search parameters. - + Whether normal query activity on this objectsource indicates to the server that the user is active. Some objectsources (in conjunction with a widget/timer) regularly refresh their data; for those, indicates_activity can be set to "no" so that those refreshes don't cause a user's session to never time-out. The name of the object in the SQL query that contains the primary key for the query. Represents the number to fetch from the server when more records are needed from the server (such as when a form requests that the Current Record be a record beyond the end of the current replica contents). - ** This feature currently disabled in Centrallix 0.9.1 ** Default "no". If set to "yes", the objectsource will ask the server to send it updates on any changes that occur on the server side (i.e., if the changes were made by another objectsource or by another user, they would be automatically refreshed into this objectsource in near real-time). + ** This feature currently disabled in Centrallix 0.9.1 ** Default "no". If set to "yes", the objectsource will ask the server to send it updates on any changes that occur on the server side (i.e., if the changes were made by another objectsource or by another user, they would be automatically refreshed into this objectsource in near real-time). + + The time in milliseconds between the data refreshing, if set to 0 it does not automatically refresh. - The time between the data refreshing, if set to 0 it does not automatically refresh. - Represents the number of records to store in its replica. This value should be larger than the maximum number of records that will be displayed at any one time. At times, Centrallix may increase the number of records cached on the client beyond this number. - Acts as a boolean and delays query until the osrc is visable (if "true"). + Acts as a boolean and delays query until the osrc is visible (if "true"). - Similar to readahead, but relates to how many records should be fetched when more records are needed from the server to fulfill a request to display more data, such as a table widget scrolling without changing the current record. By default this is set to the value of 'readahead'. + Similar to readahead, but relates to how many records should be fetched when more records are needed from the server to fulfill a request to display more data, such as a table widget scrolling without changing the current record. Defaults to the value of 'readahead'. Default "yes". If set to "no", the objectsource will not relay any updates (modifications, etc.) that the user makes to the server. @@ -2595,7 +2746,7 @@ myMenu "widget/menu" - When autoquery is set to true, when the master changes, the slave automatially requeries (otherwise have to explicitly call requery or refresh on the slave osrc). When autoquery is false, it causes relationships to be in enforced, but doesn't cause a re-query when the master's osrc refreshes / requeries. + When autoquery is set to true, when the master changes, the slave automatically re-queries its data. Otherwise, it will not re-query unless an action is explicitly called to do so. When autoquery is false, it causes relationships to be in enforced, but doesn't cause a re-query when the master's osrc refreshes or re-queries. Defaults to 'yes'. If it is set to 'no', then this osrc is set up to be a master of the target, otherwise the default is for it to be a slave of the target. @@ -2607,7 +2758,7 @@ myMenu "widget/menu" The target objectsource that this objectsource will be related to. - The field names in the target objectsource to be used for the key value for the relationship (where # is an integer 1 through 5). These keys can be target_key_1 through target_key_5. + The field names in the target objectsource to be used for the key value for the relationship (where # is an integer 1 through 5). These keys can be target_key_1 through target_key_5. @@ -2628,28 +2779,39 @@ myMenu "widget/menu" This event occurs when a query is opened and the query ID is not null. - + + This event occurs once the SaveClients action has saved every client widget of this objectsource, and of those clients' own clients. + This event occurs when data is created in the object source. - + This event is invoked whenever the current record changes to a different record. + This event occurs when a record created or modified through this objectsource has been saved on the server. It is invoked alongside Created or Modified, but only when the save was not requested by a client widget. + + This event occurs once an object has been deleted through this objectsource. + This event is invoked when a query is completed and the last row(s) retrieved. + This event occurs when a FindObject search locates a record and makes it the current record. A search by record number passes 'ID', and a search by object name passes 'Name'. A search by other field values passes the name and value of each field that matched. + This event occurs when data is modified in the object source. - + + This event occurs when a FindObject search does not locate a matching record. It passes the values that were given to FindObject. + + This event occurs each time a batch of query results has been retrieved. It passes 'FirstRecord', 'LastRecord', and 'CurrentRecord', which are record numbers within the replica, and 'FinalRecord', which is the record number of the last record in the query once the end of the query has been reached. + This event occurs when replicant entries are swapped. - - + - Creates a base of an object and notifies all childeren that a child is creating an object. - + Creates a base of an object and notifies all children that a child is creating an object. + If it is called while an object is being created it stops the creation and cleans up the mess. - - Changes the data item that the object source points to in the server. - + + Changes the data item that the object source points to in the server. The 'Source' parameter is the new object path, which replaces the old path everywhere it occurs in the query. The query is then re-run, unless the 'Refresh' parameter is given a false value. + Clears the replica of data. Encapsulates data in an array and calls CreateObject. @@ -2658,14 +2820,20 @@ myMenu "widget/menu" Deletes an object through OSML. - Stops relations with all clients and forces them to resync. + Discards the unsaved changes in all client widgets of this objectsource, then passes the request on to the clients of those clients. + + Runs the SQL statement given in the 'SQL' parameter once for each distinct value of the field named by the 'GroupingField' parameter in the retrieved records. Any other parameters are passed to the statement as query parameters. - DEPRECATED: Performs a double synchronization with two other objectsources, known as the Parent and the Child, in two steps. The first step is like Sync (see below), with a ParentOSRC and ParentKey1-ParentKey9] / ParentSelfKey1-ParentSelfKey9. Next, a Sync is performed between the current objectsource and the ChildOSRC in the same way the first step performed a sync between the ParentOSRC and the current objectsource, respectively, using SelfChildKey1-SelfChildKey9 / ChildKey1-ChildKey9. + DEPRECATED: Performs a double synchronization with two other object sources, known as the Parent and the Child, in two steps. The first step is like Sync (see below), using 'ParentOSRC' along with the 'ParentKey' and 'ParentSelfKey' parameters, each followed by a digit from 1 to 9 (e.g. ParentKey1 and ParentSelfKey1). Next, a Sync is performed between the current objectsource and the 'ChildOSRC' in the same way the first step performed a sync between the 'ParentOSRC' and the current objectsource, respectively, using the 'SelfChildKey' and 'ChildKey' parameters, numbered in the same way. - Searches for a certain object in the replica (retrieved records), and makes it the current object. Parameters: To search by record number, set ID equal to the integer (1 = first record). To search by object name (primary key), set Name equal to a string containing the primary key (note that concatenated keys use | as a separator). To search by other abitrary field values, set those values in the parameters to this action. + Searches for a certain object in the replica (retrieved records), and makes it the current object. Parameters: To search by record number, set ID equal to the integer (1 = first record). To search by object name (primary key), set Name equal to a string containing the primary key (note that concatenated keys use | as a separator). To search by other arbitrary field values, set those values in the parameters to this action. Returns first record in the replica. + Invokes the action named by 'ForEachAction' on the widget named by 'ForEachTarget' once for each row in the replica. + + Discards the record of this objectsource's last synchronization, so that the next Sync re-runs the query even when the synchronization key values have not changed. + Returns last record in the replica. Modifies an object through OSML. @@ -2682,25 +2850,25 @@ myMenu "widget/menu" Refreshes the query, and allows new parameter values to be passed in. Previous comment: Re-runs the SQL query, but adds more constraints. The additional constraints are provided as parameters to the connector which invokes this action. - Runs the query, searching for objects whose attributes *contain* a combination of string values. 'query' contains a space-separated list of strings that must be present in each returned record (typically the 'query' is typed by the user). 'field_list' is a comma-separated list of field names to search in. Each field name (attribute) can be preceded by a * or followed by a *; the presence of these asterisks controls whether the matching is done on the entire attribute value or just as a substring match. Examples: 'my_key,*my_description*' for field_list means to match exact values for my_key, and match anywhere in my_description. cx__case_insensitive can be set to 1 to make the search case insensitive. + Runs the query, searching for objects whose attributes *contain* a combination of string values. 'query' contains a space-separated list of strings that must be present in each returned record (typically the 'query' is typed by the user). 'field_list' is a comma-separated list of field names to search in. Each field name (attribute) can be preceded by a * or followed by a *; the presence of these asterisks controls whether the matching is done on the entire attribute value or just as a substring match. Examples: 'my_key,*my_description*' for field_list means to match exact values for my_key, and match anywhere in my_description. cx__case_insensitive can be set to 1 to make the search case-insensitive. Re-runs the SQL query using the previously stored parameter values (if any). This is used when the data on the server is believed to have changed, and the new data is desired to be displayed in the application. This action does not change the current row. Re-runs the SQL query for just the one current object (record). This does not change the currently selected record in the osrc. This should be used when the intent is to refresh the values of just the current object without affecting anything else. - DEPRECATED: Performs a synchronization operation with another objectsource by re-running the query for this objectsource based on another objectsource's data. Used for implementing relationships between objectsources. The ParentOSRC (string) parameter specifies the name of the objectsource to sync with. Up to nine synchronization keys can be specified, as ParentKey1 and ChildKey1 through ParentKey9 and ChildKey9. The ParentKey indicates the name of the field in the ParentOSRC to sync with (probably a primary key), and ChildKey indicates the name of the field (the foreign key) in the current objectsource to match with the parent objectsource. + DEPRECATED: Performs a synchronization operation with another objectsource by re-running the query for this objectsource based on another objectsource's data. Used for implementing relationships between objectsources. The 'ParentOSRC' (string) parameter specifies the name of the objectsource to sync with. Up to nine synchronization keys can be specified, as a pair of 'ParentKey' and 'ChildKey' parameters, each followed by a digit from 1 to 9 (e.g. ParentKey1 and ChildKey1). 'ParentKey' indicates the name of the field in the 'ParentOSRC' to sync with (probably a primary key), and 'ChildKey' indicates the name of the field (the foreign key) in the current objectsource to match with the parent objectsource. + + Save the clients that connect and lets clients of clients know that orsc is connected to them. - Save the clients that connect and lets clients of clients know that orsc is conected to them. - Moves the current row backwards. - + Moves the current row forwards. - + - + ]]>
+ -

The page widget represents the HTML application (or subapplication) as a whole and serves as a top-level container for other widgets in the application. The page widget also implements some important functionality regarding the management of keypresses, focus, widget resizing, and event management. When creating an application, the top-level object in the application must either be "widget/page" or "widget/frameset", where the latter is used to create a multi-framed application containing multiple page widgets.

+

The page widget represents the HTML application (or subapplication) as a whole and serves as a top-level container for other widgets in the application. The page widget also implements some important functionality regarding the management of keypresses, focus, widget resizing, and event management. When creating an application, the top-level object in the application must either be "widget/page" or "widget/frameset", where the latter is used to create a multi-framed application containing multiple page widgets.

Page widgets specify the colors for mouse, keyboard, and data focus for the application. Focus is usually indicated via the drawing of a rectangle around a widget or data item, and for a 3D-effect two colors are specified for each type of focus: a color for the top and left of the rectangle, and another color for the right and bottom of the rectangle. Mouse focus gives feedback to the user as to which widget they are pointing at (and thus which one will receive keyboard and/or data focus if the user clicks the mouse). Keyboard focus tells the user which widget will receive data entered via the keyboard. Data focus tells the user which record or data item is selected in a widget.

@@ -2750,17 +2919,17 @@ osrc1 "widget/osrc" -

The page widget cannot be embedded within other widgets on a page. There must only be one per page, unless a frameset is used, in which case page widgets may be added within a frameset widget.

+

The page widget cannot be embedded within other widgets on a page. There must only be one per page, unless a frameset is used, in which case page widgets may be added within a frameset widget.

Determines how close the window is to the browser edge. - + A background image for the page. - The background color for the page. Can either be a recognized color (such as "red"), or an RGB color (such as "#C0C0C0"). + The background color for the page. Can either be a recognized color (such as "red"), or an RGB color (such as "#C0C0C0"). A color, RGB or named, for the top and left edges of rectangles drawn to indicate data focus (default is dark blue). @@ -2775,17 +2944,17 @@ osrc1 "widget/osrc" When the application is rendered over HTTP/HTML, this controls the X-Frame-Options anti-clickjacking HTTP response header. Possible values are "none", "sameorigin", and "deny". The default value is set by the x_frame_options setting in the "net_http" section of centrallix.conf (see configuration docs for more information). The http_frame_options setting only applies to the page widget it is set in, not to any components that the page loads (it can be set in those components separately if needed). The file path to the page icon. - + A color, RGB or named, to be used for the top and left edges of rectangles drawn to indicate keyboard focus (default is "white"). A color, RGB or named, for the bottom and right edges of rectangles drawn to indicate keyboard focus (default is dark grey). - Color for hyper links. - + Color for hyperlinks. + True if page is loaded, false otherwise. - + The maximum amount of server requests at one time. - + A color, RGB or named, to be used for the top and left edges of rectangles drawn to indicate mouse focus (default is "black"). A color, RGB or named, for the bottom and right edges of rectangles drawn to indicate mouse focus (default is "black"). @@ -2803,34 +2972,35 @@ osrc1 "widget/osrc" - - - - + This occurs when the page initializes. - + This occurs when the uses right clicks on a mouse or mouse pad. - - Sends an alert widget. - + + Sends an alert widget. Set the 'Message' to specify a text string that should appear in the alert. + Closes the page. - - Starts a new app in a new window. - - Loads the page. + + Loads the app at the specified "Source" in a new window of the specified "Width" and "Height". Specify 1 or yes for "LinkApp" to link the new app into the same group as the current page for permissions. Specify any value other than no for the "UseragentMenu", "UseragentResize", or "UseragentScroll" parameters to allow the user to see the menu bar, resize the window, or scroll it (respectively). The "Name" parameter names the new window (the default name is new_window); specify 1 or true for "Multi" to open an additional numbered window instead of reusing the window with that name. + + Logs data to the console (using console.log()), for testing and debugging. Set the 'Message' to specify a text string that should appear in the log. Might be useful for logging Easter Egg #8. + + Loads the page at the specified "Source", or reloads the current page if no source is given. Any other parameters are appended to the URL as query parameters. Specify 1 or yes for "LinkApp" to link the loaded page into the same group as the current page for permissions. + + Reloads the page in the user's browser. Note: This event forces a reload, even if the original content could be loaded without one. + + Reloads the page in the user's browser. Note: This event forces a reload, even if the original content could be loaded without one. - + ]]>
+ -

The pane is Centrallix's simplest container. It consists only of a background and a border, which can either have a "raised" edge or "lowered" edge style.

+

The pane is Centrallix's simplest container. It consists only of a background and a border, which can either have a "raised" edge or "lowered" edge style.

@@ -2881,7 +3052,7 @@ MyPage "widget/page" A color, RGB or named, to be used as the pane's background. If neither bgcolor nor background is specified, the pane will be transparent. The radius (sharpness) of the pane, smaller is more sharp. - + For "bordered" pane styles, this is the color of the border, either named or a #number. Height, in pixels, of the pane. @@ -2902,19 +3073,15 @@ MyPage "widget/page" - - - + Indicates if the user can interact with the pane (if so "true"). - + - - This event occurs when the user clicks the checkbox. No parameters are available from this event. + + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. This event occurs when the user presses the mouse button on the checkbox. This differs from the 'Click' event in that the user must actually press and release the mouse button on the checkbox for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. @@ -2931,20 +3098,20 @@ MyPage "widget/page" Creates a triangular pointer on the edge of a pane to point at a given (X,Y) coordinate. - + Changes the width and height of the pane to the given (Width,Height). - - Sets the backgound image or color of the pane, using the attribute Color or Image. - + + Sets the background to the specified "Image" path or "Color" of the pane. + - + ]]>
+ @@ -2984,26 +3152,26 @@ mypane "widget/pane" Parameter widgets are treated as other widgets and normally would appear as widgets in the namespace on the client, with the parameter values being available in runclient() expressions on the client. For efficiency reasons, however, parameters to static components, other than parameters of type "object", are not deployed to the client. To override this behavior, set this option to "yes". - If this is a parameter to a component, and the parameter has type "object", this can be set to a type of widget that should be searched for in the containing application or component once the component is instantiated. Note that the object being searched for must be a container of the component instance, either directly or indirectly. This option is frequently used to link in with a form or objectsource in the contianing application or component, without that form or objectsource having to be explicitly passed to the component. + If this is a parameter to a component, and the parameter has type "object", this can be set to a type of widget that should be searched for in the containing application or component once the component is instantiated. Note that the object being searched for must be a container of the component instance, either directly or indirectly. This option is frequently used to link in with a form or objectsource in the containing application or component, without that form or objectsource having to be explicitly passed to the component. Gives the name of the parameter being passed, thus allowing the parameter widget name (which must be unique within the application or component) to be different from the parameter name as referenced within expressions, SQL, or connectors. - + The data type of the parameter. Can be "integer", "string", "double", "datetime", "money", or "object". - - - This sets the value of the parameter. - - - - + This event occurs when the parameter being passed is changed. - + - + + + + This sets the value property to the specified "Value". + + + + @@ -3072,7 +3241,7 @@ my_cmp "widget/component-decl" -

The radio button panel can be placed inside of any visual container, and will automatically attach itself to a form widget if it is inside of one (directly or indirectly). The "widget/radiobuttonpanel" is the main widget, and can contain any number of "widget/radiobutton" widgets which specify the choices which will be present on the panel. No other visual widgets can be contained within a radio button panel.

+

The radio button panel can be placed inside any visual container, and will automatically attach itself to a form widget if it is inside of one (directly or indirectly). The "widget/radiobuttonpanel" is the main widget, and can contain any number of "widget/radiobutton" widgets which specify the choices which will be present on the panel. No other visual widgets can be contained within a radio button panel.

Note: form widget interaction was not yet implemented as of the time of writing of this document.

@@ -3080,45 +3249,75 @@ my_cmp "widget/component-decl" - Name of the column in the datasource you want to reference. + Name of the column in the datasource you want to reference. + + A background image for the radio button panel. + + A color, RGB or named, for the panel background. If neither bgcolor nor background transparent. - A background image for the radio button panel. + Height, in pixels, of the panel. - A color, RGB or named, for the panel background. If neither bgcolor nor background transparent. + The maximum height (in pixels) of space allowed between radio buttons on the panel (default: 10px). - Height, in pixels, of the panel. - - An image to be used for the rectangular border drawn around the radio buttons. + An image to be used for the rectangular border drawn around the radio buttons. - The color, RGB or named, of the text within the panel. Default: "black". + The color, RGB or named, of the text within the panel. Default: "black". - The title for the radio button panel, which appears superimposed on the rectangular border around the radio buttons. + The title for the radio button panel, which appears superimposed on the rectangular border around the radio buttons. - Width, in pixels, of the panel. + Width, in pixels, of the panel. - X-coordinate of the upper left corner of the panel, default is 0. + X-coordinate of the upper left corner of the panel, default is 0. - Y-coordinate of the upper left corner of the panel, default is 0. + Y-coordinate of the upper left corner of the panel, default is 0. - + - The text label to appear beside the radio button. - - the radio button is initially selected or not. Should only be set on one radio Default;"false". + The text label to appear beside the radio button. - The value of the selected item. + the radio button is initially selected or not. Should only be set on one radio Default;"false". - + The value of the selected item. + + + + + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. + + This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. + + This event occurs when the user moves the mouse pointer while it is over the widget (or content inside the widget). The event will repeatedly fire each time the pointer moves. + + This event occurs when the user moves the mouse pointer off of the widget. + + This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. + + This event occurs when the user releases the mouse button on the widget. + + This event occurs when a new option is selected in the dropdown widget. It provides the "Value" parameter, which indicates the new selected option. + + This event occurs when the parent pane gains the user's focus. + + This event occurs when the parent pane looses the user's focus. + + + + + + Sets the selected option for the radio button panel to the new option specified with the "Value" parameter, or unsets the dropdown if no value is specified. + + + - + ]]>
+ -

The remote control nonvisual widget allows for one application (or instance of an application) to activate Actions in another running application, even if those applications are on two separate client computer systems. This is done by passing the event/action information through a remote control channel on the server.

+

The remote control nonvisual widget allows for one application (or instance of an application) to activate Actions in another running application, even if those applications are on two separate client computer systems. This is done by passing the event/action information through a remote control channel on the server.

-

Two remote control widgets are required: a master and slave. This widget is the slave widget, which receives remote control events via the Centrallix server. When a master widget (remotemgr) sends an event through the channel, this slave widget is automatically activated and can then trigger the appropriate action on another widget on the page.

+

Two remote control widgets are required: a master and slave. This widget is the slave widget, which receives remote control events via the Centrallix server. When a master widget (remotemgr) sends an event through the channel, this slave widget is automatically activated and can then trigger the appropriate action on another widget on the page.

In order for the remote control event to be passed through Centrallix, the master and slave widgets must both be using the same channel id and be logged in with the same username.They need not be a part of the same session on the server.

@@ -3168,23 +3368,18 @@ testradio "widget/radiobuttonpanel" - - - - - +
+ -

The remote control manager nonvisual widget allows for one application (or instance of an application) to activate Actions in another running application, even if those applications are on two separate client computer systems. This is done by passing the event/action information through a remote control channel on the server.

+

The remote control manager nonvisual widget allows for one application (or instance of an application) to activate Actions in another running application, even if those applications are on two separate client computer systems. This is done by passing the event/action information through a remote control channel on the server.

-

Two remote control widgets are required: a master and slave. This widget is the master widget, which sends remote control events via the Centrallix server. When a this widget sends an event through the channel, the slave widget (remotectl) is automatically activated and can then trigger the appropriate action on another widget on the remote application's page.

+

Two remote control widgets are required: a master and slave. This widget is the master widget, which sends remote control events via the Centrallix server. When this widget sends an event through the channel, the slave widget (remotectl) is automatically activated and can then trigger the appropriate action on another widget on the remote application's page.

In order for the remote control event to be passed through Centrallix, the master and slave widgets must both be using the same channel id and be logged in with the same username.They need not be a part of the same session on the server.

@@ -3198,41 +3393,36 @@ testradio "widget/radiobuttonpanel" - - - - - +
+ - + -

The 'repeat' nonvisual widget is used to repeat its entire subtree of widgets for each record in an sql query.

+

The 'repeat' nonvisual widget is used to repeat its entire subtree of widgets for each record in an sql query.

-
+
- + -

This widget has no content of its own, so it is only useful if it has widgets inside it. For positioning of visual widgets inside a widget/repeat, an hbox or vbox (outside the widget/repeat) can be used, or the x and y can be set mathematically based on results from the SQL query.

+

This widget has no content of its own, so it is only useful if it has widgets inside it. For positioning of visual widgets inside a widget/repeat, an hbox or vbox (outside the widget/repeat) can be used, or the x and y can be set mathematically based on results from the SQL query.

-

The widget/repeat can be useful in creating data-driven user interfaces, as well as in facilitating a plug-in architecture in your application. For instance, the SQL query could retrieve a list of matching components to be included in an interface, and the repeat widget could create components, tabs, windows, table columns, buttons, etc., for each returned SQL query record.

+

The widget/repeat can be useful in creating data-driven user interfaces, as well as in facilitating a plug-in architecture in your application. For instance, the SQL query could retrieve a list of matching components to be included in an interface, and the repeat widget could create components, tabs, windows, table columns, buttons, etc., for each returned SQL query record.

-
+
- + The sql query that will be run on the server. - + - + - + ]]> - +
+ @@ -3294,11 +3485,12 @@ $Version=2$ + -

The scrollbar is used to allow the uesr to control a numeric value; typically the scrollbar is tied to the scrolling behavior of another widget.

+

The scrollbar is used to allow the user to control a numeric value; typically the scrollbar is tied to the scrolling behavior of another widget.

Currently, both table and scrollpane widgets have their own scrollbars, so this widget is not used for either of those.

@@ -3312,53 +3504,56 @@ $Version=2$ - A background image to be placed behind the scrollbar. + A background image to be placed behind the scrollbar. - A color, RGB or named, to be used as the scrollbar background. If neither bgcolor nor background is supplied, the scrollbar is transparent. + A color, RGB or named, to be used as the scrollbar background. If neither bgcolor nor background is supplied, the scrollbar is transparent. - Either "horizontal" or "vertical" indicating the visible direction that the scrollbar is oriented. + Either "horizontal" or "vertical" indicating the visible direction that the scrollbar is oriented. - Height, in pixels, of the scrollbar. + Height, in pixels, of the scrollbar. - The upper limit of the range of the scrollbar's value. The value will range from 0 to the number specified by 'range'. This property can be set to a runclient() dynamic expression, in which case the range will change as the expression changes. + The upper limit of the range of the scrollbar's value. The value will range from 0 to the number specified by 'range'. This property can be set to a runclient() dynamic expression, in which case the range will change as the expression changes. This acts as a boolean to denote if the scroll bar is visible ("true" is visible). - - Width, in pixels, of the scrollbar. - X-coordinate of the upper left corner of the scrollbar, relative to its container. + Width, in pixels, of the scrollbar. - Y-coordinate of the upper left corner of the scrollbar, relative to its container. + X-coordinate of the upper left corner of the scrollbar, relative to its container. + + Y-coordinate of the upper left corner of the scrollbar, relative to its container. - - - - Sets the scroll bar to a specific location determined by the parameter. - - - + - + + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. + This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. This event occurs when the user moves the mouse pointer while it is over the widget. The event will repeatedly fire each time the pointer moves. This event occurs when the user moves the mouse pointer off of the widget. - + This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. This event occurs when the user releases the mouse button on the widget. + + + Sets the scrollbar to a specific location determined by the "Value" parameter. + + +
+ -

The scrollpane widget provides a container and a scrollbar. The scrollbar can be used to move up and down in the container, so more content can be placed in the container than can be normally viewed at one time.

+

The scrollpane widget provides a container and a scrollbar. The scrollbar can be used to move up and down in the container, so more content can be placed in the container than can be normally viewed at one time.

The scrollbar includes a draggable thumb as well as up and down arrows at the top and bottom. Clicking the arrows scrolls the content of the container up or down by a small amount, whereas clicking on the scrollbar itself above or below the thumb will scroll the area by a large amount.

@@ -3372,46 +3567,53 @@ $Version=2$ - A background image to be placed behind the scrollpane. + A background image to be placed behind the scrollpane. - A color, RGB or named, to be used as the scrollpane background.If neither bgcolor transparent. + A color, RGB or named, to be used as the scrollpane background.If neither bgcolor transparent. - height, in pixels, of the scrollpane's visible area. Due to the nature of the can time. + height, in pixels, of the scrollpane's visible area. Due to the nature of the can time. - allows user to set scroll pane to visible (true) or not (false). + allows user to set scroll pane to visible (true) or not (false). - width, in pixels, of the scrollpane, including the scrollbar area on the right side. + width, in pixels, of the scrollpane, including the scrollbar area on the right side. - x-coordinate of the upper left corner of the scrollpane, relative to its container. + x-coordinate of the upper left corner of the scrollpane, relative to its container. - y-coordinate of the upper left corner of the scrollpane, relative to its container. + y-coordinate of the upper left corner of the scrollpane, relative to its container. - - - - Scrolls to a specific location determined by the scroll bar. - - - + - + + This event occurs any time the user scrolls the scroll pane. This includes scrolling by clicking the scroll buttons, clicking on the scroll bar, dragging the scroll thumb, turning the scroll wheel, or when the ScrollTo action is used. This event does not occur when the scroll pane moves because the contained content changed in length, or when the scroll pane is forced to scroll because the available visible area was resized. This event will never occur if the content within the scroll pane is shorter than the available visible area because then the content cannot be scrolled. This event provides the :Percent attribute, a number from 0 to 100 (the same as the ScrollTo action above) representing the percentage that the user has now scrolled down the page as of the event occurring. This event also provides :Change, representing how much the user's scroll location has changed in the same unit as above (although this value will be negative if the user scrolled up). + + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. + + This event occurs when the user moves the scroll wheel while it is over the widget (or content inside the widget). The event will repeatedly fire each time the pointer moves. + This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. - This event occurs when the user moves the mouse pointer while it is over the widget. The event will repeatedly fire each time the pointer moves. + This event occurs when the user moves the mouse pointer while it is over the widget (or content inside the widget). The event will repeatedly fire each time the pointer moves. This event occurs when the user moves the mouse pointer off of the widget. - + This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. This event occurs when the user releases the mouse button on the widget. + The Click, Wheel, MouseDown, and MouseUp events provide several pieces of useful information, including :shiftKey, :ctrlKey, :altKey, and :metaKey, which are 1 if the respective key is held down and 0 otherwise. These events also provide :button, a number representing the button number that the user used to execute the event (which appears to always be 0 for wheel). + - - + + + + Scrolls to a specific location determined by the scroll bar. Specify the 'Percent' attribute to indicate how far to scroll in decimal representation (so 1.00 is 100%, aka. the bottom of the page). Specify 'Offset' how many pixels the content should be offset from the top (specify 100 to scroll the first 100 px of content off the top of the scroll pane). Specify 'RangeStart' and 'RangeEnd' to scroll to within the pixel range (using the same units as offset). Keep in mind that this action will trigger a scroll event to occur. + + + - + ]]>
+ -

The TabControl widget provides a DHTML tab control within Centrallix. The widget behaves in the same way as tab controls in other GUI environments, providing a set of tab pages, layered one on top of the other, which can be selected (brought to the foreground) by clicking the mouse on the respective visible tab at the top of the tab control.

+

The TabControl widget provides a DHTML tab control within Centrallix. The widget behaves in the same way as tab controls in other GUI environments, providing a set of tab pages, layered one on top of the other, which can be selected (brought to the foreground) by clicking the mouse on the respective visible tab at the top of the tab control.

To further distinguish which tab at the top of the tab control is active, this widget slightly modifies the X/Y position of the tab as well as changing a thumbnail image (on the left edge of the tab) to further enhance the distinction between selected and inactive tab pages.

@@ -3467,7 +3670,7 @@ MyScrollPane "widget/scrollpane"

The tab pages are containers, and as such, controls of various kinds, including other tab controls, can be placed inside the tab pages.

-

Tab pages are added to a tab control by including widgets of type "widget/tabpage" within the "widget/tab" widget in the structure file that defines the application. Any controls to appear inside a particular tab page should be placed inside their respective "widget/tabpage" widgets in the structure file.Only widgets of type "widget/tabpage" should be placed inside a "widget/tab", with the exception of nonvisuals such as connectors.

+

Tab pages are added to a tab control by including widgets of type "widget/tabpage" within the "widget/tab" widget in the structure file that defines the application. Any controls to appear inside a particular tab page should be placed inside their respective "widget/tabpage" widgets in the structure file. Only widgets of type "widget/tabpage" should be placed inside a "widget/tab", except for nonvisual widgets such as connectors.

Tab pages also have a 'visible' property which allows them to be hidden and revealed. This is used if the type is set to dynamic, but can be used manually as well.

@@ -3475,95 +3678,105 @@ MyScrollPane "widget/scrollpane" - An image to be used as the background of the tab control. + An image to be used as the background of the tab control. + + As an alternate to "background", "bgcolor" can specify a color, either named or RGB. - As an alternate to "background", "bgcolor" can specify a color, either named or RGB. - A color that outlines window. - + A radius that describes the sharpness of the corners of the window (smaller means sharper). Determines the look of the outline. - - The height, in pixels, of the tab control, including the page height but not including the height of the tabs at the top. - An image to be used as the background of the tabs which are inactive (in the background). + The height, in pixels, of the tab control, including the page height but not including the height of the tabs at the top. - As an alternate to "inactive_background", "inactive_bgcolor" can specify a color, either named or RGB. + An image to be used as the background of the tabs which are inactive (in the background). + + As an alternate to "inactive_background", "inactive_bgcolor" can specify a color, either named or RGB. The placement of the shadow described as a rotational transformation with respect to the window. - + The color of the shadow. The placement of the shadow with respect to the window. A radius that describes the sharpness of the corners of the shadow (smaller means sharper). - The location of the tabs: "top" (default), "bottom", "left", "right", or "none". + The location of the tabs: "top" (default), "bottom", "left", "right", or "none". + + The width of the tabs in pixels. This is optional for tab_locations of "top", "bottom", and "none". + + The height of the tabs in pixels. This is optional for all tab_locations. Defaults to 24px. + + The color of the text to be used on the tabs to identify them. + + allows user to set tab to visible (true) or not (false). + + Width, in pixels, of the tab control, including the page width but not the width of the tabs (if they are at the side). + + X-coordinate of the upper left corner of the tab control, relative to the container. - The width of the tabs in pixels. This is optional for tab_locations of "top", "bottom", and "none". + 'client-side' or 'server-side'. This property is intended for developers (although it can give a very small performance boost). A value of "server-side" turns off JS rendering on the client. This does not work for dynamic width tabs (aka. top or bottom tabs with no 'tab_width' property). Defaults to "client-side". - The color of the text to be used on the tabs to identify them. - - allows user to set tab to visible (true) or not (false). + The amount to translate a selected tab along the side of the tab control. Defaults to 0px. - Width, in pixels, of the tab control, including the page width but not the width of the tabs (if they are at the side). + The amount to translate a selected tab out and away from the side of the tab control. Defaults to 2px. - X-coordinate of the upper left corner of the tab control, relative to the container. + The amount to translate a selected tab in the x direction. If set, overrides the value derived from select_translate_along and/or select_translate_out. - Y-coordinate of the upper left corner of the control, relative to its container. + The amount to translate a selected tab in the y direction. If set, overrides the value derived from select_translate_along and/or select_translate_out. + + + The name of the tab page that should be initially selected. This can also contain a dynamic runclient() expression controlling which tab page is selected. + + Similar to "selected", but selects the tab by numeric index. + + + - + - This is the fieldname from the objectsource that the tabpage will use for its title. + This is the fieldname from the objectsource that the tabpage will use for its title. - The name of the tab page, which appears in the tab at the top of the control. + The name of the tab page, which appears in the tab at the top of the control. - If this is set to dynamic then the tabpage will act as an objectsource client, displaying zero or more tabs: one tab for each item in the replica with title set to the value of the fieldname. In this way, a tab control can have a mix of dynamic tabs and static ones. + If this is set to dynamic then the tabpage will act as an objectsource client, displaying zero or more tabs: one tab for each item in the replica with title set to the value of the fieldname. In this way, a tab control can have a mix of dynamic tabs and static ones. - 0 or 1. Can contain a dynamic runclient() expression to control when the tab page is visible to the user. + 0 or 1. Can contain a dynamic runclient() expression to control when the tab page is visible to the user. - + - This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. + This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. - This event occurs when the user moves the mouse pointer while it is over the widget. The event will repeatedly fire each time the pointer moves. + This event occurs when the user moves the mouse pointer while it is over the widget. The event will repeatedly fire each time the pointer moves. - This event occurs when the user moves the mouse pointer off of the widget. + This event occurs when the user moves the mouse pointer off of the widget. - This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. + This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. - This event occurs when the user releases the mouse button on the widget. + This event occurs when the user releases the mouse button on the widget. This event occurs when the visible tab changes. - + - - - - The name of the tab page that should be initially selected. This can also contain a dynamic runclient() expression controlling which tab page is selected. - Similar to "selected", but selects the tab by numeric index. - - - - - Sets the selected tab according to the parameter given (the tab itself or its index). - + + Sets the selected tab to the tab with the index specified by the "TabIndex" parameter, or the tab with the name specified by the "Tab" parameter. ("Tab" takes priority if both are specified. Does nothing if neither is specified.) + - + ]]>
+ -

A table widget is used to display data in a tabular format. It consists of a header row with column labels, followed by any number of rows containing data.The header may have a different color or image scheme than the rows, and the rows may or may not be configured to alternate between two colors or background images.

+

A table widget is used to display data in a tabular format. It consists of a header row with column labels, followed by any number of rows containing data. The header may have a different color or image scheme than the rows, and the rows may or may not be configured to alternate between two colors or background images.

- Table widgets come in three different flavors: static, dynamicpage, and dynamicrow.Static table widgets are built on the server and write their data directly into the container in which they reside, which is usually a scrollpane widget. Dynamicpage table widgets load their data once they initialize on the client, by activating a query through an ObjectSource nonvisual widget.Dynamicpage table widgets do not support modification, but can be reloaded through an ObjectSource at will.Dynamicrow table widgets, on the other hand, display each row as an individual layer, and thus are modifiable on the client. Dynamicrow table widgets also load their contents through an ObjectSource widget query.As of the time of writing of this document, only static mode and dynamicrow mode were supported. +

Table widgets come in three different flavors: static, dynamicpage, and dynamicrow. Static table widgets are built on the server and write their data directly into the container in which they reside, which is usually a scrollpane widget. Dynamicpage table widgets load their data once they initialize on the client, by activating a query through an ObjectSource nonvisual widget. Dynamicpage table widgets do not support modification, but can be reloaded through an ObjectSource at will. Dynamicrow table widgets, on the other hand, display each row as an individual layer, and thus are modifiable on the client. Dynamicrow table widgets also load their contents through an ObjectSource widget query. As of the time of writing of this document, only static mode and dynamicrow mode were supported.

Table widgets allow the selection (keyboard, mouse, and data focus) of individual rows.

@@ -3605,7 +3819,7 @@ myTabControl "widget/tab" -

Table widgets are normally placed inside of a scrollpane so that any rows which don't fit int the container can still be viewed. Table columns are created via "widget/table-column" child widgets within the table widget.

+

Table widgets are normally placed inside a scrollpane so that any rows which don't fit int the container can still be viewed. Table columns are created via "widget/table-column" child widgets within the table widget.

@@ -3625,10 +3839,14 @@ myTabControl "widget/tab" The width of the column separation lines in pixels. Default is 1. + Either 'full' or 'header'. Default is 'full'. + + Either 'full' or 'header'. Default is 'full'. + Either "rows" (default) or "properties". In "properties" mode, the table displays one row per attribute, and so only displays the current record in the objectsource. In "rows" mode, the table displays one row per record in the objectsource. Acts as a boolean to only show the scrollbar when it is needed (activates when it is set to "1"). - + Whether to allow dragging of column boundaries to resize columns; set to 1 to allow it and 0 to disallow. Default is 1. Set to 'yes' to cause the table's current row to follow the currently selected object in the ObjectSource, and 'no' to disable this behavior. Default is 'yes'. @@ -3640,21 +3858,21 @@ myTabControl "widget/tab" A color, RGB or named, for the header row cells. Acts as a boolean to determine if the scrollbar can be seen (hide is "1"). - + The height in pixels of the table. The maximum height of the image from the child. - + The maximum width of the image from the child. - + Sets which data element in the table has focus initially. - - width of the inner spacing between cells in a table. Default0. + + width of the inner spacing between cells in a table. Default0. margins within each cell, in pixels. Default is 0 pixels. - + The maximum height of each row. - + The minimum height of each row. A background image for the "new row" placeholder that is visible when a new object is being created. @@ -3662,13 +3880,13 @@ myTabControl "widget/tab" A color, RGB or named, for the "new row" placeholder that is visible when a new object is being created. Identifies the OSRC that the table connects to. - + width of the outer spacing around the outside of the table, in pixels. Default0. - - Acts as a boolean to allow the scroll bar to overlab with the table (allow is "1"). - + + Acts as a boolean to allow the scroll bar to overlap with the table (allow is "1"). + Acts as a boolean to reverse to order of the table elements (reverses if set to "1"). - + A background image for the table row cells. A color, RGB or named, for the table row cells. @@ -3676,11 +3894,11 @@ myTabControl "widget/tab" A background image for the table row cells. If this is specified, rows will alternate in backgrounds between "row_background1" and "row_background2". A color, RGB or named, for the table row cells. If this is specified, rows will alternate in colors between "row_bgcolor1" and "row_bgcolor2". - + Determines the color of the edge of the rows. - + Determines the sharpness of the row corners, smaller is more sharp. - + How many rows are shown in the table. The height of the individual rows in pixels. Default is 15 pixels. @@ -3690,11 +3908,11 @@ myTabControl "widget/tab" A color, RGB or named, for the current (selected) row cells. The color of the shadow of the row. - + How far the shadow is from the row. - + The sharpness of the corners of the shadow, smaller means more sharp. - + Whether to highlight the currently selected row. Default "yes". A color, RGB or named, of the text in the normal data rows. @@ -3711,9 +3929,9 @@ myTabControl "widget/tab" The maximum number of rows to show at any given time, for dynamic tables. - X-coordinate of the upper left corner of the table. Default is 0. + X-coordinate of the upper left corner of the table. Default is 0. - Y-coordinate of the upper left corner of the table. Default is 0. + Y-coordinate of the upper left corner of the table. Default is 0. @@ -3721,49 +3939,107 @@ myTabControl "widget/tab" - The alignment of the column: "left" or "right". + The alignment of the column: "left", "center", or "right". + + The field name that this column will use to get data. + + Not documented. + + The color of the text displayed in this column. + + The alignment of the caption in the column: "left", "center", or "right". + + The field name that this column will use to get caption data. + + Not documented. + + The color of the captions in this column. + + The font size of thecaption text. + + Not documented. + + Determines if the caption text will wrap around obstacles. Whether clicking this column's header sorts the table by this column. Defaults to the table's "allow_sorting" value (which defaults to "yes"). It is a pseudonym for the fieldname. - + The color of the caption_fieldname. - + Acts as a boolean to determine if items are group able. - + The height in pixels of the column. - + The title of the column to be displayed in the header row. The type of the column: "text", "check", or "image". "text" is a normal column, and displays the textual value of the data element. "check" displays a checkmark if the data is non-zero (integers) or for strings if the value is non-empty and not "N" or "No". "image" displays the image referred to by the pathname contained in the data value. + The max width of the image displayed when type=image. + + The max height of the image displayed when type=image. + + Not documented. + width of the column. Determines if text can wrap around an obstacle. - + + Set this to 0 to hide the column from the table. + + + + + + Not documented. + + The height of the table row detail. (This will expand the height of the selected row when the widget appears.) + + Specify 0 to hide the row-detail (useful for creating multiple widgets and picking between them dynamically). Support run_client(). + + Specify 1 to make new rows show their table-row-detail when added (deault: 0). + + The Check event fires when the user clicks an unchecked checkbox in a row. Passes the same values as DblClick, plus 'checkbox' which is set to 1. Cancelling this event prevents the row from being selected. + The Click event fires when a user clicks on a row. + The ClickItem event fires when the user clicks an item in an 'itemlist' column. Passes 'Item' which is the item text, 'ItemKey' and 'ItemIndex' which identify the item, 'Row' which is the row number, and 'Column' and 'ColumnName' which are the field name and the column name. + + The Close event fires on a "widget/table-row-detail" child widget when it stops being shown for a row, either because another row was selected, because its "display_for" value was set to 0, or because the row was scrolled out of the table. Passes no values. + The DblClick event fires when the user double-clicks the mouse on a row. Passes three values, 'Caller' which is the table's name, 'recnum' which is the sequential number of the record in the table, and 'data' which is an array of the data in the selected record. - The Click event fires when a user right clicks on a mouse or mouse pad. - + The DragCancelItem event fires when the user drops a dragged item somewhere other than an 'itemlist' column, cancelling the drag. Passes the same values as ClickItem, describing where the drag started. + + The DragDropItem event fires when the user drops a dragged item on an 'itemlist' column. Passes 'Item', 'ItemKey', and 'ItemIndex' which identify the dragged item, 'SourceRow', 'SourceColumn', and 'SourceColumnName' which describe where the drag started, 'TargetRow', 'TargetColumn', and 'TargetColumnName' which describe where it was dropped, and 'BeforeItem' which is the item it was dropped in front of (or null). + + The DragStartItem event fires when the user begins dragging an item in an 'itemlist' column. Passes the same values as ClickItem, plus 'OffsetX' and 'OffsetY' which are the distance the pointer has moved. + + The Open event fires on a "widget/table-row-detail" child widget when it is shown for a row, which happens when the row is selected (or when a new row is added, if "show_on_new" is set). Passes no values. + + The RightClick event occurs when a user clicks on the widget with the relevant button on their mouse or mouse pad. + + The Uncheck event fires when the user clicks a checked checkbox in a row. Passes the same values as DblClick, plus 'checkbox' which is set to 2. Cancelling this event prevents the row from being selected. + - + Drops all elements from the table. + Enables the initial_selection property and redraws the table. + - + ]]>
- - + - + -

A widget/template must be a root widget of a file (which normally is given a .tpl extension).

+

A widget/template must be a root widget of a file (which normally is given a .tpl extension).

-

Each child in a widget/template is a "rule". Each rule applies to widgets that both 1) have the same 'widget_class' property value (there can be only one widget_class per widget (and "rule")) and 2) match the widget type of the child (eg "widget/imagebutton").

+

Each child in a widget/template is a "rule". Each rule applies to widgets that both 1) have the same 'widget_class' property value (there can be only one widget_class per widget (and "rule")) and 2) match the widget type of the child (eg "widget/imagebutton").

-

Every other property of the "rule" are default values.

+

Every other property of the "rule" are default values.

-

All children of the "rule" are automatically inserted into the matched widgets.

+

All children of the "rule" are automatically inserted into the matched widgets.

- + - + - The value of this property must match another widget before said widget will "inherit" this "rule". + The value of this property must match another widget before said widget will "inherit" this "rule". - + - - - + ]]>
- + + -

The textarea is a multi-line text edit widget, allowing the user to enter text data containing line endings, tabs, and more. It also contains a scrollbar which allows more text to be edited than can fit in the displayable area.

+

The terminal widget displays a fixed-size grid of characters (rows by cols) and emulates a subset of the VT100 terminal. It opens the ObjectSystem object named by the 'source' property through the OSML, polls it for data, and interprets the escape sequences in that data stream to position and erase text on the display.

+ +

The widget is experimental and largely incomplete. Only cursor positioning, carriage return, line feed, tab, and erase-to-end-of-line are honored; character attributes (colors, bold, reverse), erase-to-end-of-page, scrolling, keyboard input, sending data back to the source, and disconnecting are not implemented. Because keyboard input is not delivered to the widget, the terminal is read-only in practice, and is best suited to viewing the output of a program such as "top".

-

The textarea is a visual form element widget, and can be contained inside any container capable of holding visual widgets. It may only contain nonvisual widgets like the connector.

- -
- - - - A background image for the textarea. - - A color, RGB or named, to be used as the background. If neither bgcolor nor background are specified, the textarea is transparent. - - The field in the objectsource to associate this textarea with. - - Links text area to a form. - - The height of the textarea, in pixels - - The maximum number of characters the textarea should accept from the user +

The terminal widget can be used inside any container capable of having visual subwidgets. It may contain no widgets other than any applicable connectors.

- Can hold text, html, or wiki and display. - - Set to 'yes' if the data in the text area should be viewed only and not be modified. +

The 'source' property must reference an ObjectSystem object which can be opened and read repeatedly, such as a shell script object (a ".shl" file). The object is read in blocks of 2048 bytes and re-polled every two seconds when no data is available. The path is resolved relative to the directory containing the application, since cross-directory reads are blocked by the browser's cross-site scripting protections.

- The border style of the text area, can be 'raised' or 'lowered'. Default is 'raised'. +

The widget requires a browser with W3C DOM1 HTML and DOM2 CSS support, and it renders only on the "dhtml" output driver.

- The width of the textarea, in pixels + - The horizontal coordinate of the left edge of the textarea, relative to the container. + - The vertical coordinate of the top edge of the textarea, relative to the container. + Required. The width of the terminal, in characters. The widget's width in pixels is 'cols' multiplied by 'fontsize'. - + A color, either named or numeric, to use for VT100 color 0. Default is "black". - + A color, either named or numeric, to use for VT100 color 2. Default is "green". - - - This event occurs before the key press event is fired and can stop the key press event. - - This event occurs when the user has modified the data value of the widget (clicked or unclicked it). + A color, either named or numeric, to use for VT100 color 3. Default is "yellow". - This event occurs when the data is changed (occurs when key press or button changes things). + A color, either named or numeric, to use for VT100 color 4. Default is "blue". - This event occurs when the user presses the escape key. + A color, either named or numeric, to use for VT100 color 5. Default is "purple". - This event occurs when the editbox receives keyboard focus (if the user tabs on to it or clicks on it, for instance). + A color, either named or numeric, to use for VT100 color 6. Default is "aqua". - This event occurs when the user presses any key. - - This event occurs when the editbox loses keyboard focus (if the user tabs off of it, for instance). + A color, either named or numeric, to use for VT100 color 7. Default is "white". - This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. + The size, in pixels, of one character cell. Default is 12. It is used only to compute the size of the widget; the characters themselves are displayed in the browser's "fixed" font at its normal size. - This event occurs when the user moves the mouse pointer while it is over the widget. The event will repeatedly fire each time the pointer moves. + Required. The height of the terminal, in character rows. - This event occurs when the user moves the mouse pointer off of the widget. + Required. The ObjectSystem path of the object supplying the terminal's data stream. - This event occurs when the user first moves the mouse pointer over the widget. It will not occur again until the user moves the mouse off of the widget and then back over it again. + Required. The X location of the left edge of the widget, relative to its container. - This event occurs when the user releases the mouse button on the widget. + Required. The Y location of the top edge of the widget, relative to its container. - This event occurs when the user presses the tab key. + The terminal widget has no 'width' or 'height' properties; its size is determined by 'rows', 'cols', and 'fontsize'. - +
- Checks to see if things need to be changed (new is different from old) then calls set value. - - Sets the focus on a selected widget. - - Sets the content to the text parameter sent. - - + Opens an OSML session on the 'source' object and begins reading from it. Does nothing if the terminal is already connected. The terminal connects automatically when the page loads, so this action is only needed after a disconnect. - + - + ]]>
- - + -

A textbutton provides similar functionality to the imagebutton. However, the programmer need not create two or three graphics images in order to use a textbutton; rather simply specifying the text to appear on the button is sufficient.

+ -

Textbuttons, like imagebuttons, can either have two or three states. A three-state textbutton doesn't have a "raised" border until the user points to it, whereas a two-state textbutton retains its raised border whether pointed to or not.

+

The textarea is a multi-line text edit widget, allowing the user to enter text data containing line endings, tabs, and more. It also contains a scrollbar which allows more text to be edited than can fit in the displayable area.

-

The TextButton can be placed inside any visible container, but only nonvisual widgets can be placed within it.

+

The textarea is a visual form element widget, and can be contained inside any container capable of holding visual widgets. It may only contain nonvisual widgets like the connector.

- - Sets the alignment of text in the button, can have left right or center (default). - A background image for the button. + A background image for the textarea. - A color, RGB or named, to be used as the button's background.If neither bgcolor nor background are specified, the button is transparent. - - A color that outlines the button. - - A radius that describes the sharpness of the corners of the button (smaller means sharper). + A color, RGB or named, to be used as the background. If neither bgcolor nor background are specified, the textarea is transparent. - Determines the look of the outline. - - A color, RGB or named, to be used for the button's text when it is disabled. + The field in the objectsource to associate this textarea with. - Whether the button is enabled (can be clicked). Default is 'yes'. Also supports dynamic runclient() expressions allowing the enabled status of the button to follow the value of an expression. + Links text area to a form. - A color, RGB or named, for the text on the button. Default "white". + The height of the textarea, in pixels - A color, RGB or named, for the text's 1-pixel drop-shadow. Default "black". + The maximum number of characters the textarea should accept from the user - Height, in pixels, of the text button. + Can hold text, html, or wiki and display. - File path to the source of the image. - - Defines the height of image. - - Defines spacing between image and the border. - - Defines the width of image. - - Describes where an image is in the text button (top-default, right, bottom, left). - - The text to appear on the button. This may be a dynamic runclient() expression to dynamically change the button's text. + Set to 'yes' if the data in the text area should be viewed only and not be modified. - Whether or not the button is tri-state (does not display a raised border until the user points at it). Default is yes. + The border style of the text area, can be 'raised' or 'lowered'. Default is 'raised'. - The width, in pixels, of the text button. + The width of the textarea, in pixels - X-coordinate of the upper left corner of the button, relative to its container. + The horizontal coordinate of the left edge of the textarea, relative to the container. - Y-coordinate of the upper left corner of the button, relative to its container. + The vertical coordinate of the top edge of the textarea, relative to the container. - + - + This event occurs before the key press event is fired and can stop the key press event. - + This event occurs when the user has modified the data value of the widget (clicked or unclicked it). + + This event occurs when the data is changed (occurs when key press or button changes things). + + This event occurs when the user presses the escape key. + + This event occurs when the editbox receives keyboard focus (if the user tabs on to it or clicks on it, for instance). + + This event occurs when the user presses any key. + + This event occurs when the editbox loses keyboard focus (if the user tabs off of it, for instance). - This event occurs when the user clicks the button. No parameters are available from this event. + This event occurs when the user clicks the mouse pointer (equivalent to a MouseDown followed immediately by a MouseUp on the same widget) while it is over this widget. This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. @@ -4074,17 +4306,44 @@ objcanvas_test "widget/template" This event occurs when the user releases the mouse button on the widget. + This event occurs when the user presses the tab key. + - Called from a connector, this action sets the button's text to the value passed in through connector's "Text" parameter. + Checks to see if things need to be changed (new is different from old) then calls SetValue with the provided "Text". Specify 1 for the "SetFocus" parameter to cause this textarea to become the focus when the text is inserted. + + Sets the focus on a selected widget. Specify the optional "X" or "Y" parameters to move the focus box that appears on the textarea. + + Sets the content to the specified "Value" parameter. +
+ + + + + + +

Deprecated. This name is rendered by widget/button and is kept only so that existing applications keep working. New applications should use widget/button, which documents the properties, events, actions, and client properties that a textbutton accepts.

+ +

A textbutton is a widget/button that takes its 'type' from its content: 'text' when no 'image' is given, otherwise the position named by 'image_position', which defaults to 'top'. Two of the property names used by existing textbuttons are deprecated spellings: 'image_margin' is now 'spacing', and 'image_position' is now covered by the 'topimage', 'rightimage', 'bottomimage', and 'leftimage' values of 'type'.

+ +
+ + + +

The TextButton can be placed inside any visible container, but only nonvisual widgets can be placed within it.

+ +
+ + + - + ]]>
+ -

A timer widget is used to schedule an Event to occur after a specified amount of time. Timers can be set to expire a set amount of time after the page is loaded, or they can be triggered into counting down via activating an Action on the timer. Timers can be used to create animations, delayed effects, and more.

+

A timer widget is used to schedule an Event to occur after a specified amount of time. Timers can be set to expire a set amount of time after the page is loaded, or they can be triggered into counting down via activating an Action on the timer. Timers can be used to create animations, delayed effects, and more.

-

Timers are nonvisual widgets which can be placed almost anywhere in an application. They are most commonly found at the top-level of the application, however. Timers have no direct effects on the object in which they are placed. Timers can only contain Connector widgets.

+

Timers are nonvisual widgets which can be placed almost anywhere in an application. They are most commonly found at the top-level of the application, however. Timers have no direct effects on the object in which they are placed. Timers can only contain Connector widgets.

- The timer starts counting down again immediately after it expires. + The timer starts counting down again immediately after it expires. - The timer starts counting down immediately after the page loads. + The timer starts counting down immediately after the page loads. - Of milliseconds (1/1000th of a second) before the timer expires. + Of milliseconds (1/1000th of a second) before the timer expires. + + + This occurs when the timer hits its timeout and the auto reset is not enabled. + + + - Actions causes a timer to stop counting down, and thus no Expire event will occur until another countdown sequence is initiated by a SetTimer action. + Actions causes a timer to stop counting down, and thus no Expire event will occur until another countdown sequence is initiated by a SetTimer action. - Action takes two parameters: "Time" (integer in milliseconds) and "AutoReset" (integer 0 or 1). It causes a timer to begin counting down towards an Expire event. + Action takes two parameters: "Time" (integer in milliseconds) and "AutoReset" (integer 0 or 1). It causes a timer to begin counting down towards an Expire event. - - - - This occurs when the timer hits its timeout and the auto reset is not enabled. - - - + ]]>
+ -

A treeview provides a way of viewing hierarchically-organized data via a "traditional" GUI point-and-click tree structure. A treeview has "branches" that can expand and collapse entire subtrees of data.

+

A treeview provides a way of viewing hierarchically-organized data via a "traditional" GUI point-and-click tree structure. A treeview has "branches" that can expand and collapse entire subtrees of data.

-

Centrallix treeviews present a subtree of the ObjectSystem, with the data dynamically loaded, on demand, from the server as the widget is used. The treeview widget thus has no intelligence in and of itself in determining what kinds of objects are presented at each level of the tree. Many times, this is exactly what is desired because the treeview is being used to simply browse objects in the ObjectSystem, such as directories and files. In other cases, the treeview is teamed up with a special ObjectSystem object called a "querytree" (QYT) object. The querytree object creates a hierarchical view from other potentially non-hierarchical data in the ObjectSystem, such as that from different database tables and so forth.

+

Centrallix treeviews present a subtree of the ObjectSystem, with the data dynamically loaded, on demand, from the server as the widget is used. The treeview widget thus has no intelligence in and of itself in determining what kinds of objects are presented at each level of the tree. Many times, this is exactly what is desired because the treeview is being used to simply browse objects in the ObjectSystem, such as directories and files. In other cases, the treeview is teamed up with a special ObjectSystem object called a "querytree" (QYT) object. The querytree object creates a hierarchical view from other potentially non-hierarchical data in the ObjectSystem, such as that from different database tables and so forth.

-

Treeviews can be placed inside of any visual container, but are usually placed inside of a scrollpane, since scrollpanes can expand to allow the user to view data that would not normally fit inside the desired container. Treeviews can contain only nonvisual widgets such as connectors.

+

Treeviews can be placed inside any visual container, but are usually placed inside a scrollpane, since scrollpanes can expand to allow the user to view data that would not normally fit inside the desired container. Treeviews can contain only nonvisual widgets such as connectors.

- A color, RGB or named, to be used for the text for the items in the treeview. + A color, RGB or named, to be used for the text for the items in the treeview. - A background image for the selected item in the treeview. + A background image for the selected item in the treeview. - A color, RGB or named, to be used for the text for the selected item. + A color, RGB or named, to be used for the text for the selected item. - A pathname to an image to be used as icons for the items in the treeview. + A pathname to an image to be used as icons for the items in the treeview. Orders treeview in descending (desc) and ascending (anything else) order. - - Whether to display the connections between items in the tree using lines. - Whether to display the root of the tree. If "no", then the root is auto-expanded and only its children are visible. + Whether to display the connections between items in the tree using lines. + + Whether to display the root of the tree. If "no", then the root is auto-expanded and only its children are visible. - Whether to display the connection lines between the root and the root's immediate children. If "no", the root's children are flush against the lefthand side of the treeview, otherwise the connecting lines show up. + Whether to display the connection lines between the root and the root's immediate children. If "no", the root's children are flush against the left-hand side of the treeview, otherwise the connecting lines show up. - The ObjectSystem path of the root of the treeview. + The ObjectSystem path of the root of the treeview. - If set to "yes", the branch lines are drawn in a 3D style, otherwise they are drawn in a single color. + If set to "yes", the branch lines are drawn in a 3D style, otherwise they are drawn in a single color. - Width, in pixels, of the treeview. + Width, in pixels, of the treeview. - X-coordinate of the upper left corner of the treeview's root object. + X-coordinate of the upper left corner of the treeview's root object. - Y-coordinate of the upper left corner of the treeview's root object. + Y-coordinate of the upper left corner of the treeview's root object. This event occurs when the user clicks while the treeview is in focus. - - Occurs when the user clicks on the clickable link for an item in the treeview. Its one parameter is "Pathname", or the ObjectSystem path to the object which was selected. + + Occurs when the user clicks on the clickable link for an item in the treeview. Its one parameter is "Pathname", or the ObjectSystem path to the object which was selected. This event occurs when the user presses the mouse button on the widget. This differs from the 'Click' event in that the user must actually press and release the mouse button on the widget for a Click event to fire, whereas simply pressing the mouse button down will cause the MouseDown event to fire. @@ -4277,32 +4538,32 @@ mytimerTwo "widget/timer" This event occurs when the user releases the mouse button on the widget. This event occurs when one of the elements of the treeview is given focus. - - To the above, but when the user right-clicks on an item in the treeview. + + To the above, but when the user right-clicks on an item in the treeview. - + - Searches for a specific element. - + Searches the tree for the text given in the 'Value' parameter, then expands and selects the first matching element. + Returns the next element in the treeview. - + Transfers focus to the selected element. - - Sets the selected element as the root. + + Collapses the tree and re-roots it. The 'NewRoot' parameter is the pathname of the new root object (javascript:window is used if it is omitted), and the 'NewRootObj' parameter optionally supplies an already-retrieved object to use as that root. Set the 'Expand' parameter to yes to expand the new root immediately. - + ]]>
+ @@ -4348,37 +4610,37 @@ mypane "widget/pane" -

This nonvisual widget is used at the top-level (within a "widget/page"). Currently it has no events, and so shouldn't contain any visual or nonvisual widgets.

+

This nonvisual widget is used at the top-level (within a "widget/page"). It should not contain any visual or nonvisual child widgets.

- The field with which to link / connect to the variable. + The field with which to link / connect to the variable. - The form (e.g. it's osrc) with which to associate the variable and fieldname (if different than the form in which variable is currently nested). + The form (e.g. it's osrc) with which to associate the variable and fieldname. Defaults to the form in which variable is nested. Can be accessed as :var_name:value. - - - Called by a widget/connector: Sets the value of the variable to whatever is specified by the "Value" parameter. - - - - + This event occurs when the user has modified the data value of the widget (clicked or unclicked it). This event occurs when the data is changed (occurs when key press or button changes things). + + + Called by a widget/connector: Sets the value of the variable to whatever is specified by the "Value" parameter. + + + - + ]]>
+ -

An autolayout widget with style set to "vbox". See "widget/autolayout".

+

An autolayout widget with style set to "vbox". See "widget/autolayout".

+ See "widget/autolayout" + + +
+ \"%s\":\"%s\" (child).", + parent->Name, parent->Type, child->Name, child->Type + ); + return -1; + } } return 0; } -/*** htrCheckNSTransitionReturn -- check to see if we are transitioning between - *** namespaces, and if so, emit the code to switch the context. +/*** Check to see if we are transitioning when we move from the child's + *** namespace back up to the parent's namespace. If so, write code to + *** switch the namespace context. + *** + *** @param s The current session. + *** @param parent The parent we are returning to. + *** @param parent The child we are moving away from. + *** @returns 0 if successful, and -1 if an error occurs. ***/ int htrCheckNSTransitionReturn(pHtSession s, pWgtrNode parent, pWgtrNode child) { - /** Crossing a namespace boundary? **/ - if (child && parent && strcmp(child->Namespace, parent->Namespace) != 0) + if (child != NULL && parent != NULL && + strcmp(child->Namespace, parent->Namespace) != 0) { - htrLeaveNamespace(s); + if (UNLIKELY(htrLeaveNamespace(s) != 0)) + { + mssError(0, "HTR", + "Transition return failed: \"%s\":\"%s\" (parent) <-- \"%s\":\"%s\" (child).", + parent->Name, parent->Type, child->Name, child->Type + ); + return -1; + } } return 0; @@ -519,7 +588,13 @@ htrCheckNSTransitionReturn(pHtSession s, pWgtrNode parent, pWgtrNode child) int htrAddStylesheetItem(pHtSession s, char* html_text) { - return htr_internal_AddTextToArray(&(s->Page.HtmlStylesheet), html_text); + if (UNLIKELY(htr_internal_AddTextToArray(&(s->Page.HtmlStylesheet), html_text) != 0)) + { + mssError(0, "HTR", "Failed to add style sheet item."); + return -1; + } + + return 0; } @@ -529,7 +604,13 @@ htrAddStylesheetItem(pHtSession s, char* html_text) int htrAddHeaderItem(pHtSession s, char* html_text) { - return htr_internal_AddTextToArray(&(s->Page.HtmlHeader), html_text); + if (UNLIKELY(htr_internal_AddTextToArray(&(s->Page.HtmlHeader), html_text) != 0)) + { + mssError(0, "HTR", "Failed to add header item."); + return -1; + } + + return 0; } @@ -539,7 +620,13 @@ htrAddHeaderItem(pHtSession s, char* html_text) int htrAddBodyItem(pHtSession s, char* html_text) { - return htr_internal_AddTextToArray(&(s->Page.HtmlBody), html_text); + if (UNLIKELY(htr_internal_AddTextToArray(&(s->Page.HtmlBody), html_text) != 0)) + { + mssError(0, "HTR", "Failed to add body item."); + return -1; + } + + return 0; } @@ -549,7 +636,13 @@ htrAddBodyItem(pHtSession s, char* html_text) int htrAddExpressionItem(pHtSession s, char* html_text) { - return htr_internal_AddTextToArray(&(s->Page.HtmlExpressionInit), html_text); + if (UNLIKELY(htr_internal_AddTextToArray(&(s->Page.HtmlExpressionInit), html_text) != 0)) + { + mssError(0, "HTR", "Failed to add expression item."); + return -1; + } + + return 0; } @@ -560,7 +653,13 @@ htrAddExpressionItem(pHtSession s, char* html_text) int htrAddBodyParam(pHtSession s, char* html_param) { - return htr_internal_AddTextToArray(&(s->Page.HtmlBodyParams), html_param); + if (UNLIKELY(htr_internal_AddTextToArray(&(s->Page.HtmlBodyParams), html_param) != 0)) + { + mssError(0, "HTR", "Failed to add body parameter."); + return -1; + } + + return 0; } @@ -577,19 +676,18 @@ htr_internal_GrowFn(char** str, size_t* size, size_t offs, void* arg, size_t req char* new_buf; size_t new_buf_size; - if (*size >= req_size) return 1; + if (UNLIKELY(*size >= req_size)) return 1; assert(*str == s->Tmpbuf); assert(*size == s->TmpbufSize); new_buf_size = s->TmpbufSize * 2; while(new_buf_size < req_size) new_buf_size *= 2; - new_buf = nmSysRealloc(s->Tmpbuf, new_buf_size); - if (!new_buf) - return 0; + new_buf = checkPtr(nmSysRealloc(s->Tmpbuf, new_buf_size)); + if (new_buf == NULL) return false; /* Grow failed. */ *str = s->Tmpbuf = new_buf; *size = s->TmpbufSize = new_buf_size; - return 1; /* OK */ + return true; /* Success. */ } @@ -599,7 +697,8 @@ htr_internal_GrowFn(char** str, size_t* size, size_t offs, void* arg, size_t req int htr_internal_QPAddText(pHtSession s, int (*fn)(), char* fmt, va_list va) { - int rval; + int rval = -1, tmp; + pQPSession error_session = NULL; /** Print a warning if we think the format string isn't a constant. ** We'll need to upgrade this once htdrivers start being loaded as @@ -613,18 +712,43 @@ htr_internal_QPAddText(pHtSession s, int (*fn)(), char* fmt, va_list va) } #endif - rval = qpfPrintf_va_internal(NULL, &(s->Tmpbuf), &(s->TmpbufSize), htr_internal_GrowFn, (void*)s, fmt, va); - if (rval < 0) + /** Create a pQPSession to track errors from qpfPrintf. **/ + error_session = checkPtr(qpfOpenSession()); + if (error_session == NULL) goto end; + + /** Print text using qpfPrintf(). **/ + tmp = qpfPrintf_va_internal(error_session, &(s->Tmpbuf), &(s->TmpbufSize), htr_internal_GrowFn, (void*)s, fmt, va); + if (UNLIKELY(tmp < 0)) { - printf("WARNING: QPAddText() failed for format: %s\n", fmt); + mssError(1, "HTR", "QPAddText() failed to format: \"%s\"", fmt); + qpfLogErrors(error_session); + goto end; + } + if (UNLIKELY(s->TmpbufSize - 1 < tmp)) + { + mssError(1, "HTR", "qpfPrintf() failed to write all the required text."); + qpfLogErrors(error_session); + goto end; } - if (rval < 0 || rval > (s->TmpbufSize - 1)) - return -1; - /** Ok, now add the tmpbuf normally. **/ - fn(s, s->Tmpbuf); + /** Add the text using the callback function. **/ + tmp = fn(s, s->Tmpbuf); + if (UNLIKELY(tmp != 0)) + { + char error_code[32] = {'\0'}; + if (UNLIKELY(tmp != -1)) snprintf(error_code, sizeof(error_code), " (error code: %d)", tmp); + mssError(0, "HTR", "Provided callback function failed%s.", error_code); + goto end; + } + + /** Success. **/ + rval = 0; + + end: + /** Clean up. **/ + if (LIKELY(error_session != NULL)) check(qpfCloseSession(error_session)); /* Failure ignored. */ - return 0; + return rval; } @@ -634,10 +758,7 @@ htr_internal_QPAddText(pHtSession s, int (*fn)(), char* fmt, va_list va) int htr_internal_AddText(pHtSession s, int (*fn)(), char* fmt, va_list va) { - va_list orig_va; - int rval; - char* new_buf; - int new_buf_size; + int tmp; /** Print a warning if we think the format string isn't a constant. ** We'll need to upgrade this once htdrivers start being loaded as @@ -651,40 +772,55 @@ htr_internal_AddText(pHtSession s, int (*fn)(), char* fmt, va_list va) } #endif - /** Save the current va_list state so we can retry it. **/ + /** Save a copy of the va_list so we can retry. **/ + va_list orig_va; va_copy(orig_va, va); + retry: /** Attempt to print the thing to the tmpbuf. **/ - while(1) - { - rval = vsnprintf(s->Tmpbuf, s->TmpbufSize, fmt, va); - - /** Sigh. Some libc's return -1 and some return # bytes that would be written. **/ - if (rval < 0 || rval > (s->TmpbufSize - 1)) - { - /** I think I need a bigger box. Fix it and try again. **/ - new_buf_size = s->TmpbufSize * 2; - while(new_buf_size < rval) new_buf_size *= 2; - new_buf = nmSysMalloc(new_buf_size); - if (!new_buf) - { - return -1; - } - nmSysFree(s->Tmpbuf); - s->Tmpbuf = new_buf; - s->TmpbufSize = new_buf_size; - va = orig_va; - } - else + tmp = checkNeg(vsnprintf(s->Tmpbuf, s->TmpbufSize, fmt, va)); + if (tmp < 0) goto err; + if (UNLIKELY(tmp > s->TmpbufSize - 1)) + { + /** Increase buffer size. **/ + size_t new_buf_size = s->TmpbufSize * 2lu; + while (new_buf_size < tmp) new_buf_size *= 2lu; + + /** Allocate the new buffer. **/ + void* new_buf = checkPtr(nmSysMalloc(new_buf_size)); + if (new_buf == NULL) { - break; + /** Start a new error chain. **/ + mssClearError(); + goto err; } + nmSysFree(s->Tmpbuf); /* Clean up. */ + + /** Set new buffer. **/ + s->TmpbufSize = new_buf_size; + s->Tmpbuf = new_buf; + + /** Try the write again. **/ + va = orig_va; + goto retry; + } + + /** Add the text using the callback function. **/ + tmp = fn(s, s->Tmpbuf); + if (UNLIKELY(tmp != 0)) + { + char error_code[32] = {'\0'}; + if (UNLIKELY(tmp != -1)) snprintf(error_code, sizeof(error_code), " (error code: %d)", tmp); + mssError(0, "HTR", "Provided callback function failed%s.", error_code); + goto err; } - - /** Ok, now add the tmpbuf normally. **/ - fn(s, s->Tmpbuf); - - return 0; + + /** Success. **/ + return 0; + + err: + mssError(0, "HTR", "Failed to add text using format: \"%s\"", fmt); + return -1; } @@ -697,10 +833,11 @@ htrAddBodyItem_va(pHtSession s, char* fmt, ... ) va_list va; va_start(va, fmt); - htr_internal_QPAddText(s, htrAddBodyItem, fmt, va); + const int ret = htr_internal_QPAddText(s, htrAddBodyItem, fmt, va); va_end(va); + if (UNLIKELY(ret != 0)) mssError(0, "HTR", "Failed to add HTML body item."); - return 0; + return ret; } @@ -713,10 +850,11 @@ htrAddExpressionItem_va(pHtSession s, char* fmt, ... ) va_list va; va_start(va, fmt); - htr_internal_QPAddText(s, htrAddExpressionItem, fmt, va); + const int ret = htr_internal_QPAddText(s, htrAddExpressionItem, fmt, va); va_end(va); + if (UNLIKELY(ret != 0)) mssError(0, "HTR", "Failed to add expression item."); - return 0; + return ret; } @@ -729,10 +867,11 @@ htrAddStylesheetItem_va(pHtSession s, char* fmt, ... ) va_list va; va_start(va, fmt); - htr_internal_QPAddText(s, htrAddStylesheetItem, fmt, va); + const int ret = htr_internal_QPAddText(s, htrAddStylesheetItem, fmt, va); va_end(va); + if (UNLIKELY(ret != 0)) mssError(0, "HTR", "Failed to add CSS stylesheet item."); - return 0; + return ret; } @@ -745,10 +884,11 @@ htrAddHeaderItem_va(pHtSession s, char* fmt, ... ) va_list va; va_start(va, fmt); - htr_internal_QPAddText(s, htrAddHeaderItem, fmt, va); + const int ret = htr_internal_QPAddText(s, htrAddHeaderItem, fmt, va); va_end(va); + if (UNLIKELY(ret != 0)) mssError(0, "HTR", "Failed to add HTML document header item."); - return 0; + return ret; } @@ -761,10 +901,11 @@ htrAddBodyParam_va(pHtSession s, char* fmt, ... ) va_list va; va_start(va, fmt); - htr_internal_QPAddText(s, htrAddBodyParam, fmt, va); + const int ret = htr_internal_QPAddText(s, htrAddBodyParam, fmt, va); va_end(va); + if (UNLIKELY(ret != 0)) mssError(0, "HTR", "Failed to add body parameter."); - return 0; + return ret; } @@ -777,10 +918,11 @@ htrAddScriptWgtr_va(pHtSession s, char* fmt, ...) va_list va; va_start(va, fmt); - htr_internal_QPAddText(s, htrAddScriptWgtr, fmt, va); + const int ret = htr_internal_QPAddText(s, htrAddScriptWgtr, fmt, va); va_end(va); + if (UNLIKELY(ret != 0)) mssError(0, "HTR", "Failed to add JS code."); - return 0; + return ret; } @@ -793,6 +935,10 @@ htrAddScriptInit_va(pHtSession s, char* fmt, ... ) { va_list va; + /*** TODO: Greg - Can we remove this code? It seems like this is the + *** concern of htrRender(), and if it's not, should we remove the + *** line from htrRender() that also does the same thing? + ***/ if (!s->Namespace->HasScriptInits) { /** No script inits for this namespace yet? Issue the context @@ -800,14 +946,15 @@ htrAddScriptInit_va(pHtSession s, char* fmt, ... ) **/ s->Namespace->HasScriptInits = 1; /*htrAddScriptInit_va(s, "\n nodes = wgtrNodeList(%STR&SYM);\n", s->Namespace->DName);*/ - htrAddScriptInit_va(s, "\n ns = \"%STR&SYM\";\n", s->Namespace->DName); + htrAddScriptInit_va(s, "\tns = \"%STR&SYM\";\n", s->Namespace->DName); } va_start(va, fmt); - htr_internal_QPAddText(s, htrAddScriptInit, fmt, va); + const int ret = htr_internal_QPAddText(s, htrAddScriptInit, fmt, va); va_end(va); + if (UNLIKELY(ret != 0)) mssError(0, "HTR", "Failed to add JS init call."); - return 0; + return ret; } @@ -820,10 +967,11 @@ htrAddScriptCleanup_va(pHtSession s, char* fmt, ... ) va_list va; va_start(va, fmt); - htr_internal_QPAddText(s, htrAddScriptCleanup, fmt, va); + const int ret = htr_internal_QPAddText(s, htrAddScriptCleanup, fmt, va); va_end(va); + if (UNLIKELY(ret != 0)) mssError(0, "HTR", "Failed to add JS clean up code."); - return 0; + return ret; } @@ -835,30 +983,33 @@ int htrAddScriptInclude(pHtSession s, char* filename, int flags) { pStrValue sv; - char* scripts_already; - /** What scripts has the client already loaded? If the - ** requested script is in the list, ignore it since the - ** client already has it. - **/ - scripts_already = htrParamValue(s, "cx__scripts"); - if (scripts_already && strstr(scripts_already, filename)) + /** Ignore this call if the script is already included. **/ + char* scripts_loaded = htrParamValue(s, "cx__scripts"); + if (scripts_loaded != NULL && strstr(scripts_loaded, filename)) return 0; + + /** Cache check. **/ + if (xhLookup(&(s->Page.NameIncludes), filename) != NULL) return 0; /** Alloc the string val. **/ - if (xhLookup(&(s->Page.NameIncludes), filename)) return 0; - sv = (pStrValue)nmMalloc(sizeof(StrValue)); - if (!sv) return -1; + sv = (pStrValue)checkPtr(nmMalloc(sizeof(StrValue))); + if (sv == NULL) goto err; sv->Name = filename; if (flags & HTR_F_NAMEALLOC) sv->NameSize = strlen(filename)+1; sv->Value = ""; sv->Alloc = (flags & HTR_F_NAMEALLOC); /** Add to the hash table and array **/ - xhAdd(&(s->Page.NameIncludes), filename, (char*)sv); - xaAddItem(&(s->Page.Includes), (char*)sv); + if (check(xhAdd(&(s->Page.NameIncludes), filename, (char*)sv)) != 0) goto err; + if (checkNeg(xaAddItem(&(s->Page.Includes), (char*)sv)) < 0) goto err; - return 0; + /** Success. **/ + return 0; + + err: + mssError(1, "HTR", "Failed to include JS file: \"%s\"", filename); + return -1; } @@ -873,8 +1024,8 @@ htrAddScriptFunction(pHtSession s, char* fn_name, char* fn_text, int flags) /** Alloc the string val. **/ if (xhLookup(&(s->Page.NameFunctions), fn_name)) return 0; - sv = (pStrValue)nmMalloc(sizeof(StrValue)); - if (!sv) return -1; + sv = (pStrValue)checkPtr(nmMalloc(sizeof(StrValue))); + if (sv == NULL) goto err; sv->Name = fn_name; if (flags & HTR_F_NAMEALLOC) sv->NameSize = strlen(fn_name)+1; sv->Value = fn_text; @@ -882,10 +1033,15 @@ htrAddScriptFunction(pHtSession s, char* fn_name, char* fn_text, int flags) sv->Alloc = flags; /** Add to the hash table and array **/ - xhAdd(&(s->Page.NameFunctions), fn_name, (char*)sv); - xaAddItem(&(s->Page.Functions), (char*)sv); + if (check(xhAdd(&(s->Page.NameFunctions), fn_name, (char*)sv)) != 0) goto err; + if (checkNeg(xaAddItem(&(s->Page.Functions), (char*)sv)) < 0) goto err; - return 0; + /** Success. **/ + return 0; + + err: + mssError(1, "HTR", "Failed to add JS function: %s()", fn_name); + return -1; } @@ -897,10 +1053,12 @@ htrAddScriptGlobal(pHtSession s, char* var_name, char* initialization, int flags { pStrValue sv; - /** Alloc the string val. **/ - if (xhLookup(&(s->Page.NameGlobals), var_name)) return 0; - sv = (pStrValue)nmMalloc(sizeof(StrValue)); - if (!sv) return -1; + /** Cache check. **/ + if (xhLookup(&(s->Page.NameGlobals), var_name) != NULL) return 0; + + /** Alloc a new string val. **/ + sv = (pStrValue)checkPtr(nmMalloc(sizeof(StrValue))); + if (sv == NULL) goto err; sv->Name = var_name; sv->NameSize = strlen(var_name)+1; sv->Value = initialization; @@ -908,10 +1066,20 @@ htrAddScriptGlobal(pHtSession s, char* var_name, char* initialization, int flags sv->Alloc = flags; /** Add to the hash table and array **/ - xhAdd(&(s->Page.NameGlobals), var_name, (char*)sv); - xaAddItem(&(s->Page.Globals), (char*)sv); + if (check(xhAdd(&(s->Page.NameGlobals), var_name, (char*)sv)) != 0) goto err; + if (checkNeg(xaAddItem(&(s->Page.Globals), (char*)sv)) < 0) goto err; - return 0; + /** Success. **/ + return 0; + + err:; + char flags_buf[20] = {'\0'}; + if (flags != 0) snprintf(flags_buf, sizeof(flags_buf), " (flags %d)", flags); + mssError(1, "HTR", + "Failed to add global variable %s = %s%s.", + var_name, initialization, flags_buf + ); + return -1; } @@ -921,7 +1089,13 @@ htrAddScriptGlobal(pHtSession s, char* var_name, char* initialization, int flags int htrAddScriptInit(pHtSession s, char* init_text) { - return htr_internal_AddTextToArray(&(s->Page.Inits), init_text); + if (UNLIKELY(htr_internal_AddTextToArray(&(s->Page.Inits), init_text) != 0)) + { + mssError(0, "HTR", "Failed to add script init."); + return -1; + } + + return 0; } @@ -930,7 +1104,13 @@ htrAddScriptInit(pHtSession s, char* init_text) int htrAddScriptWgtr(pHtSession s, char* wgtr_text) { - return htr_internal_AddTextToArray(&(s->Page.Wgtr), wgtr_text); + if (UNLIKELY(htr_internal_AddTextToArray(&(s->Page.Wgtr), wgtr_text) != 0)) + { + mssError(0, "HTR", "Failed to add to script in widget tree."); + return -1; + } + + return 0; } @@ -940,7 +1120,13 @@ htrAddScriptWgtr(pHtSession s, char* wgtr_text) int htrAddScriptCleanup(pHtSession s, char* init_text) { - return htr_internal_AddTextToArray(&(s->Page.Cleanups), init_text); + if (UNLIKELY(htr_internal_AddTextToArray(&(s->Page.Cleanups), init_text) != 0)) + { + mssError(0, "HTR", "Failed to add script clean up."); + return -1; + } + + return 0; } @@ -951,46 +1137,57 @@ htrAddScriptCleanup(pHtSession s, char* init_text) *** -- drvname is no longer used, just ignored. ***/ int -htrAddEventHandlerFunction(pHtSession s, char* event_src, char* event, char* drvname, char* function) +htrAddEventHandlerFunction(pHtSession s, char* event_src, char* event, char* drv_name, char* function) { pHtDomEvent e = NULL; - pHtDomEvent e_srch; - int i,cnt; /** Look it up? ** (GRB note -- is this slow enough to need an XHashTable?) + ** (Israel note -- idk, but it hasn't been a problem over the + ** past 19 years, so it's probably fine.) **/ - cnt = xaCount(&s->Page.EventHandlers); - for(i=0;iPage.EventHandlers); + for (unsigned int i = 0u; i < n_event_handlers; i++) { - e_srch = (pHtDomEvent)xaGetItem(&s->Page.EventHandlers,i); - if (!strcmp(event, e_srch->DomEvent)) + pHtDomEvent event_handler = checkPtr(xaGetItem(&s->Page.EventHandlers, i)); + if (event_handler == NULL) goto err; + if (strcmp(event, event_handler->DomEvent) == 0) { - e = e_srch; + e = event_handler; break; } } - /** Make a new one? **/ - if (!e) + /** Make a new event handler, if needed. **/ + if (e == NULL) { - e = (pHtDomEvent)nmMalloc(sizeof(HtDomEvent)); - if (!e) return -1; + e = (pHtDomEvent)checkPtr(nmMalloc(sizeof(HtDomEvent))); + if (e == NULL) goto err; strtcpy(e->DomEvent, event, sizeof(e->DomEvent)); - xaInit(&e->Handlers,64); - xaAddItem(&s->Page.EventHandlers, e); + if (check(xaInit(&e->Handlers,64)) != 0) goto err; + if (checkNeg(xaAddItem(&s->Page.EventHandlers, e)) < 0) goto err; } /** Add our handler **/ - cnt = xaCount(&e->Handlers); - for(i=0;iHandlers); + for (unsigned int i = 0u; i < n_handlers; i++) { - if (!strcmp(function, (char*)xaGetItem(&e->Handlers,i))) + char* handler = checkPtr(xaGetItem(&e->Handlers, i)); + if (handler == NULL) goto err; + if (strcmp(function, handler) == 0) return 0; } - xaAddItem(&e->Handlers, function); + if (checkNeg(xaAddItem(&e->Handlers, function)) < 0) goto err; - return 0; + /** Success. **/ + return 0; + + err: + mssError(1, "HTR", + "Failed to add %s event handler function \'%s()\' to %s for driver %s.", + event, function, event_src, drv_name + ); + return -1; } @@ -1005,84 +1202,6 @@ htrDisableBody(pHtSession s) } -/*** htrAddEvent - adds an event to a driver. - ***/ -int -htrAddEvent(pHtDriver drv, char* event_name) - { - pHtEventAction event; - - /** Create the action **/ - event = (pHtEventAction)nmSysMalloc(sizeof(HtEventAction)); - if (!event) return -1; - memccpy(event->Name, event_name, 0, 31); - event->Name[31] = '\0'; - xaInit(&(event->Parameters),16); - xaAddItem(&drv->Events, (void*)event); - - return 0; - } - - -/*** htrAddAction - adds an action to a widget. - ***/ -int -htrAddAction(pHtDriver drv, char* action_name) - { - pHtEventAction action; - - /** Create the action **/ - action = (pHtEventAction)nmSysMalloc(sizeof(HtEventAction)); - if (!action) return -1; - memccpy(action->Name, action_name, 0, 31); - action->Name[31] = '\0'; - xaInit(&(action->Parameters),16); - xaAddItem(&drv->Actions, (void*)action); - - return 0; - } - - -/*** htrAddParam - adds a parameter to a widget's action or event - ***/ -int -htrAddParam(pHtDriver drv, char* eventaction, char* param_name, int datatype) - { - pHtEventAction ea = NULL; - int i; - pHtParam p; - - /** Look for a matching event/action **/ - for(i=0;iActions.nItems;i++) - { - if (!strcmp(((pHtEventAction)(drv->Actions.Items[i]))->Name, eventaction)) - { - ea = (pHtEventAction)(drv->Actions.Items[i]); - break; - } - } - if (!ea) for(i=0;iEvents.nItems;i++) - { - if (!strcmp(((pHtEventAction)(drv->Events.Items[i]))->Name, eventaction)) - { - ea = (pHtEventAction)(drv->Events.Items[i]); - break; - } - } - if (!ea) return -1; - - /** Add the parameter **/ - p = nmSysMalloc(sizeof(HtParam)); - if (!p) return -1; - memccpy(p->ParamName, param_name, 0, 31); - p->ParamName[31] = '\0'; - p->DataType = datatype; - xaAddItem(&(ea->Parameters), (void*)p); - - return 0; - } - - /*** htrAddBodyItemLayer_va - adds an entire "layer" to the document. A layer *** in the traditional netscape-4 sense of a layer, which might be implemented *** in a number of different ways in the actual user agent itself. @@ -1093,17 +1212,23 @@ htrAddBodyItemLayer_va(pHtSession s, int flags, char* id, int cnt, char* cls, co va_list va; /** Add the opening tag **/ - htrAddBodyItemLayerStart(s, flags, id, cnt, cls); + if (UNLIKELY(htrAddBodyItemLayerStart(s, flags, id, cnt, cls) != 0)) goto err; /** Add the content **/ va_start(va, fmt); - htr_internal_QPAddText(s, htrAddBodyItem, (char*)fmt, va); + const int rval = htr_internal_QPAddText(s, htrAddBodyItem, (char*)fmt, va); va_end(va); + if (UNLIKELY(rval != 0)) goto err; /** Add the closing tag **/ - htrAddBodyItemLayerEnd(s, flags); + if (UNLIKELY(htrAddBodyItemLayerEnd(s, flags) != 0)) goto err; - return 0; + /** Success. **/ + return 0; + + err: + mssError(0, "HTR", "Failed to add body item layer of format: \"%s\".", fmt); + return -1; } @@ -1115,28 +1240,47 @@ htrAddBodyItemLayer_va(pHtSession s, int flags, char* id, int cnt, char* cls, co *** PARAMETER WHICH IS A FORMAT STRING FOR THE LAYER'S ID!!! ***/ int -htrAddBodyItemLayerStart(pHtSession s, int flags, char* id, int cnt, char* cls) +htrAddBodyItemLayerStart(pHtSession s, int flags, char* id, int cnt, char* class) { - char* starttag; char id_sbuf[64]; + int rval = -1; + pQPSession error_session = NULL; + + /** Create a pQPSession to track errors from qpfPrintf. **/ + error_session = checkPtr(qpfOpenSession()); + if (error_session == NULL) goto end; - if(s->Capabilities.HTML40) + /** Pick a starting tag. **/ + const int use_iframe = (s->Capabilities.HTML40 && (flags & HTR_LAYER_F_DYNAMIC)); + const char* start_tag = (use_iframe) ? "iframe style='border:0'" : "div"; + + /** Add the starting tag. **/ + if (UNLIKELY(qpfPrintf(error_session, id_sbuf, sizeof(id_sbuf), id, cnt) < 0)) { - if (flags & HTR_LAYER_F_DYNAMIC) - starttag = "IFRAME frameBorder=\"0\""; - else - starttag = "DIV"; + mssError(1, "HTR", "Failed to format \"%s\" with %d", id, cnt); + qpfLogErrors(error_session); + goto end; } - else + if (UNLIKELY(htrAddBodyItem_va(s, + "<%STR %[class='%STR&HTE' %]id='%STR&HTE'>", + start_tag, (class != NULL), class, id_sbuf + ) != 0)) { - starttag = "DIV"; + mssError(0, "HTR", "Failed to write HTML tag to start body item."); + goto end; } - /** Add it. **/ - qpfPrintf(NULL, id_sbuf,sizeof(id_sbuf),id,cnt); - htrAddBodyItem_va(s, "<%STR %[class=\"%STR&HTE\" %]id=\"%STR&HTE\">", starttag, cls != NULL, cls, id_sbuf); + /** Success. **/ + rval = 0; - return 0; + end: + if (UNLIKELY(rval != 0)) mssError(0, "HTR", "Failed to add starting body item with id: %s.", id); + + /** Clean up. **/ + if (LIKELY(error_session != NULL)) check(qpfCloseSession(error_session)); /* Failure ignored. */ + + /** Done. **/ + return rval; } @@ -1146,23 +1290,18 @@ htrAddBodyItemLayerStart(pHtSession s, int flags, char* id, int cnt, char* cls) int htrAddBodyItemLayerEnd(pHtSession s, int flags) { - char* endtag; - - if(s->Capabilities.HTML40) - { - if (flags & HTR_LAYER_F_DYNAMIC) - endtag = "IFRAME"; - else - endtag = "DIV"; - } - else + /** Pick a starting tag. **/ + const int use_iframe = (s->Capabilities.HTML40 && (flags & HTR_LAYER_F_DYNAMIC)); + const char* end_tag = (use_iframe) ? "iframe" : "div"; + + /** Add it. **/ + if (UNLIKELY(htrAddBodyItem_va(s, "", end_tag) != 0)) { - endtag = "DIV"; + mssError(0, "HTR", "Failed to write HTML tag to end body item."); + return -1; } - /** Add it. **/ - htrAddBodyItem_va(s, "", endtag); - + /** Done. **/ return 0; } @@ -1173,43 +1312,53 @@ htrAddBodyItemLayerEnd(pHtSession s, int flags) int htrGetExpParams(pExpression exp, pXString xs) { - int i, first; - XArray objs, props; - char* obj; - char* prop; + int rval = -1; + XArray objs = { nAlloc: 0 }, props = { nAlloc: 0 }; /** setup **/ - xaInit(&objs, 16); - xaInit(&props, 16); + if (check(xaInit(&objs, 16)) != 0) goto end; + if (check(xaInit(&props, 16)) != 0) goto end; /** Find the properties accessed by the expression **/ - expGetPropList(exp, &objs, &props); + if (checkNeg(expGetPropList(exp, &objs, &props) < 0)) goto end; /** Build the list **/ - xsCopy(xs,"[",-1); - first=1; - for(i=0;iName); + + /** Clean up. **/ + for (unsigned int i = 0u; i < objs.nItems; i++) + if (LIKELY(objs.Items[i] != NULL)) nmSysFree(objs.Items[i]); + if (LIKELY(objs.nAlloc != 0)) check(xaDeInit(&objs)); /* Failure ignored. */ + for (unsigned int i = 0u; i < props.nItems; i++) + if (LIKELY(props.Items[i] != NULL)) nmSysFree(props.Items[i]); + if (LIKELY(props.nAlloc != 0)) check(xaDeInit(&props)); /* Failure ignored. */ + + /** Done. **/ + return rval; } @@ -1223,45 +1372,70 @@ htrGetExpParams(pExpression exp, pXString xs) int htrAddExpression(pHtSession s, char* objname, char* property, pExpression exp) { - int i,first; - XArray objs, props; - XString xs,exptxt; - char* obj; - char* prop; - - xaInit(&objs, 16); - xaInit(&props, 16); - xsInit(&xs); - xsInit(&exptxt); - expGetPropList(exp, &objs, &props); - - xsCopy(&xs,"[",-1); - first=1; - for(i=0;iNamespace->DName); - - for(i=0;iNamespace->DName + ) != 0)) + { + mssError(0, "HTR", "Failed to write the expresion item."); + cls = 0; + goto end; } - xaDeInit(&objs); - xaDeInit(&props); - xsDeInit(&xs); - xsDeInit(&exptxt); - return 0; + /** Success. **/ + rval = 0; + + end: + if (UNLIKELY(rval != 0)) mssError(cls, "HTR", "Failed to add expression \"%s\".", objname); + + /** Clean up. **/ + for (unsigned int i = 0u; i < objs.nItems; i++) + if (LIKELY(objs.Items[i] != NULL)) nmSysFree(objs.Items[i]); + if (LIKELY(objs.nAlloc != 0)) check(xaDeInit(&objs)); /* Failure ignored. */ + for (unsigned int i = 0u; i < props.nItems; i++) + if (LIKELY(props.Items[i] != NULL)) nmSysFree(props.Items[i]); + if (LIKELY(props.nAlloc != 0)) check(xaDeInit(&props)); /* Failure ignored. */ + if (LIKELY(xs.AllocLen != 0)) check(xsDeInit(&xs)); /* Failure ignored. */ + if (LIKELY(exptxt.AllocLen != 0)) check(xsDeInit(&exptxt)); /* Failure ignored. */ + + /** Done. **/ + return rval; } @@ -1271,16 +1445,27 @@ htrAddExpression(pHtSession s, char* objname, char* property, pExpression exp) int htrCheckAddExpression(pHtSession s, pWgtrNode tree, char* w_name, char* property) { - pExpression code; - if (wgtrGetPropertyType(tree,property) == DATA_T_CODE) { - wgtrGetPropertyValue(tree,property,DATA_T_CODE,POD(&code)); - htrAddExpression(s, w_name, property, code); + pExpression code; + if (UNLIKELY(wgtrGetPropertyValue(tree, property, DATA_T_CODE, POD(&code)) < 0)) + { + mssError(1, "HTR", "Failed to get property: '%s'", property); + goto err; + } + if (UNLIKELY(htrAddExpression(s, w_name, property, code) != 0)) goto err; return 1; } - return 0; + /** Success. **/ + return 0; + + err: + mssError(0, "HTR", + "Failed to add expression \"%s\" to widget \"%s\":\"%s\".", + property, tree->Name, tree->Type + ); + return -1; } @@ -1291,28 +1476,24 @@ htrCheckAddExpression(pHtSession s, pWgtrNode tree, char* w_name, char* property *** as panes and tab pages. ***/ int -htrRenderSubwidgets(pHtSession s, pWgtrNode widget, int zlevel) +htrRenderSubwidgets(pHtSession s, pWgtrNode widget, int z_level) { -// pObjQuery qy; -// pObject sub_widget_obj; - int i, count; - - /** Open the query for subwidgets **/ - /* - qy = objOpenQuery(widget_obj, "", NULL, NULL, NULL, 0); - if (qy) + /** Iterate through each sub_widget and call its render function. **/ + const unsigned int n_sub_widgets = widget->Children.nItems; + for (unsigned int i = 0u; i < n_sub_widgets; i++) { - while((sub_widget_obj = objQueryFetch(qy, O_RDONLY))) - { - htrRenderWidget(s, sub_widget_obj, zlevel, docname, layername); - objClose(sub_widget_obj); + pWgtrNode sub_widget = widget->Children.Items[i]; + if (UNLIKELY(htrRenderWidget(s, sub_widget, z_level) != 0)) + { + mssError(0, "HTR", + "Failed to render \"%s\":\"%s\", " + "child #%d/%d of \"%s\":\"%s\" (z: %d).", + sub_widget->Name, sub_widget->Type, + i + 1, n_sub_widgets, widget->Name, widget->Type, z_level + ); + return -1; } - objQueryClose(qy); } - **/ - count = xaCount(&(widget->Children)); - for (i=0;iChildren), i), zlevel); return 0; } @@ -1341,7 +1522,7 @@ htr_internal_GenInclude(pHtSession s, char* filename) include_file = objOpen(s->ObjSession, filename, O_RDONLY, 0600, "application/x-javascript"); if (include_file) { - htrQPrintf(s, "\n", path, buf[0], buf, slash+1); + htrQPrintf(s, "\t\n", path, buf[0], buf, slash+1); } return 0; @@ -1380,50 +1561,128 @@ htr_internal_GenInclude(pHtSession s, char* filename) ***/ int htr_internal_WriteWgtrProperty(pHtSession s, pWgtrNode tree, char* propname) - { - int t; - ObjData od; - int rval; - pExpression code; - XString exptxt; - XString proptxt; - - t = wgtrGetPropertyType(tree, propname); - if (t > 0) + { + /** Get type. **/ + const int type = wgtrGetPropertyType(tree, propname); + if (UNLIKELY(type < 0)) { - rval = wgtrGetPropertyValue(tree, propname, t, &od); - if (rval == 1) - { - /** null **/ - htrAddScriptWgtr_va(s, "%STR&SYM:null, ", propname); - } - else if (rval == 0) + mssError(1, "HTR", "Failed to get property type for property '%s'.", propname); + goto err; + } + + /** Get value. **/ + ObjData od; + const int result = wgtrGetPropertyValue(tree, propname, type, &od); + if (UNLIKELY(result < 0)) + { + mssError(1, "HTR", + "Failed to get property value for property '%s' of type %d.", + propname, type + ); + goto err; + } + + /** Value is null. **/ + if (result == 1) + { + if (UNLIKELY(htrAddScriptWgtr_va(s, "%STR&SYM:null, ", propname) != 0)) goto err; + return 0; /* success */ + } + + /** Write values by type. **/ + switch (type) + { + case DATA_T_INTEGER: + if (UNLIKELY(htrAddScriptWgtr_va(s, "%STR&SYM:%INT, ", propname, od.Integer) != 0)) goto err; + break; + + case DATA_T_STRING: + if (UNLIKELY(htrAddScriptWgtr_va(s, "%STR&SYM:'%STR&JSSTR', ", propname, od.String) != 0)) goto err; + break; + + case DATA_T_DOUBLE: + if (UNLIKELY(htrAddScriptWgtr_va(s, "%STR&SYM:%DBL, ", propname, od.Double) != 0)) goto err; + break; + + case DATA_T_DATETIME: + if (UNLIKELY(htrAddScriptWgtr_va(s, + "%STR&SYM:new Date(%LL, %LL, %LL, %LL, %LL, %LL), ", + propname, + (long long)od.DateTime->Part.Year + 1900, + (long long)od.DateTime->Part.Month, + (long long)od.DateTime->Part.Day + 1, + (long long)od.DateTime->Part.Hour, + (long long)od.DateTime->Part.Minute, + (long long)od.DateTime->Part.Second + ) != 0)) goto err; + break; + + case DATA_T_INTVEC: + if (UNLIKELY(htrAddScriptWgtr_va(s, "%STR&SYM:[", propname) != 0)) goto err; + for (unsigned int i = 0; i < od.IntVec->nIntegers; i++) + if (UNLIKELY(htrAddScriptWgtr_va(s, "%[, %]%INT", (i != 0), od.IntVec->Integers[i]) != 0)) goto err; + if (UNLIKELY(htrAddScriptWgtr(s, "], ") != 0)) goto err; + break; + + case DATA_T_STRINGVEC: + if (UNLIKELY(htrAddScriptWgtr_va(s, "%STR&SYM:[", propname) != 0)) goto err; + for (unsigned int i = 0; i < od.StringVec->nStrings; i++) + if (UNLIKELY(htrAddScriptWgtr_va(s, "%[, %]'%STR&JSSTR'", (i != 0), od.StringVec->Strings[i]) != 0)) goto err; + if (UNLIKELY(htrAddScriptWgtr(s, "], ") != 0)) goto err; + break; + + case DATA_T_CODE: { - switch(t) + pExpression code; + XString exptxt = { AllocLen: 0 }; + XString proptxt = { AllocLen: 0 }; + bool successful = false; + + /** Get the code data. **/ + if (UNLIKELY(wgtrGetPropertyValue(tree, propname, DATA_T_CODE, POD(&code)) < 0)) { - case DATA_T_INTEGER: - htrAddScriptWgtr_va(s, "%STR&SYM:%INT, ", propname, od.Integer); - break; - - case DATA_T_STRING: - htrAddScriptWgtr_va(s, "%STR&SYM:'%STR&JSSTR', ", propname, od.String); - break; - - case DATA_T_CODE: - wgtrGetPropertyValue(tree,propname,DATA_T_CODE,POD(&code)); - xsInit(&exptxt); - xsInit(&proptxt); - htrGetExpParams(code, &proptxt); - expGenerateText(code, NULL, xsWrite, &exptxt, '\0', "javascript", EXPR_F_RUNCLIENT); - htrAddScriptWgtr_va(s, "%STR&SYM:{val:null, exp:function(_this,_context){return ( %STR );}, props:%STR, revexp:null}, ", propname, exptxt.String, proptxt.String); - xsDeInit(&proptxt); - xsDeInit(&exptxt); - break; + mssError(1, "HTR", "Failed to get value for property '%s'", propname); + goto end; } + if (check(xsInit(&exptxt)) != 0) goto end; + if (check(xsInit(&proptxt)) != 0) goto end; + if (UNLIKELY(htrGetExpParams(code, &proptxt) != 0)) goto end; + if (UNLIKELY(expGenerateText(code, NULL, xsWrite, &exptxt, '\0', "javascript", EXPR_F_RUNCLIENT) != 0)) + { + mssError(0, "HTR", "Failed to generate expression text."); + goto end; + } + + /** Write a script to execute the code. **/ + if (UNLIKELY(htrAddScriptWgtr_va(s, + "%STR&SYM:{ val:null, exp:(_this, _context) => { return ( %STR ); }, props:%STR, revexp:null }, ", + propname, exptxt.String, proptxt.String + ) != 0)) goto end; + + /** Success. **/ + successful = true; + + end: /** Clean up. **/ + if (proptxt.AllocLen != 0) checkNeg(xsDeInit(&proptxt)); /* Failure ignored. */ + if (exptxt.AllocLen != 0) checkNeg(xsDeInit(&exptxt)); /* Failure ignored. */ + if (!successful) goto err; + break; } + + default: + fprintf(stderr, "Warning: skipping property '%s' with unhandled datatype %d.\n", propname, type); + break; } - - return 0; + + /** Success. **/ + return 0; + + err: + mssError(0, "HTR", + "Failed to write widget property \"%s\" to widget \"%s\":\"%s\".", + propname, tree->Name, tree->Type + ); + return -1; } @@ -1433,22 +1692,18 @@ htr_internal_WriteWgtrProperty(pHtSession s, pWgtrNode tree, char* propname) int htr_internal_BuildClientWgtr_r(pHtSession s, pWgtrNode tree, int indent) { - int i; int childcnt = xaCount(&tree->Children); - char* objinit; - char* ctrinit; pHtDMPrivateData inf = wgtrGetDMPrivateData(tree); pWgtrNode child; - int rendercnt; char* scope = NULL; char* scopename = NULL; char* propname; /** Check recursion **/ - if (thExcessiveRecursion()) + if (UNLIKELY(thExcessiveRecursion())) { mssError(1,"HTR","Could not render application: resource exhaustion occurred"); - return -1; + goto err; } /** Widget name scope **/ @@ -1460,10 +1715,10 @@ htr_internal_BuildClientWgtr_r(pHtSession s, pWgtrNode tree, int indent) wgtrGetPropertyValue(tree,"scope_name",DATA_T_STRING,POD(&scopename)); /** Deploy the widget **/ - objinit = inf?(inf->ObjectLinkage):NULL; - ctrinit = inf?(inf->ContainerLinkage):NULL; - htrAddScriptWgtr_va(s, - " %STR&*LEN{name:'%STR&SYM'%[, obj:%STR%]%[, cobj:%STR%]%[, scope:'%STR&JSSTR'%]%[, sn:'%STR&JSSTR'%], type:'%STR&JSSTR', vis:%STR, ctl:%STR%[, namespace:'%STR&SYM'%]", + const char* objinit = (inf != NULL) ? (inf->ObjectLinkage) : NULL; + const char* ctrinit = (inf != NULL) ? (inf->ContainerLinkage) : NULL; + const int result = htrAddScriptWgtr_va(s, + " %STR&*LEN{name:'%STR&SYM'%[, obj:%STR%]%[, cobj:%STR%]%[, scope:'%STR&JSSTR'%]%[, sn:'%STR&JSSTR'%], type:'%STR&JSSTR', vis:%BOOL, ctl:%BOOL%[, namespace:'%STR&SYM'%]", indent*4, " ", tree->Name, objinit, objinit, @@ -1471,52 +1726,69 @@ htr_internal_BuildClientWgtr_r(pHtSession s, pWgtrNode tree, int indent) scope, scope, scopename, scopename, tree->Type, - (tree->Flags & WGTR_F_NONVISUAL)?"false":"true", - (tree->Flags & WGTR_F_CONTROL)?"true":"false", + !(tree->Flags & WGTR_F_NONVISUAL), + (tree->Flags & WGTR_F_CONTROL), (!tree->Parent || strcmp(tree->Parent->Namespace, tree->Namespace)), tree->Namespace); + if (UNLIKELY(result != 0)) + { + mssError(0, "HTR", "Failed to write JS data."); + goto err; + } /** Parameters **/ - htrAddScriptWgtr_va(s, ", param:{"); + if (UNLIKELY(htrAddScriptWgtr(s, ", param:{") != 0)) goto err; if (!(tree->Flags & WGTR_F_NONVISUAL)) { - htr_internal_WriteWgtrProperty(s, tree, "x"); - htr_internal_WriteWgtrProperty(s, tree, "y"); - htr_internal_WriteWgtrProperty(s, tree, "width"); - htr_internal_WriteWgtrProperty(s, tree, "height"); - htr_internal_WriteWgtrProperty(s, tree, "r_x"); - htr_internal_WriteWgtrProperty(s, tree, "r_y"); - htr_internal_WriteWgtrProperty(s, tree, "r_width"); - htr_internal_WriteWgtrProperty(s, tree, "r_height"); + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "x") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "y") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "width") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "height") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "r_x") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "r_y") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "r_width") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "r_height") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "fl_width") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "fl_height") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "fl_scale_x") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "fl_scale_y") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "fl_scale_w") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "fl_scale_h") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "fl_parent_w") != 0)) goto err; + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, "fl_parent_h") != 0)) goto err; } propname = wgtrFirstPropertyName(tree); - while(propname) + while (propname != NULL) { - htr_internal_WriteWgtrProperty(s, tree, propname); + if (UNLIKELY(htr_internal_WriteWgtrProperty(s, tree, propname) != 0)) goto err; propname = wgtrNextPropertyName(tree); } - htrAddScriptWgtr_va(s, "}"); + if (UNLIKELY(htrAddScriptWgtr(s, "}") != 0)) goto err; /** ... and any subwidgets **/ //TODO: there's a glitch in this section in which a comma is placed after the last element of an array. - for(rendercnt=i=0;iChildren, i); if (child->RenderFlags & HT_WGTF_NORENDER) continue; rendercnt++; } - if (rendercnt) + if (LIKELY(rendercnt > 0)) { - htrAddScriptWgtr_va(s, ", sub:\n %STR&*LEN [\n", indent*4, " "); + if (htrAddScriptWgtr_va(s, + ", sub:\n %STR&*LEN [\n", + indent*4, " " + ) != 0) goto err; rendercnt = 0; - for(i=0;iChildren, i); if (child->RenderFlags & HT_WGTF_NORENDER) continue; rendercnt--; if (htr_internal_BuildClientWgtr_r(s, child, indent+1) < 0) - return -1; + goto err; if (rendercnt == 0) /* last one - no comma */ htrAddScriptWgtr(s, "\n"); else @@ -1526,22 +1798,39 @@ htr_internal_BuildClientWgtr_r(pHtSession s, pWgtrNode tree, int indent) } else { - htrAddScriptWgtr(s, "}"); + if (UNLIKELY(htrAddScriptWgtr(s, "}") != 0)) goto err; } - return 0; + /** Success. **/ + return 0; + + err: + mssError(0, "HTR", + "Failed to build client widget tree for widget \"%s\":\"%s\" (indent: %d).", + tree->Name, tree->Type, indent + ); + + return -1; } int htrBuildClientWgtr(pHtSession s, pWgtrNode tree) { + /** Build the JS for the tree. **/ + if (htrAddScriptInclude(s, "/sys/js/ht_utils_wgtr.js", 0) != 0) goto err; + if (htrAddScriptWgtr_va(s, " pre_%STR&SYM =\n", tree->DName) != 0) goto err; + if (htr_internal_BuildClientWgtr_r(s, tree, 0) != 0) goto err; + if (htrAddScriptWgtr(s, ";\n") != 0) goto err; - htrAddScriptInclude(s, "/sys/js/ht_utils_wgtr.js", 0); - htrAddScriptWgtr_va(s, " pre_%STR&SYM =\n", tree->DName); - htr_internal_BuildClientWgtr_r(s, tree, 0); - htrAddScriptWgtr(s, ";\n"); + /** Success. **/ + return 0; - return 0; + err: + mssError(0, "HTR", + "Failed to build client widget tree for widget \"%s\":\"%s\".", + tree->Name, tree->Type + ); + return -1; } @@ -1627,14 +1916,194 @@ htrQPrintf(pHtSession s, char* fmt, ...) if (!xs) return -1; va_start(va, fmt); - xsQPrintf_va(xs, fmt, va); + rval = xsQPrintf_va(xs, fmt, va); va_end(va); - rval = htrWrite(s, xsString(xs), -1); + if (rval >= 0) + rval = htrWrite(s, xsString(xs), -1); xsFree(xs); return rval; } +/*** Generates HTML for an error page displaying a caller-supplied message. + *** Use htrGetErrorHTML() instead if the message should come from the + *** calling thread's error stack. + *** + *** @param title The title of the error page. e.g. "An error occurred!" + *** @param err_str The error message to display, or NULL if unavailable. + *** @returns A pointer to a new string buffer allocated with nmSysMalloc(), + *** containing data that can be sent to a browser as HTML, or NULL in the + *** rare case that an error page absolutely could not be generated. + ***/ +char* +htrGetErrorHTMLMsg(char* title, char* err_str) + { + char* page_buf = NULL; + unsigned int n_lines = 1u; + pQPSession error_session = NULL; + + /** Defaults. **/ + if (title == NULL) title = "An error occurred!"; + if (err_str == NULL) err_str = "Failed to fetch error."; + + /** Count the number of lines in the error message. **/ + for (unsigned int i = 0u; err_str[i] != '\0'; i++) + { + if (err_str[i] == '\n') n_lines++; + } + + const char* page_format = "" + "" + "" + "Error" + "" + "" + "" + "" + "" + "" + "

Uh oh!

" + "

%STR&HTE

" + "

" + "Sorry for the inconvenience. If this issue persists, please contact " + "your organization's tech support and provide the error below." + "

" + "

Error message:

" + "
" + "
Copy
" + "
%STR&HTE
" + "
" + "" + ""; + + /** Allocate space for page. **/ + size_t page_buf_size = 0lu + + strlen(page_format) + + 10lu /* %POS (n_lines) */ + + strlen(title) + + strlen(err_str) + + 1lu; /* Null terminator. */ + page_buf = checkPtr(nmSysMalloc(page_buf_size)); + if (page_buf == NULL) goto clean_up; + + /** Write an error HTML for the user. **/ + error_session = checkPtr(qpfOpenSession()); /* Failure ignored. */ + // nmSysFree(checkPtr(nmSysMalloc(8))); + if (checkNeg(qpfPrintf_g( + error_session, &page_buf, &page_buf_size, &qpfSysMallocGrow, NULL, page_format, + n_lines, title, err_str + )) < 0) + { + if (LIKELY(error_session != NULL)) qpfLogErrors(error_session); + goto fail; + } + // nmSysFree(checkPtr(nmSysMalloc(8))); + + /** Success. **/ + goto clean_up; + + fail: + fprintf(stderr, "Failed to print error page. (Continuing...)"); + if (LIKELY(page_buf != NULL)) nmSysFree(page_buf); + page_buf = NULL; + + clean_up: + /** Clean up. **/ + if (LIKELY(error_session != NULL)) check(qpfCloseSession(error_session)); /* Failure ignored. */ + + /** Final fallback chain if we STILL couldn't create an error page. **/ + if (UNLIKELY(page_buf == NULL)) page_buf = checkPtr(nmSysStrdup("See server logs")); + if (UNLIKELY(page_buf == NULL)) page_buf = checkPtr(nmSysStrdup("err")); + if (UNLIKELY(page_buf == NULL)) page_buf = checkPtr(nmSysStrdup("!")); + + return page_buf; + } + + +/*** Generates HTML for an error page from the calling thread's error stack. + *** Used as a fallback if an error occurs during normal rendering. + *** + *** @param title The title of the error page. e.g. "An error occurred!" + *** @returns A pointer to a new string buffer allocated with nmSysMalloc(), + *** containing data that can be sent to a browser as HTML, or NULL in the + *** rare case that an error page absolutely could not be generated. + ***/ +char* +htrGetErrorHTML(char* title) + { + char* page_buf; + char* err_str = NULL; + pXString err_xs; + + /** Get the error string. **/ + err_xs = checkPtr(xsNew()); + if (err_xs != NULL && check(mssStringError(err_xs)) == 0 && check(xsTrim(err_xs)) == 0) + err_str = checkPtr(xsString(err_xs)); + + page_buf = htrGetErrorHTMLMsg(title, err_str); + + if (LIKELY(err_xs != NULL)) xsFree(err_xs); + + return page_buf; + } + /*** htrRender - generate an HTML document given the app structure subtree *** as an open ObjectSystem object. @@ -1658,19 +2127,18 @@ htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSe char* agent = NULL; char* classname = NULL; int rval; - pXString err_xs; /** What UA is on the other end of the connection? **/ agent = (char*)mssGetParam("User-Agent"); - if (!agent) + if (UNLIKELY(agent == NULL || agent[0] == '\0')) { mssError(1, "HTR", "User-Agent undefined in the session parameters"); return -1; } /** Initialize the session **/ - s = (pHtSession)nmMalloc(sizeof(HtSession)); - if (!s) return -1; + s = (pHtSession)checkPtr(nmMalloc(sizeof(HtSession))); + if (s == NULL) return -1; memset(s,0,sizeof(HtSession)); s->Params = params; s->ObjSession = obj_s; @@ -1786,13 +2254,8 @@ htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSe s->DisableBody = 0; /** first thing in the startup() function should be calling build_wgtr **/ - htrAddScriptInit_va(s, " build_wgtr_%STR&SYM();\n", - s->Namespace->DName); - /*htrAddScriptInit_va(s, "\n var nodes = wgtrNodeList(%STR&SYM);\n",*/ - htrAddScriptInit_va(s, "\n var ns = \"%STR&SYM\";\n", - /*" var rootname = \"%STR&SYM\";\n", */ - s->Namespace->DName /*, s->Namespace->DName */); - /*htrAddStylesheetItem(s, "\tdiv {position:absolute; visibility:inherit; overflow:hidden; }\n");*/ + htrAddScriptInit_va(s, "\tvar ns = '%STR&SYM';\n", s->Namespace->DName); + htrAddScriptInit_va(s, "\tbuild_wgtr_%STR&SYM();\n", s->Namespace->DName); /** Render the top-level widget -- the function that's run * underneath will be dependent upon what the widget @@ -1812,7 +2275,7 @@ htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSe #ifdef WGTR_DBG_WINDOW htrAddScriptWgtr_va(s, " wgtrWalk(%STR&SYM);\n", tree->Name); htrAddScriptWgtr(s, " ifcLoadDef(\"net/centrallix/button.ifc\");\n"); - htrAddStylesheetItem(s, "\t#dbgwnd {position: absolute; top: 400; left: 50;}\n"); + htrAddStylesheetItem(s, "\t\t#dbgwnd {position: absolute; top: 400; left: 50;}\n"); htrAddBodyItem(s, "
" "" "
\n"); @@ -1820,26 +2283,25 @@ htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSe /** Could not render? **/ - if (rval < 0) + if (UNLIKELY(rval < 0)) { - err_xs = xsNew(); - if (err_xs) - { - mssStringError(err_xs); - htrQPrintf(s, "Error

An Error occured while attempting to render this document


%STR&HTE
\r\n", xsString(err_xs)); - xsFree(err_xs); - } + /** Render an error page to gracefully recover from the error. **/ + char* error_title = "An error occurred while rendering the page."; + char* error_html = checkPtr(htrGetErrorHTML(error_title)); + if (UNLIKELY(error_html == NULL)) error_html = error_title; + checkNeg(htrQPrintf(s, "%STR", error_html)); /* Failure ignored. */ + if (LIKELY(error_html != error_title)) nmSysFree(error_html); + check(mssClearError()); /* Failure ignored. */ + goto end_free; } - /** Output the DOCTYPE for browsers supporting HTML 4.0 -- this will make them use HTML 4.0 Strict **/ - /** FIXME: should probably specify the DTD.... **/ - if(s->Capabilities.HTML40 && !s->Capabilities.Dom0IE) - htrWrite(s, "\n\n", -1); + /** Write the DOCTYPE header, enabling HTML 4.0 Strict mode. **/ + htrWriteConst(s, "\n\n"); - /** Write the HTML out... **/ + /** Write HTML header comment & license. **/ htrQPrintf(s, "\n\n" , cx__version); - htrQPrintf(s, "\n" + /** Write the start of the HTML document. **/ + htrQPrintf(s, "\n" "\n" - " \n" - " \n" - " \n" + "\t\n" + "\t\n" + "\t\n" + "\t\n" , cx__version); - htrWrite(s, " \n", -1); + htrWriteConst(s, "\t\n"); /** Write the HTML header items. **/ for(i=0;iPage.HtmlHeader.nItems;i++) { @@ -1872,19 +2336,26 @@ htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSe } /** Write the script globals **/ - htrWrite(s, "\n\n", -1); + htrWriteConst(s, "\t\n\n"); /** include ht_render.js **/ htr_internal_GenInclude(s, "/sys/js/ht_render.js"); @@ -1912,7 +2383,7 @@ htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSe sv = (pStrValue)(s->Page.Includes.Items[i]); htr_internal_GenInclude(s, sv->Name); } - htrWrite(s, "\n",-1); - htrWrite(s, "\n\n"); + htrWriteConst(s, "\nPage.HtmlBodyParams.nItems;i++) { ptr = (char*)(s->Page.HtmlBodyParams.Items[i]); @@ -2019,13 +2509,13 @@ htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSe /** work around the Netscape 4.x bug regarding page resizing **/ if(s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML) { - htrWrite(s, " onResize=\"location.reload()\"",-1); + htrWriteConst(s, " onResize=\"location.reload()\""); } - htrWrite(s, ">\n", -1); + htrWriteConst(s, ">\n"); } else { - htrWrite(s, "\n\n",-1); + htrWriteConst(s, "\n\n"); } /** Write the HTML body. **/ @@ -2038,13 +2528,14 @@ htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSe if (s->DisableBody == 0) { - htrWrite(s, "\n\n",-1); + htrWriteConst(s, "\n\n"); } else { - htrWrite(s, "\n\n",-1); + htrWriteConst(s, "\n\n"); } + end_free: /** Deinitialize the session and page structures **/ for(i=0;iPage.Functions.nItems;i++) { @@ -2124,18 +2615,20 @@ htrAllocDriver() pHtDriver drv; /** Allocate the driver structure **/ - drv = (pHtDriver)nmMalloc(sizeof(HtDriver)); - if (!drv) return NULL; + drv = (pHtDriver)checkPtr(nmMalloc(sizeof(HtDriver))); + if (drv == NULL) goto err; memset(drv, 0, sizeof(HtDriver)); /** Init some of the basic array structures **/ - xaInit(&(drv->PosParams),16); - xaInit(&(drv->Properties),16); - xaInit(&(drv->Events),16); - xaInit(&(drv->Actions),16); - xaInit(&(drv->PseudoTypes), 4); + if (check(xaInit(&(drv->Properties), 16)) != 0) goto err; + if (check(xaInit(&(drv->PseudoTypes), 4)) != 0) goto err; + + /** Success. **/ + return drv; - return drv; + err: + mssError(1, "HTR", "Failed to allocate driver."); + return NULL; } @@ -2219,115 +2712,214 @@ htrInitialize() } -/*** htrGetBackground - gets the background image or color from the config - *** and converts it into a string we can use in the DHTML - *** - *** obj - an open Object for the config data - *** prefix - the prefix to add to 'bgcolor' and 'background' for the attrs - *** as_style - set to 1 to build the string as a style rather than as HTML - *** buf - the buffer to print into - *** buflen - the buffer length we can use +/*** htrGetBackground - Gets the background color/image the from the bgcolor + *** or background attributes on the provided widget node, then writes a CSS + *** rule or HTML attribute for the background into the provided buffer. *** - *** returns with buf set to "" if any error occurs. + *** @param tree The widget node to query for attributes. + *** @param prefix The prefix, prepended with an underscore (`_`) to any + *** attribute queried from the widget by this function. + *** @param as_style If true, writes a CSS rule (recommended). If false, + *** writes an HTML attribute, which is usually deprecated in + *** functionality in modern browsers. + *** @param buf The buffer where the written output should be stored. + *** A value or `""` is always written to buf as long as at + *** least one character of space was allocated. + *** @param buflen The allocated length of `buf`. + *** @returns 0 if successful, 1 if no background attribute was provided, or + *** -1 if an error occurs (and calls `mssError()`). ***/ int htrGetBackground(pWgtrNode tree, char* prefix, int as_style, char* buf, int buflen) { - char bgcolor_name[64]; - char background_name[128]; - char* bgcolor = "bgcolor"; - char* background = "background"; + pQPSession error_session = NULL; char* ptr; + int rval = -1; - /** init buf **/ - if (buflen < 1) return -1; + /** Initialize the buffer. **/ + if (UNLIKELY(buflen < 1)) + { + mssError(1, "HTR", "Failed! buflen (%d) < 1", buflen); + goto end; + } buf[0] = '\0'; + + /** Create a pQPSession to track errors from qpfPrintf. **/ + error_session = checkPtr(qpfOpenSession()); + if (error_session == NULL) goto end; + + /** Allocate space for attribute names. **/ + char bgcolor_buf[64]; + char background_buf[128]; + char* bgcolor = "bgcolor"; + char* background = "background"; /** Prefix supplied? **/ - if (prefix && *prefix) + if (prefix != NULL && prefix[0] != '\0') { - qpfPrintf(NULL, bgcolor_name,sizeof(bgcolor_name),"%STR&SYM_bgcolor",prefix); - qpfPrintf(NULL, background_name,sizeof(background_name),"%STR&SYM_background",prefix); - bgcolor = bgcolor_name; - background = background_name; + /** Write bgcolor_buf. **/ + if (UNLIKELY(qpfPrintf(error_session, + bgcolor_buf, sizeof(bgcolor_buf), + "%STR&SYM_bgcolor", prefix + ) < 0)) + { + mssError(1, "HTR", "Failed to write background color attribute name."); + qpfLogErrors(error_session); + goto end; + } + else qpfClearErrors(error_session); + + /** Write background_buf. **/ + if (UNLIKELY(qpfPrintf(error_session, + background_buf, sizeof(background_buf), + "%STR&SYM_background", prefix + ) < 0)) + { + mssError(1, "HTR", "Failed to write background color attribute name."); + qpfLogErrors(error_session); + goto end; + } + else qpfClearErrors(error_session); + + /** Update attribute name pointers. **/ + bgcolor = bgcolor_buf; + background = background_buf; } - /** Image? **/ + /** Search for different background types. **/ if (wgtrGetPropertyValue(tree, background, DATA_T_STRING, POD(&ptr)) == 0) - { - if (strpbrk(ptr,"\"'\n\r\t")) return -1; - if (as_style) - qpfPrintf(NULL, buf,buflen,"background-image: URL('%STR&CSSURL');",ptr); - else - qpfPrintf(NULL, buf,buflen,"background='%STR&HTE'",ptr); + { /* Background image. */ + /** Check for invalid characters in the string. **/ + if (UNLIKELY(strpbrk(ptr, "\"'\n\r\t") != NULL)) + { + mssError(1, "HTR", + "Value for attribute '%s' contains invalid characters: \"%s\"", + background, ptr + ); + goto end; + } + + /** Write background image. **/ + const char* format = (as_style) ? "background-image:URL('%STR&CSSURL');" : "background='%STR&HTE'"; + if (UNLIKELY(qpfPrintf(error_session, buf, buflen, format, ptr) < 0)) + { + mssError(1, "HTR", "Failed to write background image using format: \"%s\"", format); + qpfLogErrors(error_session); + goto end; + } + else qpfClearErrors(error_session); } else if (wgtrGetPropertyValue(tree, bgcolor, DATA_T_STRING, POD(&ptr)) == 0) - { - /** Background color **/ - if (strpbrk(ptr,"\"'\n\r\t;}<>&")) return -1; - if (as_style) - qpfPrintf(NULL, buf,buflen,"background-color: %STR&CSSVAL;",ptr); - else - qpfPrintf(NULL, buf,buflen,"bgColor='%STR&HTE'",ptr); + { /* Background color. */ + /** Check for invalid characters in the string. **/ + if (UNLIKELY(strpbrk(ptr, "\"'\n\r\t;}<>&") != NULL)) + { + mssError(1, "HTR", + "Value for attribute '%s' contains invalid characters: \"%s\"", + background, ptr + ); + goto end; + } + + /** Write background color. **/ + const char* format = (as_style) ? "background-color:%STR&CSSVAL;" : "bgColor='%STR&HTE'"; + if (UNLIKELY(qpfPrintf(error_session, buf, buflen, format, ptr) < 0)) + { + mssError(1, "HTR", "Failed to write background color using format: \"%s\"", format); + qpfLogErrors(error_session); + goto end; + } + else qpfClearErrors(error_session); } + /** Null background: return 1. **/ else { - return -1; + rval = 1; + goto free; } - return 0; + /** Success. **/ + rval = 0; + + end: + if (UNLIKELY(rval != 0)) + { + mssError(0, "HTR", + "Failed write \"%s\"-background for \"%s\":\"%s\" into buffer %p of length %d.", + prefix, tree->Name, tree->Type, buf, buflen + ); + } + + free: + /** Clean up. **/ + if (LIKELY(error_session != NULL)) qpfCloseSession(error_session); + + return rval; } -/*** htrGetBoolean() - a convenience routine to retrieve a boolean value - *** from the structure file. Boolean values can be specified as yes/no, - *** true/false, on/off, y/n, or 1/0. This routine checks for those. - *** Return value = 1 if true, 0 if false, -1 if error, and default_value - *** if not found. default_value can be set to -1 to indicate an error - *** on a nonexistent attribute. +/*** htrGetBoolean() - Get a widget attribute as a boolean value. ***/ int htrGetBoolean(pWgtrNode wgt, char* attrname, int default_value) { - int t; - int rval = default_value; - char* ptr; - int n; + int result = default_value; /** type of attr (need to check number if 1/0) **/ - t = wgtrGetPropertyType(wgt,attrname); - if (t < 0) return default_value; + const int type = wgtrGetPropertyType(wgt, attrname); + if (type < 0) goto end; /* Value not specified. */ - /** integer? **/ - if (t == DATA_T_INTEGER) + switch (type) { - if (wgtrGetPropertyValue(wgt,attrname,t,POD(&n)) == 0) + case DATA_T_INTEGER: { - rval = (n != 0); + int n; + if (wgtrGetPropertyValue(wgt, attrname, type, POD(&n)) != 0) break; + + /** Any nonzero int is true. **/ + result = (n != 0); + + goto end; /* Value found. */ } - } - else if (t == DATA_T_STRING) - { - /** string? **/ - if (wgtrGetPropertyValue(wgt,attrname,t,POD(&ptr)) == 0) + + case DATA_T_STRING: { - if (!strcasecmp(ptr,"yes") || !strcasecmp(ptr,"true") || !strcasecmp(ptr,"on") || !strcasecmp(ptr,"y")) - { - rval = 1; + char* ptr; + if (wgtrGetPropertyValue(wgt, attrname, type, POD(&ptr)) != 0) break; + + /** Check truthy strings. **/ + if (strcasecmp(ptr, "yes") == 0 + || strcasecmp(ptr, "true") == 0 + || strcasecmp(ptr, "y") == 0 + || strcasecmp(ptr, "on") == 0 + ) { + result = 1; + goto end; /* Value found. */ } - else if (!strcasecmp(ptr,"no") || !strcasecmp(ptr,"false") || !strcasecmp(ptr,"off") || !strcasecmp(ptr,"n")) - { - rval = 0; + + /** Check falsy strings. **/ + if (strcasecmp(ptr, "no") == 0 + || strcasecmp(ptr, "false") == 0 + || strcasecmp(ptr, "n") == 0 + || strcasecmp(ptr, "off") == 0 + ) { + result = 0; + goto end; /* Value found. */ } } - } - else - { - mssError(1,"HT","Invalid data type for attribute '%s'", attrname); - rval = -1; + + break; } - return rval; + /** Unrecognized boolean value: Warn and use the default. **/ + fprintf(stderr, + "Ignored unrecognized value for boolean attribute " + "'%s' of \"%s\":\"%s\" (defaulting to %d)", + attrname, wgt->Name, wgt->Type, result + ); + + end: + return result; } @@ -2360,7 +2952,8 @@ htr_internal_CheckDMPrivateData(pWgtrNode widget) if (!inf) { - inf = (pHtDMPrivateData)nmMalloc(sizeof(HtDMPrivateData)); + inf = (pHtDMPrivateData)checkPtr(nmMalloc(sizeof(HtDMPrivateData))); + if (inf == NULL) return NULL; memset(inf, 0, sizeof(HtDMPrivateData)); wgtrSetDMPrivateData(widget, inf); } @@ -2402,11 +2995,29 @@ htr_internal_FreeDMPrivateData(pWgtrNode widget) int htrAddWgtrObjLinkage(pHtSession s, pWgtrNode widget, char* linkage) { - pHtDMPrivateData inf = htr_internal_CheckDMPrivateData(widget); - - inf->ObjectLinkage = nmSysStrdup(objDataToStringTmp(DATA_T_STRING, linkage, DATA_F_QUOTED)); - - return 0; + /** Get private data. **/ + pHtDMPrivateData inf = checkPtr(htr_internal_CheckDMPrivateData(widget)); + if (inf == NULL) goto err; + + /** Get temporary string data. **/ + char* str_tmp = checkPtr(objDataToStringTmp(DATA_T_STRING, linkage, DATA_F_QUOTED)); + if (str_tmp == NULL) goto err; + + /** Dup string data. **/ + char* str = checkPtr(nmSysStrdup(str_tmp)); + if (str == NULL) goto err; + + /** Set string data. **/ + inf->ObjectLinkage = str; + + return 0; + + err: + mssError(1, "HTR", + "Failed to add object linkage \"%s\" to widget \"%s\":\"%s\".", + linkage, widget->Name, widget->Type + ); + return -1; } @@ -2417,12 +3028,45 @@ htrAddWgtrObjLinkage_va(pHtSession s, pWgtrNode widget, char* fmt, ...) { va_list va; char buf[256]; + int rval = -1, tmp; + pQPSession error_session = NULL; + + /** Create a pQPSession to track errors from qpfPrintf. **/ + error_session = checkPtr(qpfOpenSession()); + if (error_session == NULL) goto end; + /** Process the provided format. **/ va_start(va, fmt); - qpfPrintf_va(NULL, buf, sizeof(buf), fmt, va); + tmp = qpfPrintf_va(error_session, buf, sizeof(buf), fmt, va); va_end(va); - - return htrAddWgtrObjLinkage(s, widget, buf); + if (UNLIKELY(tmp < 0)) + { + mssError(1, "HTR", "qpfPrintf_va() failed to format: \"%s\"", fmt); + qpfLogErrors(error_session); + rval = tmp; + goto end; + } + + /** Add the linkage. **/ + tmp = htrAddWgtrObjLinkage(s, widget, buf); + if (UNLIKELY(tmp < 0)) goto end; + + /** Success. **/ + rval = 0; + + end: + if (rval != 0) + { + mssError(0, "HTR", + "Failed to add object linkage of format \"%s\" to widget \"%s\":\"%s\".", + fmt, widget->Name, widget->Type + ); + } + + /** Clean up. **/ + if (LIKELY(error_session != NULL)) qpfCloseSession(error_session); + + return rval; } @@ -2433,11 +3077,29 @@ htrAddWgtrObjLinkage_va(pHtSession s, pWgtrNode widget, char* fmt, ...) int htrAddWgtrCtrLinkage(pHtSession s, pWgtrNode widget, char* linkage) { - pHtDMPrivateData inf = htr_internal_CheckDMPrivateData(widget); - - inf->ContainerLinkage = nmSysStrdup(objDataToStringTmp(DATA_T_STRING, linkage, DATA_F_QUOTED)); + /** Get private data. **/ + pHtDMPrivateData inf = checkPtr(htr_internal_CheckDMPrivateData(widget)); + if (inf == NULL) goto err; + + /** Get temporary string data. **/ + char* str_tmp = checkPtr(objDataToStringTmp(DATA_T_STRING, linkage, DATA_F_QUOTED)); + if (str_tmp == NULL) goto err; + + /** Dup string data. **/ + char* str = checkPtr(nmSysStrdup(str_tmp)); + if (str == NULL) goto err; + + /** Set string data. **/ + inf->ContainerLinkage = str; + + return 0; - return 0; + err: + mssError(1, "HTR", + "Failed to add container linkage \"%s\" to widget \"%s\":\"%s\".", + linkage, widget->Name, widget->Type + ); + return -1; } @@ -2448,12 +3110,43 @@ htrAddWgtrCtrLinkage_va(pHtSession s, pWgtrNode widget, char* fmt, ...) { va_list va; char buf[256]; - + int rval = -1; + pQPSession error_session = NULL; + + /** Create a pQPSession to track errors from qpfPrintf. **/ + error_session = checkPtr(qpfOpenSession()); /* Failure ignored. */ + + /** Process the provided format. **/ va_start(va, fmt); - qpfPrintf_va(NULL, buf, sizeof(buf), fmt, va); + const int print_rval = qpfPrintf_va(error_session, buf, sizeof(buf), fmt, va); va_end(va); - - return htrAddWgtrCtrLinkage(s, widget, buf); + if (UNLIKELY(print_rval < 0)) + { + mssError(1, "HTR", "qpfPrintf_va() failed to format: \"%s\"", fmt); + qpfLogErrors(error_session); + rval = print_rval; + goto end; + } + + /** Add the linkage. **/ + if (UNLIKELY(htrAddWgtrCtrLinkage(s, widget, buf) < 0)) goto end; + + /** Success. **/ + rval = 0; + + end: + if (rval != 0) + { + mssError(0, "HTR", + "Failed to add container linkage of format \"%s\" to widget \"%s\":\"%s\".", + fmt, widget->Name, widget->Type + ); + } + + /** Clean up. **/ + if (LIKELY(error_session != NULL)) qpfCloseSession(error_session); + + return rval; } @@ -2464,15 +3157,31 @@ htrAddWgtrInit(pHtSession s, pWgtrNode widget, char* func, char* paramfmt, ...) { va_list va; char buf[256]; - pHtDMPrivateData inf = htr_internal_CheckDMPrivateData(widget); + pHtDMPrivateData inf = checkPtr(htr_internal_CheckDMPrivateData(widget)); + if (UNLIKELY(inf == NULL)) goto err; + + /** Process format. **/ inf->InitFunc = func; va_start(va, paramfmt); vsnprintf(buf, sizeof(buf), paramfmt, va); va_end(va); - inf->Param = nmSysStrdup(objDataToStringTmp(DATA_T_STRING, buf, DATA_F_QUOTED)); - - return 0; + + /** Process string data. **/ + char* str_tmp = checkPtr(objDataToStringTmp(DATA_T_STRING, buf, DATA_F_QUOTED)); + if (str_tmp == NULL) goto err; + char* str = checkPtr(nmSysStrdup(str_tmp)); + if (str == NULL) goto err; + inf->Param = str; + + return 0; + + err: + mssError(1, "HTR", + "Failed to set widget initialization function to %s(%s) for widget \"%s\":\"%s\".", + func, paramfmt, widget->Name, widget->Type + ); + return -1; } @@ -2486,8 +3195,8 @@ htrAddNamespace(pHtSession s, pWgtrNode container, char* nspace, int is_subns) char* ptr; /** Allocate a new namespace **/ - new_ns = (pHtNamespace)nmMalloc(sizeof(HtNamespace)); - if (!new_ns) return -1; + new_ns = (pHtNamespace)checkPtr(nmMalloc(sizeof(HtNamespace))); + if (UNLIKELY(new_ns == NULL)) goto err; new_ns->Parent = s->Namespace; strtcpy(new_ns->DName, nspace, sizeof(new_ns->DName)); @@ -2512,7 +3221,20 @@ htrAddNamespace(pHtSession s, pWgtrNode container, char* nspace, int is_subns) nspace /*, nspace */); #endif - return 0; + return 0; + + err:; + char context[152] = ""; + if (container != NULL) + snprintf(context, sizeof(context), + "to container widget \"%s\":\"%s\"", + container->Name, container->Type + ); + mssError(1, "HTR", + "Failed to add namespace \"%s\"%s.", + nspace, context + ); + return -1; } @@ -2679,23 +3401,70 @@ htrFormatElement(pHtSession s, pWgtrNode node, char* id, int flags, int x, int y if (wgtrGetPropertyValue(node, propname, DATA_T_STRING, POD(&strval)) == 0) shadow_angle = strtod(strval, NULL); - /** Generate the style CSS **/ - htrAddStylesheetItem_va(s, "\t%STR { left:%POSpx; top:%POSpx; %[width:%POSpx; %]%[height:%POSpx; %]%[z-index:%POS; %]%[color:%STR&CSSVAL; %]%[font-weight:bold; %]%[text-decoration:underline; %]%[font-style:italic; %]%[font:%STR&CSSVAL; %]%[font-size:%DBLpx; %]%[background-color:%STR&CSSVAL; %]%[background-image:url('%STR&CSSURL'); %]%[padding:%DBLpx; %]%[border:1px %STR&CSSVAL %STR&CSSVAL; %]%[border-radius:%DBLpx; %]%[text-align:%STR&CSSVAL; %]%[white-space:nowrap; %]%[box-shadow:%DBLpx %DBLpx %DBLpx %STR&CSSVAL%STR&CSSVAL; %]%[%STR %]}\n", - id, - x, y, w > 0, w, h > 0, h, z > 0, z, - *textcolor, textcolor, - !strcmp(style, "bold"), !strcmp(style, "underline"), !strcmp(style, "italic"), - *font, font, font_size > 0, font_size, - *bgcolor, bgcolor, *background, background, - padding > 0, padding, - *border_color, (*border_style)?border_style:"solid", border_color, border_radius > 0, border_radius, - *align, align, - !wrap, - (*shadow_color && shadow_radius > 0), sin(shadow_angle*M_PI/180)*shadow_offset, cos(shadow_angle*M_PI/180)*(-shadow_offset), shadow_radius, shadow_color, (!strcasecmp(shadow_location,"inside"))?" inset":"", - addl && *addl, addl - ); + /*** Generate the style CSS. border-box keeps any border and padding + *** below inside the width and height the layout allotted, rather than + *** growing the element past it. + ***/ + const int tmp = htrAddStylesheetItem_va(s, + "\t\t%STR { " + "box-sizing:border-box; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "%[width:"ht_flex_format"; %]" + "%[height:"ht_flex_format"; %]" + "%[z-index:%POS; %]" + "%[color:%STR&CSSVAL; %]" + "%[font-weight:bold; %]" + "%[text-decoration:underline; %]" + "%[font-style:italic; %]" + "%[font:%STR&CSSVAL; %]" + "%[font-size:%DBLpx; %]" + "%[background-color:%STR&CSSVAL; %]" + "%[background-image:url('%STR&CSSURL'); %]" + "%[padding:%DBLpx; %]" + "%[border:1px %STR&CSSVAL %STR&CSSVAL; %]" + "%[border-radius:%DBLpx; %]" + "%[text-align:%STR&CSSVAL; %]" + "%[white-space:nowrap; %]" + "%[box-shadow:" + "%DBLpx " + "%DBLpx " + "%DBLpx " + "%STR&CSSVAL%STR&CSSVAL; " + "%]" + "%[%STR %]" + "}\n", + id, + ht_flex_x(x, node), + ht_flex_y(y, node), + (w > 0), ht_flex_w(w, node), + (h > 0), ht_flex_h(h, node), + (z > 0), z, + (*textcolor), textcolor, + (strcmp(style, "bold") == 0), + (strcmp(style, "underline") == 0), + (strcmp(style, "italic") == 0), + (*font), font, + (font_size > 0), font_size, + (*bgcolor), bgcolor, + (*background), background, + (padding > 0), padding, + (*border_color), (*border_style) ? border_style : "solid", border_color, + (border_radius > 0), border_radius, + (*align), align, + (!wrap), + (*shadow_color && shadow_radius > 0), + sin(shadow_angle * M_PI/180) * shadow_offset, + cos(shadow_angle * M_PI/180) *(-shadow_offset), + shadow_radius, + shadow_color, (strcasecmp(shadow_location, "inside") == 0) ? " inset" : "", + (addl != NULL && addl[0] != '\0'), addl + ); + if (UNLIKELY(tmp != 0)) + { + mssError(0, "HTR", "Failed to write CSS while formatting element.\n"); + return -1; + } return 0; } - - diff --git a/centrallix/htmlgen/htdrv_alerter.c b/centrallix/htmlgen/htdrv_alerter.c index 1bba2c8e1..e4a9e23c2 100644 --- a/centrallix/htmlgen/htdrv_alerter.c +++ b/centrallix/htmlgen/htdrv_alerter.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,6 +29,12 @@ /* a message. */ /************************************************************************/ +#include + +#include "cxlib/mtsession.h" +#include "ht_render.h" +#include "wgtr.h" + /*** htalrtRender - generate the HTML code for the alert -- not much.. ***/ @@ -48,12 +42,35 @@ int htalrtRender(pHtSession s, pWgtrNode tree, int z) { - /** Get name **/ - htrAddScriptInit_va(s," alrt_init(wgtrGetNodeRef(ns,\"%STR&SYM\"));\n", wgtrGetName(tree)); + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) + { + mssError(1, "HTALRT", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; + } - htrAddScriptInclude(s,"/sys/js/htdrv_alerter.js",0); + /** Script include call **/ + if (htrAddScriptInclude(s, "/sys/js/htdrv_alerter.js", 0) != 0) + { + mssError(0, "HTALRT", "Failed to include JS."); + goto err; + } - return 0; + /** Script initialization call. **/ + if (htrAddScriptInit_va(s, "\talrt_init(wgtrGetNodeRef(ns, '%STR&SYM'));\n", wgtrGetName(tree)) != 0) + { + mssError(0, "HTALRT", "Failed to write JS init call."); + goto err; + } + + return 0; + + err: + mssError(0, "HTALRT", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); + return -1; } @@ -73,14 +90,6 @@ htalrtInitialize() strcpy(drv->WidgetName,"alerter"); drv->Render = htalrtRender; - /** Add actions **/ - htrAddAction(drv,"Alert"); - htrAddParam(drv,"Alert","Parameter",DATA_T_STRING); - htrAddAction(drv,"Confirm"); - htrAddParam(drv,"Confirm","Parameter",DATA_T_STRING); - //htrAddAction(drv,"ViewDOM"); - //htrAddParam(drv,"ViewDOM","Paramater",DATA_T_STRING); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_autolayout.c b/centrallix/htmlgen/htdrv_autolayout.c index 0dee2198b..a50cf36d1 100644 --- a/centrallix/htmlgen/htdrv_autolayout.c +++ b/centrallix/htmlgen/htdrv_autolayout.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2007 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,15 @@ /* Description: HTML Widget driver for autolayout of its subwidgets. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -65,19 +62,17 @@ htalRender(pHtSession s, pWgtrNode tree, int z) { char name[64]; int x=-1,y=-1,w,h; - int id; - pWgtrNode subtree; - int i; - if(!s->Capabilities.Dom0NS && !(s->Capabilities.Dom1HTML && s->Capabilities.CSS1)) + /** Get an id for this. **/ + const int id = (HTAL.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTAL","Netscape DOM or W3C DOM1 HTML and CSS support required"); - return -1; + mssError(1, "HTAL", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTAL.idcnt++); - /** Get x,y,w,h of this object. X and Y can be assumed to be zero if unset, ** but the wgtr Verify routine should have taken care of width/height for us ** if those were unspecified. Bark! @@ -87,63 +82,91 @@ htalRender(pHtSession s, pWgtrNode tree, int z) if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { mssError(1,"HTAL","Bark! Autolayout widget must have a 'width' property"); - return -1; + goto err; } if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) { mssError(1,"HTAL","Bark! Autolayout widget must have a 'height' property"); - return -1; + goto err; } /** Get name **/ strtcpy(name,wgtrGetName(tree),sizeof(name)); /** Add the stylesheet for the layer **/ - htrAddStylesheetItem_va(s,"\t#al%POSbase { POSITION:absolute; VISIBILITY:inherit; OVERFLOW:visible; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n", - //htrAddStylesheetItem_va(s,"\t#al%POSbase { POSITION:absolute; VISIBILITY:inherit; OVERFLOW:visible; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; CLIP:rect(%INTpx,%INTpx,%INTpx,%INTpx); Z-INDEX:%POS; }\n", - id,x,y,w,h, - //-1, w+1, h+1, -1, - z); + if (htrAddStylesheetItem_va(s, + "\t\t#al%POSbase { " + "position:absolute; " + "visibility:inherit; " + "overflow:visible; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n ", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z + ) != 0) + { + mssError(0, "HTAL", "Failed to write CSS."); + goto err; + } - /** Linkage **/ - htrAddWgtrObjLinkage_va(s, tree, "al%POSbase",id); + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "al%POSbase", id) != 0) + { + mssError(0, "HTAL", "Failed to add object linkage."); + goto err; + } /** Script include call **/ - htrAddScriptInclude(s, "/sys/js/htdrv_autolayout.js", 0); + if (htrAddScriptInclude(s, "/sys/js/htdrv_autolayout.js", 0) != 0) goto err; /** Script initialization call. **/ - htrAddScriptInit_va(s, " al_init(wgtrGetNodeRef(ns,'%STR&SYM'), {});\n", name); + if (htrAddScriptInit_va(s, "\tal_init(wgtrGetNodeRef(ns, '%STR&SYM'), {});\n", name) != 0) + { + mssError(0, "HTAL", "Failed to write JS init call."); + goto err; + } /** Start of container **/ - htrAddBodyItemLayerStart(s, 0, "al%POSbase", id, NULL); + if (htrAddBodyItemLayerStart(s, 0, "al%POSbase", id, NULL) != 0) + { + mssError(0, "HTAL", "Failed to start body container."); + goto err; + } /** Check for objects within this autolayout widget. **/ - for (i=0;iChildren));i++) + const int n_children = xaCount(&(tree->Children)); + for (int i = 0; i < n_children; i++) { - subtree = xaGetItem(&(tree->Children), i); + pWgtrNode subtree = xaGetItem(&(tree->Children), i); + if (subtree == NULL) continue; /* Not our problem. */ if (!strcmp(subtree->Type, "widget/autolayoutspacer")) subtree->RenderFlags |= HT_WGTF_NOOBJECT; - /* - else if(!strcmp(subtree->Type, "widget/repeat")) - { - for(rpti=0;rptiChildren));rpti++) - { - rptsubtree = xaGetItem(&(subtree->Children),rpti); - htrRenderWidget(s,rptsubtree, z+1); - //mssError(1,"HTAL","Found a subwidget to a repeat"); - } - htrRenderWidget(s, subtree, z+1); - }*/ - else - { - htrRenderWidget(s, subtree, z+1); - //mssError(1,"HTAL","Found a subwidget"); - } + else if (htrRenderWidget(s, subtree, z + 1) != 0) goto err; } + /** End of container **/ - htrAddBodyItemLayerEnd(s, 0); + if (htrAddBodyItemLayerEnd(s, 0) != 0) + { + mssError(0, "HTAL", "Failed to end body container."); + goto err; + } - return 0; + return 0; + + err: + mssError(0, "HTAL", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } diff --git a/centrallix/htmlgen/htdrv_button.c b/centrallix/htmlgen/htdrv_button.c index 67439566e..6991e1a7c 100644 --- a/centrallix/htmlgen/htdrv_button.c +++ b/centrallix/htmlgen/htdrv_button.c @@ -1,20 +1,71 @@ -#include +/************************************************************************/ +/* Centrallix Application Server System */ +/* Centrallix Core */ +/* */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ +/* */ +/* This program 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 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program 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 this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ +/* 02111-1307 USA */ +/* */ +/* A copy of the GNU General Public License has been included in this */ +/* distribution in the file "COPYING". */ +/* */ +/* Module: htdrv_button.c */ +/* Author: D. Kasper */ +/* Creation: June 21, 2007 */ +/* Description: HTML widget driver for a 'generic' button widget to */ +/* replace the imagebutton/textbutton widgets. */ +/************************************************************************/ + +#include #include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" + +#include "cxlib/datatypes.h" #include "cxlib/mtsession.h" #include "cxlib/strtcpy.h" +#include "expression.h" +#include "ht_render.h" +#include "obj.h" +#include "wgtr.h" + + +/*** Where an image sits relative to the button's text. 'image_position' and + *** the image-bearing 'type' values are two spellings of the same thing, so + *** both are parsed into this and the table below maps between them. + ***/ +enum htbtn_image_positions { Top=0, Bottom=1, Left=2, Right=3 }; + +static const struct + { + char* Position; /* the deprecated 'image_position' spelling */ + char* Type; /* the 'type' spelling */ + } + HTBTN_ImagePositions[] = + { + { "top", "topimage" }, + { "bottom", "bottomimage" }, + { "left", "leftimage" }, + { "right", "rightimage" }, + }; +#define HTBTN_N_IMAGE_POSITIONS ((int)(sizeof(HTBTN_ImagePositions) / sizeof(HTBTN_ImagePositions[0]))) /** globals **/ -static struct +static struct { - int idcnt; + int idcnt; } HTBTN; @@ -24,412 +75,486 @@ static struct int htbtnRender(pHtSession s, pWgtrNode tree, int z) { + /*** Scratch for every wgtrGetPropertyValue() string fetch below. The value + *** it points at is only valid until the next fetch, so each one is copied. + ***/ char* ptr; - char name[64]; - char type[64]; - char text[64]; - char fgcolor1[64]; - char fgcolor2[64]; - char bgcolor[128]; - char bgstyle[128]; - char disable_color[64]; - char n_img[128]; - char p_img[128]; - char c_img[128]; - char d_img[128]; - int x,y,w,h,spacing; - int id, i; - int is_ts = 1; - char* dptr; - int is_enabled = 1; - pExpression code; - int box_offset; - int clip_offset; - - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom0IE && !(s->Capabilities.Dom1HTML && s->Capabilities.Dom2CSS)) + + /** Get an id for this. **/ + const int id = (HTBTN.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTBTN","Netscape DOM or (W3C DOM1 HTML and W3C DOM2 CSS) support required"); - return -1; + mssError(1, "HTBTN", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTBTN.idcnt++); - - /** Get x,y,w,h of this object **/ - if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) + /** Get x, y, w, h of this object **/ + int x, y, w, h; + if (wgtrGetPropertyValue(tree, "x", DATA_T_INTEGER, POD(&x)) != 0) + { + mssError(1, "HTBTN", "Button widget must have an 'x' property."); + goto err; + } + if (wgtrGetPropertyValue(tree, "y", DATA_T_INTEGER, POD(&y)) != 0) + { + mssError(1, "HTBTN", "Button widget must have a 'y' property."); + goto err; + } + if (wgtrGetPropertyValue(tree, "width", DATA_T_INTEGER, POD(&w)) != 0) + { + mssError(1, "HTBTN", "Button widget must have a 'width' property."); + goto err; + } + if (wgtrGetPropertyValue(tree, "height", DATA_T_INTEGER, POD(&h)) != 0) h = -1; + if (tree->Flags & WGTR_F_AUTOHEIGHT) h = -1; /* let htdrv_button.js fit the text */ + + /** Get name. **/ + char name[64]; + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&ptr)) != 0) goto err; + strtcpy(name, ptr, sizeof(name)); + + /** Images. Point, click and disabled fall back to the normal image. **/ + char image[OBJSYS_MAX_PATH]; + char point_image[OBJSYS_MAX_PATH]; + char click_image[OBJSYS_MAX_PATH]; + char disabled_image[OBJSYS_MAX_PATH]; + bool has_image; + if (wgtrGetPropertyValue(tree, "image", DATA_T_STRING, POD(&ptr)) == 0) { - mssError(1,"HTBTN","Button widget must have an 'x' property"); - return -1; + strtcpy(image, ptr, sizeof(image)); + has_image = true; } - if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) + else + { + image[0] = '\0'; + has_image = false; + } + if (wgtrGetPropertyValue(tree, "pointimage", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(point_image, ptr, sizeof(point_image)); + else + strtcpy(point_image, image, sizeof(point_image)); + if (wgtrGetPropertyValue(tree, "clickimage", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(click_image, ptr, sizeof(click_image)); + else + strtcpy(click_image, point_image, sizeof(click_image)); + if (wgtrGetPropertyValue(tree, "disabledimage", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(disabled_image, ptr, sizeof(disabled_image)); + else + strtcpy(disabled_image, image, sizeof(disabled_image)); + + /** Where the image sits, from the deprecated 'image_position' spelling. **/ + enum htbtn_image_positions position = Top; + if (wgtrGetPropertyValue(tree, "image_position", DATA_T_STRING, POD(&ptr)) == 0) + for (int i = 0; i < HTBTN_N_IMAGE_POSITIONS; i++) + if (strcmp(ptr, HTBTN_ImagePositions[i].Position) == 0) position = i; + + /*** Button type. Defaults based on the widget name and whether an image + *** was given. "widget/imagebutton" is a deprecated path to a borderless, + *** text-free button. + ***/ + char type[64]; + if (wgtrGetPropertyValue(tree, "type", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(type, ptr, sizeof(type)); + else if (strcmp(tree->Type, "widget/imagebutton") == 0) + strcpy(type, "image"); + else if (has_image) + strtcpy(type, HTBTN_ImagePositions[position].Type, sizeof(type)); + else + strcpy(type, "text"); + + /** An explicit 'type' wins over 'image_position'. Fail if unknown. **/ + bool is_known_type = ( + strcmp(type, "text") == 0 + || strcmp(type, "image") == 0 + || strcmp(type, "textoverimage") == 0 + ); + for (int i = 0; i < HTBTN_N_IMAGE_POSITIONS; i++) + { + if (strcmp(type, HTBTN_ImagePositions[i].Type) == 0) + { + position = i; + is_known_type = true; + } + } + if (!is_known_type) + { + mssError(1, "HTBTN", "Unknown button type \"%s\".", type); + goto err; + } + const bool is_text_over_image = (strcmp(type, "textoverimage") == 0); + const bool has_text = (strcmp(type, "image") != 0); + /** With no text there is nothing to position the image against. **/ + if (!has_text) position = Top; + has_image = has_image && (strcmp(type, "text") != 0); + + /*** textoverimage paints its image as the cell's background, so it is the + *** only image-bearing type that emits no element. + ***/ + const bool has_img_element = (has_image && !is_text_over_image); + + /** Text. Can be a client-side expression. **/ + char text[64]; + text[0] = '\0'; + if (has_text) + { + ptr = "-"; + if (!htrCheckAddExpression(s, tree, name, "text") && + wgtrGetPropertyValue(tree, "text", DATA_T_STRING, POD(&ptr)) != 0) + { + mssError(1, "HTBTN", "Button widget must have a 'text' property."); + goto err; + } + strtcpy(text, ptr, sizeof(text)); + } + + /** Enabled. Can be a client-side expression. **/ + bool is_enabled; + if (wgtrGetPropertyType(tree, "enabled") == DATA_T_CODE) { - mssError(1,"HTBTN","Button widget must have a 'y' property"); - return -1; + pExpression enabled_code; + wgtrGetPropertyValue(tree, "enabled", DATA_T_CODE, POD(&enabled_code)); + htrAddExpression(s, name, "enabled", enabled_code); + is_enabled = false; /* Default to disabled while loading. */ } - if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) + else { - mssError(1,"HTBTN","Button widget must have a 'width' property"); - return -1; + is_enabled = (htrGetBoolean(tree, "enabled", true)); } - if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) h = -1; - if (wgtrGetPropertyType(tree,"enabled") == DATA_T_STRING && wgtrGetPropertyValue(tree,"enabled",DATA_T_STRING,POD(&ptr)) == 0 && ptr) + /** Threestate button or twostate? **/ + const bool is_tristate = (htrGetBoolean(tree, "tristate", true)); + + /** Auto-repeat the Click event while the button is held down. **/ + const bool do_repeat = (htrGetBoolean(tree, "repeat", false)); + + /*** Border radius, color, and style. For style, we only support outset, + *** solid, and none. Image-only buttons have never drawn a border. + ***/ + int border_radius; + char border_color[64]; + char border_style[32]; + if (wgtrGetPropertyValue(tree, "border_radius", DATA_T_INTEGER, POD(&border_radius)) != 0) + border_radius = 0; + if (wgtrGetPropertyValue(tree, "border_color", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(border_color, ptr, sizeof(border_color)); + else + strcpy(border_color, "#c0c0c0"); + if (wgtrGetPropertyValue(tree, "border_style", DATA_T_STRING, POD(&ptr)) == 0 + && (strcmp(ptr, "outset") == 0 || strcmp(ptr, "solid") == 0 || strcmp(ptr, "none") == 0)) + strtcpy(border_style, ptr, sizeof(border_style)); + else if (!has_text) + strcpy(border_style, "none"); + else + strcpy(border_style, "outset"); + + /** Text alignment. **/ + char text_align[16]; + if (wgtrGetPropertyValue(tree, "align", DATA_T_STRING, POD(&ptr)) == 0 + && (strcmp(ptr, "left") == 0 || strcmp(ptr, "right") == 0 || strcmp(ptr, "center") == 0)) + strtcpy(text_align, ptr, sizeof(text_align)); + else + strcpy(text_align, "center"); + + /** Background color/image. Returns 1 when none is set, which is fine. **/ + char background_style[128]; + if (htrGetBackground(tree, NULL, 1, background_style, sizeof(background_style)) < 0) goto err; + + /*** 'fgcolor1' is the text color and 'fgcolor2' its drop shadow, which is + *** only drawn while the button is enabled. + ***/ + char text_color[64]; + char shadow_color[64]; + char disabled_text_color[64]; + if (wgtrGetPropertyValue(tree, "fgcolor1", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(text_color, ptr, sizeof(text_color)); + else + strcpy(text_color, "white"); + if (wgtrGetPropertyValue(tree, "fgcolor2", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(shadow_color, ptr, sizeof(shadow_color)); + else + strcpy(shadow_color, "black"); + if (wgtrGetPropertyValue(tree, "disable_color", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(disabled_text_color, ptr, sizeof(disabled_text_color)); + else + strcpy(disabled_text_color, "#808080"); + + /** Tooltip **/ + char tooltip[256]; + if (wgtrGetPropertyValue(tree, "tooltip", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(tooltip, ptr, sizeof(tooltip)); + else + tooltip[0] = '\0'; + + /** Image sizing. 'spacing' carries the deprecated 'image_margin'. **/ + int image_width, image_height, spacing; + if (wgtrGetPropertyValue(tree, "image_width", DATA_T_INTEGER, POD(&image_width)) != 0) + image_width = 0; + if (wgtrGetPropertyValue(tree, "image_height", DATA_T_INTEGER, POD(&image_height)) != 0) + image_height = 0; + if (wgtrGetPropertyValue(tree, "spacing", DATA_T_INTEGER, POD(&spacing)) != 0) + spacing = 0; + + /*** A button carrying text is drawn as a framed cell, and reserves room for + *** that frame whether or not the border is currently visible. An image-only + *** button has no frame unless it asked for a border, so by default it hugs + *** its image the way imagebutton always has. + ***/ + const bool has_frame = (has_text || strcmp(border_style, "none") != 0); + const int cell_padding = (has_frame) ? 1 : 0; + const int frame_adjust = (has_frame) ? 3 : 0; /* the cell's 1px border and 1px padding, less 1px of legacy slop */ + + /*** An image-only button *is* its image, so the image defaults to filling the + *** cell. imagebutton only ever sized its image when a height was given, so + *** neither do we. Alongside text the image keeps its natural size. + ***/ + if (!has_text && h >= 0) { - if (!strcasecmp(ptr,"false") || !strcasecmp(ptr,"no")) is_enabled = 0; + if (image_width == 0) image_width = w - frame_adjust; + if (image_height == 0) image_height = h - frame_adjust; } - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(name,ptr,sizeof(name)); - - if (wgtrGetPropertyValue(tree,"type",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(type,ptr,sizeof(type)); + /** Supress invalid positive values. **/ + if (image_width < 0) image_width = 0; + if (image_height < 0) image_height = 0; + if (spacing < 0) spacing = 0; - /* if not a text only button */ - if(strcmp(type,"text")) + /** DOM Linkages **/ + if (htrAddWgtrObjLinkage_va(s, tree, "gb%POSpane", id) != 0) { - if (wgtrGetPropertyValue(tree,"image",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(n_img,ptr,sizeof(n_img)); + mssError(0, "HTBTN", "Failed to add object linkage."); + goto err; } - if (wgtrGetPropertyValue(tree,"pointimage",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(p_img,ptr,sizeof(p_img)); - else - strcpy(p_img, n_img); - - if (wgtrGetPropertyValue(tree,"clickimage",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(c_img,ptr,sizeof(c_img)); - else - strcpy(c_img, n_img); - - if (wgtrGetPropertyValue(tree,"disabledimage",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(d_img,ptr,sizeof(d_img)); - else - strcpy(d_img, n_img); - - /** Threestate button or twostate? **/ - if (wgtrGetPropertyValue(tree,"tristate",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"no")) is_ts = 0; - - if(strcmp(type,"image")) - if (wgtrGetPropertyValue(tree,"text",DATA_T_STRING,POD(&ptr)) != 0) - { - mssError(1,"HTBTN","Button widget must have a 'text' property"); - return -1; - } - strtcpy(text,ptr,sizeof(text)); - - /** Get fgnd colors 1,2, and background color **/ - htrGetBackground(tree, NULL, 0, bgcolor, sizeof(bgcolor)); - htrGetBackground(tree, NULL, 1, bgstyle, sizeof(bgstyle)); - - if (wgtrGetPropertyValue(tree,"fgcolor1",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(fgcolor1,ptr,sizeof(fgcolor1)); - else - strcpy(fgcolor1,"white"); - if (wgtrGetPropertyValue(tree,"fgcolor2",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(fgcolor2,ptr,sizeof(fgcolor2)); - else - strcpy(fgcolor2,"black"); - if (wgtrGetPropertyValue(tree,"disable_color",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(disable_color,ptr,sizeof(disable_color)); - else - strcpy(disable_color,"#808080"); - - /* spacing between image and text if both are supplied */ - if (wgtrGetPropertyValue(tree,"spacing",DATA_T_INTEGER,POD(&spacing)) != 0) spacing=5; - - /** User requesting expression for enabled? **/ - if (wgtrGetPropertyType(tree,"enabled") == DATA_T_CODE) - { - wgtrGetPropertyValue(tree,"enabled",DATA_T_CODE,POD(&code)); - is_enabled = 0; - htrAddExpression(s, name, "enabled", code); - } - - /* image only button - based on imagebutton */ - - if (!strcmp(type,"image") || !strcmp(type,"textoverimage")) - { - htrAddStylesheetItem_va(s,"\t#gb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z); - - htrAddScriptGlobal(s, "gb_cur_img", "null", 0); - htrAddScriptGlobal(s, "gb_current", "null", 0); - htrAddWgtrObjLinkage_va(s, tree, "gb%POSpane", id); - - /** User requesting expression for enabled? **/ - if (wgtrGetPropertyType(tree,"enabled") == DATA_T_CODE) - { - wgtrGetPropertyValue(tree,"enabled",DATA_T_CODE,POD(&code)); - is_enabled = 0; - htrAddExpression(s, name, "enabled", code); - } - /** Widget Tree Stuff **/ - dptr = wgtrGetDName(tree); - htrAddScriptInit_va(s, " %STR&SYM = wgtrGetNodeRef(ns,'%STR&SYM');\n", dptr, name); - - if(!strcmp(type,"image")) htrAddScriptInit_va(s," gb_init({layer:%STR&SYM, n:'%STR&JSSTR', p:'%STR&JSSTR', c:'%STR&JSSTR', d:'%STR&JSSTR', width:%INT, height:%INT, name:'%STR&SYM', enable:%INT, type:'%STR&JSSTR', text:'%STR&JSSTR'});\n", dptr, n_img, p_img, c_img, d_img, w, h, name,is_enabled,type,text); - /* text over image needs second layer */ - else htrAddScriptInit_va(s," gb_init({layer:\"%STR&SYM\", layer2:htr_subel(%STR&SYM, \"gb%POSpane2\"), n:'%STR&JSSTR', p:'%STR&JSSTR', c:'%STR&JSSTR', d:'%STR&JSSTR', width:%INT, height:%INT, name:'%STR&SYM', enable:%INT, type:'%STR&JSSTR', text:'%STR&JSSTR'});\n", dptr, dptr, id, n_img, p_img, c_img, d_img, w, h, name,is_enabled,type,text); - - /** Include the javascript code for the button **/ - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - htrAddScriptInclude(s, "/sys/js/htdrv_button.js", 0); - - /** HTML body
elements for the layers. **/ - if(!strcmp(type,"image")||!strcmp(type,"textoverimage")) - { - if (h < 0) - if(is_enabled) - htrAddBodyItem_va(s,"
\n",id,n_img); - else - htrAddBodyItem_va(s,"
\n",id,d_img); - else - if(is_enabled) - htrAddBodyItem_va(s,"
\n",id,n_img,w,h); - else - htrAddBodyItem_va(s,"
\n",id,d_img,w,h); - } - /* text over image */ - if(!strcmp(type,"textoverimage")) - { - htrAddStylesheetItem_va(s,"\t#gb%POSpane2 { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,0,0,w,z+1); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h,fgcolor1,text); - } - else - htrAddBodyItem_va(s,"
"); - } + /*** Write the JS includes for the button. Its globals are declared in + *** htdrv_button.js rather than emitted here. + ***/ + if (htrAddScriptInclude(s, "/sys/js/htdrv_button.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0) != 0) goto err; + + /** Write CSS for the container that will hold the button. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane { " + "position:absolute; " + "visibility:inherit; " + "display:table; " + "%[overflow:hidden; %]" + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "%[height:"ht_flex_format"; %]" + "z-index:%POS; " + "}\n", + id, + (!has_text), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w - frame_adjust, tree), + (h >= 0), ht_flex_h(h - frame_adjust, tree), + z + ) != 0) + { + mssError(0, "HTBTN", "Failed to write CSS for main button pane."); + goto err; + } - /* other types of buttons - based on textbutton */ + /*** Cursor and click animation. Both key off a class the JS maintains, + *** so they follow the button when it is enabled or disabled at runtime. + ***/ + if (htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane:not(.gb_disabled) { cursor:pointer; }\n" + "\t\t#gb%POSpane:not(.gb_disabled):active { transform: translate(1px, 1px); }\n", + id, + id + ) != 0) + { + mssError(0, "HTBTN", "Failed to write CSS for cursor and click animation."); + goto err; + } - else - + /** Write CSS for the button content, inside the border. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane .cell { " + "height:100%%; " + "width:100%%; " + "vertical-align:middle; " + "display:table-cell; " + "padding:%POSpx; " + "font-weight:bold; " + "text-align:%STR; " + "border-width:1px; " + "border-style:%STR&CSSVAL; " + "border-color:%STR&CSSVAL; " + "border-radius:%INTpx; " + "color:%STR&CSSVAL; " + "%[text-shadow:1px 1px %STR&CSSVAL; %]" + "%[background-image:URL(%STR&CSSURL); background-size:100%% 100%%; %]" + "%STR " + "}\n", + id, + cell_padding, + text_align, + border_style, + border_color, + border_radius, + (is_enabled) ? text_color : disabled_text_color, + (is_enabled), shadow_color, + (is_text_over_image && has_image), (is_enabled) ? image : disabled_image, + background_style + ) != 0) + { + mssError(0, "HTBTN", "Failed to write CSS for button content."); + goto err; + } + + /** Write CSS for image on the button. **/ + if (has_img_element && (image_width != 0 || image_height != 0 || spacing != 0)) + { + if (htrAddStylesheetItem_va(s, + "\t\t#gb%POSpane img { " + "%[height:%POSpx; %]" + "%[width:%POSpx; %]" + "%[margin:%POSpx; %]" + "}\n", + id, + (image_height != 0), image_height, + (image_width != 0), image_width, + (spacing != 0), spacing + ) != 0) { - /** box adjustment... arrgh **/ - if (s->Capabilities.CSSBox) - box_offset = 1; - else - box_offset = 0; - clip_offset = s->Capabilities.CSSClip?1:0; - - htrAddScriptGlobal(s, "gb_current", "null", 0); - - /** DOM Linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "gb%POSpane",id); - - /** Include the javascript code for the button **/ - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - htrAddScriptInclude(s, "/sys/js/htdrv_button.js", 0); - - dptr = wgtrGetDName(tree); - htrAddScriptInit_va(s, " %STR&SYM = wgtrGetNodeRef(ns,'%STR&SYM');\n", dptr, name); - - if(s->Capabilities.Dom0NS) - { - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#gb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",id,x,y,w,z); - htrAddStylesheetItem_va(s,"\t#gb%POSpane2 { POSITION:absolute; VISIBILITY:%STR; LEFT:-1; TOP:-1; WIDTH:%POS; Z-INDEX:%POS; }\n",id,is_enabled?"inherit":"hidden",w-1,z+1); - htrAddStylesheetItem_va(s,"\t#gb%POSpane3 { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; WIDTH:%POS; Z-INDEX:%POS; }\n",id,is_enabled?"hidden":"inherit",w-1,z+1); - htrAddStylesheetItem_va(s,"\t#gb%POStop { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:%POS; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",w,z+2); - htrAddStylesheetItem_va(s,"\t#gb%POSbtm { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:%POS; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",w,z+2); - htrAddStylesheetItem_va(s,"\t#gb%POSrgt { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:1; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",z+2); - htrAddStylesheetItem_va(s,"\t#gb%POSlft { POSITION:absolute; VISIBILITY:%STR; LEFT:0; TOP:0; HEIGHT:1; WIDTH:1; Z-INDEX:%POS; }\n",id,is_ts?"hidden":"inherit",z+2); - - /** Script initialization call. **/ - htrAddScriptInit_va(s, " gb_init({layer:%STR&SYM, layer2:htr_subel(%STR&SYM, \"gb%POSpane2\"), layer3:htr_subel(%STR&SYM, \"gb%POSpane3\"), top:htr_subel(%STR&SYM, \"gb%POStop\"), bottom:htr_subel(%STR&SYM, \"gb%POSbtm\"), right:htr_subel(%STR&SYM, \"gb%POSrgt\"), left:htr_subel(%STR&SYM, \"gb%POSlft\"), width:%INT, height:%INT, tristate:%INT, name:\"%STR&SYM\", text:'%STR&JSSTR', n:\"%STR&JSSTR\", p:\"%STR&JSSTR\", c:\"%STR&JSSTR\", d:\"%STR&JSSTR\", type:\"%STR&JSSTR\"});\n", - dptr, dptr, id, dptr, id, dptr, id, dptr, id, dptr, id, dptr, id, w, h, is_ts, name, text,n_img,p_img,c_img,d_img,type); - - /** HTML body
elements for the layers. **/ - if (h >= 0) - { - if(!strcmp(type,"text")) - { - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,bgcolor,w,fgcolor2,text,h); - htrAddBodyItem_va(s, "
%STR&HTE
\n
",id,w,fgcolor1,text,h); - htrAddBodyItem_va(s, "
%STR&HTE
\n
",id,w,disable_color,text,h); - } - else if(!strcmp(type,"image")) - { - htrAddBodyItem_va(s,"
\n",id,bgcolor,w,n_img,w,h,h); - htrAddBodyItem_va(s, "
\n
",id,w,n_img,w,h,h); - htrAddBodyItem_va(s, "
\n
",id,w,n_img,w,h,h); - } - else if(!strcmp(type,"topimage")) - { - htrAddBodyItem_va(s,"

%STR&HTE
\n",id,bgcolor,w,n_img,fgcolor2,text,h); - htrAddBodyItem_va(s, "

%STR&HTE
\n
",id,w,n_img,fgcolor1,text,h); - htrAddBodyItem_va(s, "

%STR&HTE
\n
",id,w,n_img,disable_color,text,h); - } - else if(!strcmp(type,"bottomimage")) - { - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,bgcolor,w,fgcolor2,text,n_img,h); - htrAddBodyItem_va(s, "
%STR&HTE
\n
",id,w,fgcolor1,text,n_img,h); - htrAddBodyItem_va(s, "
%STR&HTE
\n
",id,w,disable_color,text,n_img,h); - } - else if(!strcmp(type,"leftimage")) - { - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,spacing,bgcolor,w,n_img,fgcolor2,text,h); - htrAddBodyItem_va(s, "
%STR&HTE
\n
",id,spacing,w,n_img,fgcolor1,text,h); - htrAddBodyItem_va(s, "
%STR&HTE
\n
",id,spacing,w,n_img,disable_color,text,h); - } - else if(!strcmp(type,"rightimage")) - { - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,spacing,bgcolor,w,fgcolor2,text,n_img,h); - htrAddBodyItem_va(s, "
%STR&HTE
\n
",id,spacing,w,fgcolor1,text,n_img,h); - htrAddBodyItem_va(s, "
%STR&HTE
\n
",id,spacing,w,disable_color,text,n_img,h); - } - else if(!strcmp(type,"textoverimage")) - { - htrAddBodyItem_va(s,"
\n",id,bgcolor,w,n_img,w,h,h); - htrAddBodyItem_va(s, "
\n
",id,w,n_img,w,h,h); - htrAddBodyItem_va(s, "
\n
",id,w,n_img,w,h,h); - htrAddBodyItem_va(s, "

%STR&HTE

",id,fgcolor1,text); - } - else - { - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,bgcolor,w,fgcolor2,text,h); - htrAddBodyItem_va(s, "
%STR&HTE
\n
",id,w,fgcolor1,text,h); - htrAddBodyItem_va(s, "
%STR&HTE
\n
",id,w,disable_color,text,h); - } - } - else - { - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,bgcolor,w,fgcolor2,text); - htrAddBodyItem_va(s,"
%STR&HTE
\n
",id,w,fgcolor1,text); - htrAddBodyItem_va(s,"
%STR&HTE
\n
",id,w,disable_color,text); - } - htrAddBodyItem_va(s,"
\n",id,w); - htrAddBodyItem_va(s,"
\n",id,w); - htrAddBodyItem_va(s,"
\n",id,(h<0)?1:h); - htrAddBodyItem_va(s,"
\n",id,(h<0)?1:h); - htrAddBodyItem(s, "
\n"); - } - else if(s->Capabilities.CSS2) - { - if(h >=0 ) - { - htrAddStylesheetItem_va(s,"\t#gb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; OVERFLOW:hidden; clip:rect(%INTpx %INTpx %INTpx %INTpx)}\n",id,x,y,w-1-2*box_offset,z,0,w-1-2*box_offset+2*clip_offset,h-1-2*box_offset+2*clip_offset,0); - htrAddStylesheetItem_va(s,"\t#gb%POSpane2, #gb%POSpane3 { height: %POSpx;}\n",id,id,h-3); - htrAddStylesheetItem_va(s,"\t#gb%POSpane { height: %POSpx;}\n",id,h-1-2*box_offset); - } - else - { - htrAddStylesheetItem_va(s,"\t#gb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; OVERFLOW:hidden; clip:rect(%INTpx %INTpx auto %INTpx)}\n",id,x,y,w-1-2*box_offset,z,0,w-1-2*box_offset+2*clip_offset,0); - } - htrAddStylesheetItem_va(s,"\t#gb%POSpane, #gb%POSpane2, #gb%POSpane3 { cursor:default; text-align: center; }\n",id,id,id); - htrAddStylesheetItem_va(s,"\t#gb%POSpane { %STR border-width: 1px; border-style: solid; border-color: white gray gray white; }\n",id,bgstyle); - /*htrAddStylesheetItem_va(s,"\t#gb%dpane { color: %s; }\n",id,fgcolor2);*/ - htrAddStylesheetItem_va(s,"\t#gb%POSpane2 { VISIBILITY: %STR; Z-INDEX: %INT; position: absolute; left:-1px; top: -1px; width:%POSpx; }\n",id,is_enabled?"inherit":"hidden",z+1,w-3); - htrAddStylesheetItem_va(s,"\t#gb%POSpane3 { VISIBILITY: %STR; Z-INDEX: %INT; position: absolute; left:0px; top: 0px; width:%POSpx; }\n",id,is_enabled?"hidden":"inherit",z+1,w-3); - - if(!strcmp(type,"text")) - { - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h-3,fgcolor2,text); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h-3,fgcolor1,text); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h-3,disable_color,text); - } - else if(!strcmp(type,"image")) - { - /* working image htrAddBodyItem_va(s,"
\n",id,h-3,n_img,w,h-3); - htrAddBodyItem_va(s,"
\n",id,h-3,n_img,w,h-3); - htrAddBodyItem_va(s,"
\n",id,h-3,n_img,w,h-3); */ - } - else if(!strcmp(type,"topimage")) - { - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h-3,n_img,fgcolor2,text); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h-3,n_img,fgcolor1,text); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h-3,n_img,disable_color,text); - } - else if(!strcmp(type,"bottomimage")) - { - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h-3,fgcolor2,text,n_img); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h-3,fgcolor1,text,n_img); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,h-3,disable_color,text,n_img); - } - else if(!strcmp(type,"leftimage")) - { - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,spacing,h-3,n_img,h-3,fgcolor2,text); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,spacing,h-3,n_img,h-3,fgcolor1,text); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,spacing,h-3,n_img,h-3,disable_color,text); - } - else if(!strcmp(type,"rightimage")) - { - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,spacing,h-3,fgcolor2,text,h-3,n_img); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,spacing,h-3,fgcolor1,text,h-3,n_img); - htrAddBodyItem_va(s,"
%STR&HTE
\n",id,spacing,h-3,disable_color,text,h-3,n_img); - } - else if(!strcmp(type,"textoverimage")) - { - //htrAddBodyItem_va(s,"
%STR&HTE
\n",id,fgcolor2,text,h-3,n_img,w,h-3); - //htrAddBodyItem_va(s,"
%STR&HTE
\n",id,fgcolor2,text,h-3,n_img,w,h-3); - //htrAddBodyItem_va(s,"
%STR&HTE
\n",id,fgcolor2,text,h-3,n_img,w,h-3); - } - else - { - htrAddBodyItem_va(s,"
default text
\n",id,h-3,fgcolor2); - htrAddBodyItem_va(s,"
default text
\n",id,h-3,fgcolor1); - htrAddBodyItem_va(s,"
default text
\n",id,h-3,disable_color); - } - htrAddBodyItem(s, "
"); - - /** Script initialization call. **/ - htrAddScriptInit_va(s, " gb_init({layer:%STR&SYM, layer2:htr_subel(%STR&SYM, \"gb%POSpane2\"), layer3:htr_subel(%STR&SYM, \"gb%POSpane3\"), top:null, bottom:null, right:null, left:null, width:%INT, height:%INT, tristate:%INT, name:\"%STR&SYM\", text:'%STR&JSSTR', n:'%STR&JSSTR', p:'%STR&JSSTR', c:'%STR&JSSTR', d:'%STR&JSSTR', type:'%STR&JSSTR'});\n", - dptr, dptr, id, dptr, id, w, h, is_ts, name, text, n_img, p_img, c_img, d_img,type); - } - else - { - mssError(0,"HTBTN","Unable to render for this browser"); - return -1; - } + mssError(0, "HTBTN", "Failed to write CSS for button image."); + goto err; } + } - /** Add the event handling scripts **/ - htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "gb", "gb_mousedown"); - htrAddEventHandlerFunction(s, "document", "MOUSEUP", "gb", "gb_mouseup"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "gb", "gb_mouseover"); - htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "gb", "gb_mouseout"); - htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "gb", "gb_mousemove"); - - /** IE handles dblclick strangely **/ - if (s->Capabilities.Dom0IE) - htrAddEventHandlerFunction(s, "document", "DBLCLICK", "gb", "gb_dblclick"); + /** We need two DIVs here because of a long-outstanding Firefox bug :( **/ + if (htrAddBodyItem_va(s, + "
" + "
" + "%[%STR&HTE%]" + "%[
%]" + "%[%STR&HTE%]" + "%[%STR&HTE%]" + "%[%STR&HTE%]" + "%[
%]" + "%[%STR&HTE%]" + "
" + "
", + id, + (!is_enabled), + (has_img_element && position == Top), (has_text) ? "" : tooltip, (is_enabled) ? image : disabled_image, + (has_img_element && position == Top && has_text), + (has_img_element && position == Left), (has_text) ? "" : tooltip, (is_enabled) ? image : disabled_image, + has_text, text, + (has_img_element && position == Right), (has_text) ? "" : tooltip, (is_enabled) ? image : disabled_image, + (has_img_element && position == Bottom && has_text), + (has_img_element && position == Bottom), (has_text) ? "" : tooltip, (is_enabled) ? image : disabled_image + ) != 0) + { + mssError(0, "HTBTN", "Failed to write HTML for button."); + goto err; + } - /** Check for more sub-widgets within the button. **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+3); + /** Script initialization call. **/ + if (htrAddScriptInit_va(s, + "\tgb_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "name:'%STR&SYM', " + "text:'%STR&JSSTR', " + "enabled:%[true%]%[false%], " + "tristate:%[true%]%[false%], " + "repeat:%[true%]%[false%], " + "type:'%STR&JSSTR', " + "border_style:'%STR&JSSTR', " + "border_color:'%STR&JSSTR', " + "tooltip:'%STR&JSSTR', " + "text_color:'%STR&JSSTR', " + "shadow_color:'%STR&JSSTR', " + "disabled_text_color:'%STR&JSSTR', " + "image:'%STR&JSSTR', " + "point_image:'%STR&JSSTR', " + "click_image:'%STR&JSSTR', " + "disabled_image:'%STR&JSSTR', " + "width:%INT, " + "height:%INT, " + "});\n", + name, name, text, + is_enabled, !is_enabled, + is_tristate, !is_tristate, + do_repeat, !do_repeat, + type, + border_style, border_color, tooltip, + text_color, shadow_color, disabled_text_color, + image, point_image, click_image, disabled_image, + w, h + ) != 0) + { + mssError(0, "HTBTN", "Failed to write JS init call."); + goto err; + } - return 0; + /** Add event handlers. **/ + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "gb", "gb_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "gb", "gb_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "gb", "gb_mouseout") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "gb", "gb_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "gb", "gb_mouseup") != 0) goto err; + + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto err; + + /** Success. **/ + return 0; + + err: + mssError(0, "HTBTN", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } -/*** htbtnInitialize - register with the ht_render module. +/*** htbtnRegisterName - register the button renderer under one widget name. + *** The deprecated textbutton and imagebutton names share this renderer and + *** differ only in the defaults htbtnRender() picks for them. + *** + *** Note: This function should be inlined for simplicity once imagebutton and + *** textbutton are removed. The inlining will also fix the doc reports + *** script failing to detect this file as an origin for the button widget. ***/ int -htbtnInitialize() +htbtnRegisterName(char* drv_name, char* widget_name) { pHtDriver drv; - /** Allocate the driver **/ drv = htrAllocDriver(); if (!drv) return -1; - /** Fill in the structure. **/ - strcpy(drv->Name,"HTML Button Widget Driver"); - strcpy(drv->WidgetName,"button"); + strtcpy(drv->Name, drv_name, sizeof(drv->Name)); + strtcpy(drv->WidgetName, widget_name, sizeof(drv->WidgetName)); drv->Render = htbtnRender; - /** Add the 'click' event **/ - htrAddEvent(drv, "Click"); - htrAddEvent(drv, "MouseUp"); - htrAddEvent(drv, "MouseDown"); - htrAddEvent(drv, "MouseOver"); - htrAddEvent(drv, "MouseOut"); - htrAddEvent(drv, "MouseMove"); - - /** Register. **/ htrRegisterDriver(drv); - htrAddSupport(drv, "dhtml"); + return 0; + } + + +/*** htbtnInitialize - register with the ht_render module. + ***/ +int +htbtnInitialize() + { + /*** The deprecated textbutton and imagebutton names remain part of the + *** language and render through here. Their own drivers are gone. + ***/ + if (htbtnRegisterName("HTML Button Widget Driver", "button") != 0) return -1; + if (htbtnRegisterName("HTML Text Button Widget Driver", "textbutton") != 0) return -1; + if (htbtnRegisterName("HTML Image Button Widget Driver", "imagebutton") != 0) return -1; + HTBTN.idcnt = 0; return 0; diff --git a/centrallix/htmlgen/htdrv_calendar.c b/centrallix/htmlgen/htdrv_calendar.c index 3605135fd..12fe7c8f4 100644 --- a/centrallix/htmlgen/htdrv_calendar.c +++ b/centrallix/htmlgen/htdrv_calendar.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2003 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -45,6 +33,14 @@ /* source to query additional data if need be. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -71,25 +67,24 @@ htcaRender(pHtSession s, pWgtrNode tree, int z) char eventpriofield[32] = ""; int minpriority=0; int x=-1,y=-1,w,h; - int id, i; - /** Verify user-agent's capabilities allow us to continue... **/ - if(!s->Capabilities.Dom0NS && !(s->Capabilities.Dom1HTML && s->Capabilities.CSS1)) + /** Get an id for this. **/ + const int id = (HTCA.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTCA","Netscape 4.x DOM support or W3C HTML DOM1/CSS1 support required"); - return -1; + mssError(1, "HTCA", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTCA.idcnt++); - /** Get x,y,w,h of this object **/ if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { mssError(1,"HTCA","Calendar widget must have a 'width' property"); - return -1; + goto err; } if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) h = 0; @@ -109,13 +104,13 @@ htcaRender(pHtSession s, pWgtrNode tree, int z) if (wgtrGetPropertyValue(tree, "eventdatefield", DATA_T_STRING, POD(&ptr)) != 0) { mssError(1,"HTCA","Calendar widget must have an 'eventdatefield' property"); - return -1; + goto err; } strtcpy(eventdatefield, ptr, sizeof(eventdatefield)); if (wgtrGetPropertyValue(tree, "eventnamefield", DATA_T_STRING, POD(&ptr)) != 0) { mssError(1,"HTCA","Calendar widget must have an 'eventnamefield' property"); - return -1; + goto err; } strtcpy(eventnamefield,ptr,sizeof(eventnamefield)); if (wgtrGetPropertyValue(tree, "eventpriofield", DATA_T_STRING, POD(&ptr)) == 0) @@ -134,43 +129,97 @@ htcaRender(pHtSession s, pWgtrNode tree, int z) } /** minimum priority **/ - wgtrGetPropertyValue(tree, "displaymode", DATA_T_STRING, POD(&minpriority)); + if (wgtrGetPropertyValue(tree, "minpriority", DATA_T_INTEGER, POD(&minpriority)) != 0) minpriority = 0; /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto err; strtcpy(name,ptr,sizeof(name)); /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#ca%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,h,z); + if (htrAddStylesheetItem_va(s, + "\t\t#ca%POSbase { " + "position:absolute; " + "visibility:inherit; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "}\n", + id, x, y, w, h, z + ) != 0) + { + mssError(0, "HTCA", "Failed to write CSS init call."); + goto err; + } /** Script include to get functions **/ - htrAddScriptInclude(s, "/sys/js/htdrv_calendar.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_calendar.js", 0) != 0) goto err; - htrAddEventHandlerFunction(s, "document","MOUSEUP", "ca", "ca_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "ca", "ca_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER", "ca", "ca_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEOUT", "ca", "ca_mouseout"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "ca", "ca_mousemove"); + /** Register events. **/ + if (htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "ca", "ca_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "ca", "ca_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document","MOUSEOUT", "ca", "ca_mouseout") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document","MOUSEOVER", "ca", "ca_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document","MOUSEUP", "ca", "ca_mouseup") != 0) goto err; /** Script initialization call. **/ - htrAddScriptInit_va(s, " ca_init(wgtrGetNodeRef(ns,\"%STR&SYM\"), \"%STR&JSSTR\", \"%STR&JSSTR\", \"%STR&JSSTR\", \"%STR&JSSTR\", \"%STR&SYM\", \"%STR&SYM\", \"%STR&SYM\", \"%STR&SYM\", %INT, %INT, %INT);\n", + if (htrAddScriptInit_va(s, + "\tca_init({" + "l:wgtrGetNodeRef(ns, '%STR&SYM'), " + "main_bg:'%STR&JSSTR', " + "cell_bg:'%STR&JSSTR', " + "textcolor:'%STR&JSSTR', " + "dispmode:'%STR&JSSTR', " + "eventdatefield:'%STR&SYM', " + "eventnamefield:'%STR&SYM', " + "%[eventpriofield:'%STR&SYM', %]" + "%[eventdescfield:'%STR&SYM', %]" + "minprio:%INT, " + "w:%INT, " + "h:%INT, " + "});\n", name, main_bg, cell_bg, textcolor, dispmode, - eventdatefield, eventdescfield, eventnamefield, eventpriofield, - minpriority, w, h); + eventdatefield, eventnamefield, + (eventpriofield[0] != '\0'), eventpriofield, + (eventdescfield[0] != '\0'), eventdescfield, + minpriority, w, h + ) != 0) + { + mssError(0, "HTCA", "Failed to write JS init call."); + goto err; + } /** HTML body
element for the base layer. **/ - htrAddBodyItem_va(s, "
\n",id, main_bg, textcolor); + if (htrAddBodyItem_va(s, + "
\n", + id, main_bg, textcolor + ) != 0) + { + mssError(0, "HTCA", "Failed to write HTML opening tags."); + goto err; + } - /** Check for more sub-widgets **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+1); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto err; /** End the containing layer. **/ - htrAddBodyItem(s, "
\n"); + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTCA", "Failed to write HTML closing tags."); + goto err; + } - return 0; + return 0; + + err: + mssError(0, "HTCA", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } @@ -190,14 +239,6 @@ htcaInitialize() strcpy(drv->WidgetName,"calendar"); drv->Render = htcaRender; - /** Events **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_chart.c b/centrallix/htmlgen/htdrv_chart.c index 073627a20..9faff668a 100644 --- a/centrallix/htmlgen/htdrv_chart.c +++ b/centrallix/htmlgen/htdrv_chart.c @@ -1,22 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/xstring.h" -#include "stparse.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1999-2007 LightSys Technology Services, Inc. */ +/* Copyright (C) 1999-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -42,6 +28,16 @@ /* Description: */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "ht_render.h" +#include "wgtr.h" + #define HTTBL_MAX_COLS (32) @@ -205,13 +201,22 @@ htchtAddSeriesProperties(pHtSession session, pWgtrNode tree) htchtGetStrValue(sub_tree, "y_column", "", y_column, sizeof(y_column)); htchtGetStrValue(sub_tree, "chart_type", "", chart_type, sizeof(chart_type)); - htrAddScriptInit_va(session, " chartobj.series.push({ label: \"%STR&JSSTR\", color: \"%STR&JSSTR\", fill: %INT, x_column: \"%STR&JSSTR\", y_column: \"%STR&JSSTR\", chart_type: \"%STR&JSSTR\" });\n", - label, - color, - htrGetBoolean(tree, "fill", 1), - x_column, - y_column, - chart_type); + htrAddScriptInit_va(session, + "\t\tchart_obj.series.push({ " + "label:'%STR&JSSTR', " + "color:'%STR&JSSTR', " + "fill:%INT, " + "x_column:'%STR&JSSTR', " + "y_column:'%STR&JSSTR', " + "chart_type:'%STR&JSSTR' " + "});\n", + label, + color, + htrGetBoolean(sub_tree, "fill", 1), + x_column, + y_column, + chart_type + ); htrCheckNSTransitionReturn(session, tree, sub_tree); } @@ -241,7 +246,13 @@ htchtAddAxesProperties(pHtSession session, pWgtrNode tree) htchtGetStrValue(sub_tree, "label", "", label, sizeof(label)); htchtGetStrValue(sub_tree, "axis", "x", axis, sizeof(axis)); - htrAddScriptInit_va(session, " chartobj.axes.push({ label: \"%STR&JSSTR\", axis: \"%STR&JSSTR\", });\n", label, axis); + htrAddScriptInit_va(session, + "\t\tchart_obj.axes.push({ " + "label:'%STR&JSSTR', " + "axis:'%STR&JSSTR', " + "});\n", + label, axis + ); htrCheckNSTransitionReturn(session, tree, sub_tree); } @@ -261,6 +272,7 @@ htchtInitCall(pHtSession session, pWgtrNode tree) char title_color[32]; char legend_position[32]; + /** Get data used for the initialization call. **/ htchtGetObjectSource(tree, object_source, sizeof(object_source)); htchtGetName(tree, name, sizeof(name)); htchtGetTitle(tree, title, sizeof(title)); @@ -269,43 +281,48 @@ htchtInitCall(pHtSession session, pWgtrNode tree) htchtGetTitleColor(tree, title_color, sizeof(title_color)); htchtGetLegendPosition(tree, legend_position, sizeof(legend_position)); - htrAddScriptInit_va(session, " var chartobj = {" - "x_pos: %INT, " - "y_pos: %INT, " - "width: %INT, " - "height: %INT, " - "title_size: %INT, " - "start_at_zero: %INT," - "stacked: %INT," - "chart: wgtrGetNodeRef(ns,\"%STR&SYM\"), " - "chart_type: '%STR&JSSTR', " - "canvas_id: '%STR&SYM', " - "osrc: '%STR&JSSTR', " - "title: '%STR&JSSTR', " - "title_color: '%STR&JSSTR', " - "legend_position: '%STR&JSSTR', " - "axes: [], " - "series: [], " - "};\n", - htchtGetX(tree), - htchtGetY(tree), - htchtGetWidth(tree), - htchtGetHeight(tree), - htchtGetIntValue(tree, "title_size", 12), - htrGetBoolean(tree, "start_at_zero", 1), - htrGetBoolean(tree, "stacked", 0), - name, - chart_type, - canvas_id, - object_source, - title, - title_color, - legend_position - ); + /** Write a JS object for the script initialization in a new scope. **/ + htrAddScriptInit_va(session, "\t\t{\n" + "\t\tconst chart_obj = { " + "x_pos: %INT, " + "y_pos: %INT, " + "width: %INT, " + "height: %INT, " + "title_size: %INT, " + "start_at_zero: %INT," + "stacked: %INT," + "chart: wgtrGetNodeRef(ns,\"%STR&SYM\"), " + "chart_type: '%STR&JSSTR', " + "canvas_id: '%STR&SYM', " + "osrc: '%STR&JSSTR', " + "title: '%STR&JSSTR', " + "title_color: '%STR&JSSTR', " + "legend_position: '%STR&JSSTR', " + "axes: [], " + "series: [], " + "};\n", + htchtGetX(tree), + htchtGetY(tree), + htchtGetWidth(tree), + htchtGetHeight(tree), + htchtGetIntValue(tree, "title_size", 12), + htrGetBoolean(tree, "start_at_zero", 1), + htrGetBoolean(tree, "stacked", 0), + name, + chart_type, + canvas_id, + object_source, + title, + title_color, + legend_position + ); + /** Write code to add values to the initialization object. **/ htchtAddAxesProperties(session, tree); htchtAddSeriesProperties(session, tree); - htrAddScriptInit_va(session, " cht_init(chartobj);\n"); + + /** Write the initialization call, using the object, and close the scope. **/ + htrAddScriptInit(session, "\t\tcht_init(chart_obj);\n\t\t}\n"); return 0; } @@ -323,27 +340,51 @@ htchtScriptInclude(pHtSession session) void htchtGenHTML(pHtSession session, pWgtrNode tree, int z) { - char buf[32]; - - htchtGetCanvasId(tree, buf, sizeof(buf)); - - htrAddBodyItem_va(session,"
\n", - buf, - buf, - htchtGetWidth(tree), - htchtGetHeight(tree) + /** Get id. **/ + char id[32]; + htchtGetCanvasId(tree, id, sizeof(id)); + + /** Get layout data. **/ + const int x = htchtGetX(tree); + const int y = htchtGetY(tree); + const int w = htchtGetWidth(tree); + const int h = htchtGetHeight(tree); + + /** Write style rules for the container div. **/ + htrAddStylesheetItem_va(session, + "\t\t#%STR&SYMdiv { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z ); - - htrAddBodyItem(session,"

CHART HERE

\n"); - htrAddBodyItem(session,"
\n"); - - htrAddStylesheetItem_va(session, "\t#%STR&SYMdiv { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; } \n", - buf, - htchtGetX(tree), - htchtGetY(tree), - htchtGetWidth(tree), - htchtGetHeight(tree), - z + + /** Write the canvas HTML. **/ + htrAddBodyItem_va(session, + "
" + "" + /** Fallback used when is not supported. **/ + "

CHART HERE

" + "
" + "
", + id, + id, + w, + h ); } @@ -393,9 +434,6 @@ htchtInitialize() xaAddItem(&(drv->PseudoTypes), "chart-axis"); xaAddItem(&(drv->PseudoTypes), "chart-series"); - htrAddEvent(drv, "Click"); - htrAddEvent(drv, "DblClick"); - /** Register. **/ htrRegisterDriver(drv); @@ -405,4 +443,3 @@ htchtInitialize() return 0; } - diff --git a/centrallix/htmlgen/htdrv_checkbox.c b/centrallix/htmlgen/htdrv_checkbox.c index c2f699e91..02091d738 100644 --- a/centrallix/htmlgen/htdrv_checkbox.c +++ b/centrallix/htmlgen/htdrv_checkbox.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 2000-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 2000-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,15 @@ /* Description: HTML Widget driver for a checkbox */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct { @@ -47,26 +44,27 @@ static struct { } HTCB; -int htcbRender(pHtSession s, pWgtrNode tree, int z) { +int htcbRender(pHtSession s, pWgtrNode tree, int z) + { char fieldname[HT_FIELDNAME_SIZE]; int x=-1,y=-1,checked=0; - int id, i; char *ptr; char name[64]; char form[64]; int enabled = 0; - if(!(s->Capabilities.Dom0NS || s->Capabilities.Dom1HTML)) - { - mssError(1,"HTCB","Netscape DOM support or W3C DOM Level 1 HTML required"); - return -1; - } - /** Get an id for this. **/ - id = (HTCB.idcnt++); + const int id = (HTCB.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) + { + mssError(1, "HTCB", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; + } /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto err; strtcpy(name, ptr, sizeof(name)); /** Get x,y of this object **/ @@ -88,46 +86,105 @@ int htcbRender(pHtSession s, pWgtrNode tree, int z) { /** Is it enabled? **/ enabled = htrGetBoolean(tree, "enabled", 1); - /** Write named global **/ - htrAddWgtrObjLinkage_va(s, tree, "cb%INTmain", id); - - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#cb%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; HEIGHT:13px; WIDTH:13px; Z-INDEX:%POS; }\n",id,x,y,z); - htrAddScriptInclude(s,"/sys/js/htdrv_checkbox.js",0); - htrAddScriptInclude(s,"/sys/js/ht_utils_hints.js",0); - - htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "checkbox", "checkbox_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEUP", "checkbox", "checkbox_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER", "checkbox", "checkbox_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEOUT", "checkbox", "checkbox_mouseout"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "checkbox", "checkbox_mousemove"); + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "cb%INTmain", id) != 0) + { + mssError(0, "HTCB", "Failed to add object linkage."); + goto err; + } - /** Script initialization call. **/ - htrAddScriptInit_va(s," checkbox_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), fieldname:\"%STR&JSSTR\", checked:%INT, enabled:%INT, form:\"%STR&JSSTR\"});\n", name, fieldname,checked,enabled,form); + /** Write style header. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#cb%POSmain { " + "position:absolute; " + "visibility:inherit; " + "cursor:%STR; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "height:13px; " + "width:13px; " + "z-index:%POS; " + "}\n", + id, + (enabled) ? "pointer" : "default", + ht_flex_x(x, tree), + ht_flex_y(y, tree), + z + ) != 0) + { + mssError(0, "HTCB", "Failed to write CSS."); + goto err; + } + + /** Include scripts. **/ + if (htrAddScriptInclude(s,"/sys/js/ht_utils_hints.js", 0) != 0) goto err; + if (htrAddScriptInclude(s,"/sys/js/htdrv_checkbox.js", 0) != 0) goto err; + + /** Register event handlers. **/ + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "checkbox", "checkbox_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "checkbox", "checkbox_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "checkbox", "checkbox_mouseout") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "checkbox", "checkbox_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "checkbox", "checkbox_mouseup") != 0) goto err; + + /** Script initialization call. **/ + if (htrAddScriptInit_va(s, + "\tcheckbox_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "fieldname:'%STR&JSSTR', " + "checked:%INT, " + "enabled:%INT, " + "form:'%STR&JSSTR', " + "});\n", + name, fieldname, checked, enabled, form + ) != 0) + { + mssError(0, "HTCB", "Failed to write JS init call."); + goto err; + } - /** HTML body
element for the layers. **/ - htrAddBodyItemLayerStart(s, 0, "cb%POSmain", id, NULL); - switch(checked) + /** Write HTML. **/ + if (htrAddBodyItemLayerStart(s, 0, "cb%POSmain", id, NULL) != 0) { - case 1: - htrAddBodyItem_va(s," \n",!enabled); - break; - case 0: - htrAddBodyItem_va(s," \n",!enabled); - break; - case -1: /* null */ - htrAddBodyItem_va(s," \n",!enabled); - break; + mssError(0, "HTCB", "Failed to write HTML layer start."); + goto err; + } + char* state_name; + switch (checked) + { + case 1: state_name = "checked"; break; + case 0: state_name = "unchecked"; break; + case -1: state_name = "null"; break; + default: /* Should be unreachable. */ + mssError(1, "HTCB", "Unexpected value %d for 'checked'.", checked); + goto err; + } + char src_path[48]; + snprintf(src_path, sizeof(src_path), "/sys/images/checkbox_%s%s.gif", state_name, (enabled) ? "" : "_dis"); + if (htrAddBodyItem_va(s, " \n", src_path) != 0) + { + mssError(0, "HTCB", "Failed to write HTML tag."); + goto err; + } + if (htrAddBodyItemLayerEnd(s, 0) != 0) + { + mssError(0, "HTCB", "Failed to write HTML layer end."); + goto err; } - htrAddBodyItemLayerEnd(s, 0); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto err; - /** Check for more sub-widgets **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+1); + /** Success. **/ + return 0; - return 0; -} + err: + mssError(0, "HTCB", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; + } /* @@ -145,15 +202,6 @@ int htcbInitialize() { strcpy(drv->WidgetName,"checkbox"); drv->Render = htcbRender; - /** Events **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - htrAddEvent(drv,"DataChange"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_clock.c b/centrallix/htmlgen/htdrv_clock.c index 26d070321..c8d73de25 100644 --- a/centrallix/htmlgen/htdrv_clock.c +++ b/centrallix/htmlgen/htdrv_clock.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,14 @@ /* Description: HTML Widget driver for a local time clock. */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" /** globals **/ @@ -64,24 +60,23 @@ htclRender(pHtSession s, pWgtrNode tree, int z) int shadowx = 0; int shadowy = 0; int size = 0; - int moveable = 0; int bold = 0; int showsecs = 1; int showampm = 1; int miltime = 0; int x=-1,y=-1,w,h; - int id, i; char fieldname[HT_FIELDNAME_SIZE]; - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML) + /** Get an id for this. **/ + const int id = (HTCL.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTCL","Netscape 4 or W3C DOM support required"); - return -1; + mssError(1, "HTCL", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTCL.idcnt++); - /** Get x,y,w,h of this object **/ if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; @@ -105,9 +100,9 @@ htclRender(pHtSession s, pWgtrNode tree, int z) miltime = 1; showampm = 0; } - else if (wgtrGetPropertyValue(tree,"ampm",DATA_T_STRING,POD(&ptr)) == 0 && (!strcasecmp(ptr,"false") || !strcasecmp(ptr,"no"))) + else { - showampm = 0; + showampm = htrGetBoolean(tree, "ampm", 1); } /** Get text color **/ @@ -117,7 +112,7 @@ htclRender(pHtSession s, pWgtrNode tree, int z) strcpy(fgcolor1,"black"); /* Shadowed text? */ - if (wgtrGetPropertyValue(tree,"shadowed",DATA_T_STRING,POD(&ptr)) == 0 && (!strcasecmp(ptr,"true") || !strcasecmp(ptr,"yes"))) + if (htrGetBoolean(tree, "shadowed", 0)) { shadowed = 1; if (wgtrGetPropertyValue(tree,"fgcolor2",DATA_T_STRING,POD(&ptr)) == 0) @@ -135,20 +130,14 @@ htclRender(pHtSession s, pWgtrNode tree, int z) } /** Bold text? **/ - if (wgtrGetPropertyValue(tree,"bold",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"true")) - bold = 1; + bold = htrGetBoolean(tree, "bold", 0); /** Get text size **/ if (wgtrGetPropertyValue(tree,"size",DATA_T_INTEGER,POD(&ptr)) == 0) size = (intptr_t)ptr; - /** Movable? **/ - if (wgtrGetPropertyValue(tree,"moveable",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"true")) - moveable = 1; - /** Show Seconds **/ - if (wgtrGetPropertyValue(tree,"seconds",DATA_T_STRING,POD(&ptr)) == 0 && (!strcasecmp(ptr,"false") || !strcasecmp(ptr,"no"))) - showsecs = 0; + showsecs = htrGetBoolean(tree, "seconds", 1); /** Get name **/ if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; @@ -160,53 +149,110 @@ htclRender(pHtSession s, pWgtrNode tree, int z) else fieldname[0]='\0'; - /** Write Style header items. **/ - htrAddStylesheetItem_va(s,"\t#cl%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z); - htrAddStylesheetItem_va(s,"\t#cl%POScon1 { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,0,0,w,z+2); - htrAddStylesheetItem_va(s,"\t#cl%POScon2 { POSITION:absolute; VISIBILITY:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,0,0,w,z+2); - - /** Write named global **/ - htrAddWgtrObjLinkage_va(s, tree, "cl%POSbase",id); + /** Write style headers. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#cl%POSbase { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + z + ) != 0) + { + mssError(0, "HTCL", "Failed to write base CSS."); + goto err; + } + if (htrAddStylesheetItem_va(s, + "\t\t.cl%POScon { " + "position:absolute; " + "left:0px; " + "top:0px; " + "width:100%%; " + "z-index:%POS; " + "}\n", + id, + z + 2 + ) != 0) + { + mssError(0, "HTCL", "Failed to write con CSS."); + goto err; + } - /** Other global variables **/ - htrAddScriptGlobal(s, "cl_move", "false", 0); - htrAddScriptGlobal(s, "cl_xOffset", "null", 0); - htrAddScriptGlobal(s, "cl_yOffset", "null", 0); + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "cl%POSbase", id) != 0) + { + mssError(0, "HTCL", "Failed to add object linkage."); + goto err; + } /** Javascript include files **/ - htrAddScriptInclude(s, "/sys/js/htdrv_clock.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0)) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_clock.js", 0)) goto err; /** Event Handlers **/ - htrAddEventHandlerFunction(s, "document","MOUSEUP", "cl", "cl_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "cl", "cl_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER", "cl", "cl_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEOUT", "cl", "cl_mouseout"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "cl", "cl_mousemove"); + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "cl", "cl_mousedown")) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "cl", "cl_mousemove")) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "cl", "cl_mouseout")) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "cl", "cl_mouseover")) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "cl", "cl_mouseup")) goto err; /** Script initialization call. **/ - htrAddScriptInit_va(s, " cl_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), c1:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"),\"cl%POScon1\"), c2:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"),\"cl%POScon2\"), fieldname:\"%STR&JSSTR\", background:\"%STR&JSSTR\", shadowed:%POS, foreground1:\"%STR&JSSTR\", foreground2:\"%STR&JSSTR\", fontsize:%INT, moveable:%INT, bold:%INT, sox:%INT, soy:%INT, showSecs:%INT, showAmPm:%INT, milTime:%INT});\n", - name, - name, id, - name, id, - fieldname, main_bg, shadowed, - fgcolor1, fgcolor2, - size, moveable, bold, - shadowx, shadowy, - showsecs, showampm, miltime); + if (htrAddScriptInit_va(s, "\t{ " + "const layer = wgtrGetNodeRef(ns, '%STR&SYM'); " + "cl_init({ " + "layer, " + "c1:htr_subel(layer, 'cl%POScon1'), " + "c2:htr_subel(layer, 'cl%POScon2'), " + "fieldname:'%STR&JSSTR', " + "fontsize:%INT, " + "bold:%INT, " + "background:'%STR&JSSTR', " + "foreground1:'%STR&JSSTR', " + "foreground2:'%STR&JSSTR', " + "shadowed:%POS, " + "sox:%INT, " + "soy:%INT, " + "showSecs:%INT, " + "showAmPm:%INT, " + "milTime:%INT, " + "}); }\n", + name, id, id, + fieldname, size, bold, + main_bg, fgcolor1, fgcolor2, + shadowed, shadowx, shadowy, + showsecs, showampm, miltime + ) != 0) + { + mssError(0, "HTCL", "Failed to add JS init call."); + goto err; + } /** HTML body
element for the base layer. **/ htrAddBodyItem_va(s, "
\n",id); htrAddBodyItem_va(s, "
\n",main_bg,w,h); - htrAddBodyItem_va(s, "
\n",id); - htrAddBodyItem_va(s, "
\n",id); + htrAddBodyItem_va(s, "
\n", id, id); + htrAddBodyItem_va(s, " \n", id, id); htrAddBodyItem(s, "
\n"); - /** Check for more sub-widgets **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+1); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto err; - return 0; + /** Success. **/ + return 0; + + err: + mssError(0, "HTCL", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } @@ -226,13 +272,6 @@ htclInitialize() strcpy(drv->WidgetName,"clock"); drv->Render = htclRender; - /** Events **/ - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_component.c b/centrallix/htmlgen/htdrv_component.c index 93c5533a5..26918cb9c 100644 --- a/centrallix/htmlgen/htdrv_component.c +++ b/centrallix/htmlgen/htdrv_component.c @@ -1,24 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "hints.h" -#include "cxlib/cxsec.h" -#include "stparse_ne.h" -#include "cxlib/qprintf.h" -#include "cxss/cxss.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2006 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -46,6 +30,23 @@ /* component loaded dynamically. */ /************************************************************************/ +#include +#include +#include + +#include "cxlib/check.h" +#include "cxlib/cxsec.h" +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/newmalloc.h" +#include "cxlib/qprintf.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "cxss/cxss.h" +#include "ht_render.h" +#include "obj.h" +#include "stparse_ne.h" +#include "wgtr.h" /** globals **/ @@ -172,7 +173,6 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) { char* ptr; char name[64]; - int id; char cmp_path[256]; pObject cmp_obj = NULL; pWgtrNode cmp_tree = NULL; @@ -185,7 +185,6 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) char sbuf[128]; pStruct params = NULL; pStruct old_params = NULL; - int i; char* path; int is_toplevel; int old_is_dynamic = 0; @@ -194,28 +193,26 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) char* slashptr; int new_sec_context = 0; - /** Verify capabilities **/ - if(!s->Capabilities.Dom0NS && !(s->Capabilities.Dom1HTML && s->Capabilities.CSS1)) + /** Get an id for this. **/ + const int id = (HTCMP.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTCMP","Either Netscape DOM or W3C DOM1 HTML and W3C CSS support required"); - return -1; + mssError(1, "HTCMP", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto end_free; } - /** Get an id for this. **/ - id = (HTCMP.idcnt++); - /** Is this a toplevel component? **/ is_toplevel = htrGetBoolean(tree, "toplevel", 0); + if (is_toplevel < 0) goto end_free; /** Get x,y,w,h of this object **/ + /** w & h default to the entire container, if unspecified. **/ if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x = 0; if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y = 0; if (is_toplevel) { -/* if (wgtrGetPropertyValue(wgtrGetRoot(tree),"width",DATA_T_INTEGER,POD(&w)) != 0) - w = wgtrGetContainerWidth(tree) - x; - if (wgtrGetPropertyValue(wgtrGetRoot(tree),"height",DATA_T_INTEGER,POD(&h)) != 0) - h = wgtrGetContainerHeight(tree) - y;*/ if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) w = s->ClientInfo->AppMaxWidth - x; if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) @@ -224,25 +221,41 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) else { if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) - w = wgtrGetContainerWidth(tree) - x; + { + const int container_width = wgtrGetContainerWidth(tree); + if (container_width < 0) + { + mssError(1, "HTCMP", "Failed to get container width for component width."); + goto end_free; + } + w = container_width - x; + } if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) - h = wgtrGetContainerHeight(tree) - y; + { + const int container_height = wgtrGetContainerHeight(tree); + if (container_height < 0) + { + mssError(1, "HTCMP", "Failed to get container height for component height."); + goto end_free; + } + h = container_height - y; + } } /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto end_free; strtcpy(name,ptr,sizeof(name)); if (cxsecVerifySymbol(name) < 0) { mssError(1,"HTCMP","Invalid name '%s' for component", name); - return -1; + goto end_free; } /** OSML path to component declaration **/ if (wgtrGetPropertyValue(tree, "path", DATA_T_STRING, POD(&ptr)) != 0) { mssError(1,"HTCMP","Component must specify declaration location with 'path' attribute"); - return -1; + goto end_free; } if (ptr[0] != '/') { @@ -262,11 +275,11 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) /** multiple-instantiation? **/ allow_multi = htrGetBoolean(tree, "multiple_instantiation", 0); - if (allow_multi < 0) return -1; + if (allow_multi < 0) goto end_free; /** multiple-instantiation? **/ auto_destroy = htrGetBoolean(tree, "auto_destroy", 1); - if (auto_destroy < 0) return -1; + if (auto_destroy < 0) goto end_free; /** Include the js module **/ htrAddScriptInclude(s, "/sys/js/htdrv_component.js", 0); @@ -275,15 +288,63 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) params = htcmp_internal_CreateParams(s, tree); /** Any params have expressions? **/ - if (params) + if (params != NULL) { - for(i=0;inSubInf;i++) + for (int i = 0; i < params->nSubInf; i++) { - htrCheckAddExpression(s, tree, name, params->SubInf[i]->Name); + if (htrCheckAddExpression(s, tree, name, params->SubInf[i]->Name) < 0) goto end_free; } } - /** If static mode, load the component **/ + + /** Write styles for the enclosing div. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#cmp%POSbase { " + "position:absolute; " + "visibility:inherit; " + "overflow:visible; " + "pointer-events:none; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree) + ) != 0) + { + mssError(0, "HTCMP", "Failed to write base CSS."); + goto end_free; + } + + /** Write enclosing div event CSS. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#cmp%POSbase > * { " + "pointer-events:auto; " + "}\n", + id + ) != 0) + { + mssError(0, "HTCMP", "Failed to write direct descendant CSS."); + goto end_free; + } + + /** Write linked container div to isolate children. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "cmp%POSbase", id) != 0) + { + mssError(0, "HTCMP", "Failed to add object linkage."); + goto end_free; + } + if (htrAddBodyItem_va(s, "
\n", id) != 0) + { + mssError(0, "HTCMP", "Failed to write container HTML."); + goto end_free; + } + + /** If static mode, render the component now. **/ if (is_static) { /** Copy in cx__scripts value **/ @@ -304,14 +365,29 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) s->IsDynamic = 0; /** Init component **/ - htrAddScriptInit_va(s, - " cmp_init({node:wgtrGetNodeRef(ns,\"%STR&SYM\"), is_static:true, allow_multi:false, auto_destroy:false, width:%INT, height:%INT, xpos:%INT, ypos:%INT});\n", - name, w,h,x,y); + if (htrAddScriptInit_va(s, + "\tcmp_init({ " + "node:wgtrGetNodeRef(ns, '%STR&SYM'), " + "is_static:true, " + "allow_multi:false, " + "auto_destroy:false, " + "width:%INT, " + "height:%INT, " + "xpos:%INT, " + "ypos:%INT, " + "});\n", + name, + w, h, x, y + ) != 0) + { + mssError(0, "HTCMP", "Failed to write JS init call."); + goto end_free; + } /** Are there any templates we should use **/ memcpy(&wgtr_params, s->ClientInfo, sizeof(wgtr_params)); memset(wgtr_params.Templates, 0, sizeof(wgtr_params.Templates)); - for(i=0;iNamespace->DName); /** Switch namespaces **/ - htrAddNamespace(s, tree, wgtrGetRootDName(cmp_tree), 0); + char* namespace_name = wgtrGetRootDName(cmp_tree); + if (htrAddNamespace(s, tree, namespace_name, 0) != 0) + { + mssError(0, "HTCMP", "Failed to add namespace."); + goto end_free; + } - /** Generate the component **/ - htrAddWgtrCtrLinkage(s, tree, "_parentctr"); - htrRenderWidget(s, cmp_tree, z+10); - htrBuildClientWgtr(s, cmp_tree); + /** Render the component-decl widget. **/ + if (htrRenderWidget(s, cmp_tree, z + 10) != 0) + { + mssError(0, "HTCMP", "Failed to render component tree."); + goto end_free; + } + if (htrBuildClientWgtr(s, cmp_tree) != 0) + { + mssError(0, "HTCMP", "Failed to build client widget tree."); + goto end_free; + } /** Switch the namespace back **/ - htrLeaveNamespace(s); + if (htrLeaveNamespace(s) != 0) + { + mssError(0, "HTCMP", "Failed to leave namespace."); + goto end_free; + } /** End Init component **/ - htrAddScriptInit_va(s, " cmp_endinit(wgtrGetNodeRef(ns,\"%STR&SYM\"));\n", name); + if (htrAddScriptInit_va(s, "\tcmp_endinit(wgtrGetNodeRef(ns, '%STR&SYM'));\n", name) != 0) + { + mssError(0, "HTCMP", "Failed to write JS init call."); + goto end_free; + } /** Return to previous security context **/ if (new_sec_context) cxssPopContext(); @@ -385,49 +491,136 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) s->IsDynamic = old_is_dynamic; old_is_dynamic = 0; } + + /** Dynamic mode, component is loaded later by the client. **/ else { - /** Init component **/ - htrAddScriptInit_va(s, - " cmp_init({node:wgtrGetNodeRef(ns,\"%STR&SYM\"), is_top:%POS, is_static:false, allow_multi:%POS, auto_destroy:%POS, path:\"%STR&JSSTR\", loader:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")), \"cmp%POS\"), width:%INT, height:%INT, xpos:%INT, ypos:%INT});\n", - name, is_toplevel, allow_multi, auto_destroy, cmp_path, - name, id, - w, h, x, y); - - /** Add template paths **/ - for(i=0;inSubInf;i++) + path = wgtrGetTemplatePath(tree, i); + if (path == NULL) continue; + + if (htrAddScriptInit_va(s, "\t\tnode.templates.push('%STR&JSSTR');\n", path) != 0) { - htrAddScriptInit_va(s, " wgtrGetNodeRef(ns,\"%STR&SYM\").AddParam(\"%STR&SYM\",%[null%]%[\"%STR&HEX\"%]);\n", - name, params->SubInf[i]->Name, !params->SubInf[i]->StrVal, params->SubInf[i]->StrVal, - params->SubInf[i]->StrVal); + mssError(0, "HTCMP", "Failed to write JS template #%d: \"%s\".", i + 1, path); + goto end_free; } } + /** Write params. **/ + if (params != NULL) + { + for (int i = 0; i < params->nSubInf; i++) + { + pStruct param = params->SubInf[i]; + if (param->StrVal == NULL) continue; + + /** Write param. **/ + if (htrAddScriptInit_va(s, + "\t\tnode.AddParam('%STR&SYM', '%STR&HEX');\n", + param->Name, param->StrVal + ) != 0) + { + mssError(0, "HTCMP", + "Failed to write JS to add param #%d/%d.", + i + 1, params->nSubInf + ); + goto end_free; + } + } + } + + /** Write data to close the scope above. **/ + if (htrAddScriptInit(s, "\t\t}\n") != 0) + { + mssError(0, "HTCMP", "Failed to write end of JS code block."); + goto end_free; + } + /** Dynamic mode -- load from client **/ - htrAddWgtrCtrLinkage(s, tree, "_parentctr"); - htrAddBodyItemLayer_va(s, HTR_LAYER_F_DYNAMIC, "cmp%POS", id, NULL, ""); - htrAddStylesheetItem_va(s,"\t#cmp%POS { POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:0px; WIDTH:0px; HEIGHT:0px; Z-INDEX:0;}\n", id); + if (htrAddBodyItemLayer_va(s, HTR_LAYER_F_DYNAMIC, "cmp%POS", id, NULL, "") != 0) + { + mssError(0, "HTCMP", "Failed to write dynamic component HTML container."); + goto end_free; + } + if (htrAddStylesheetItem_va(s, + "\t\t#cmp%POS { " + "position:absolute; " + "visibility:hidden; " + "left:0px; " + "top:0px; " + "width:0px; " + "height:0px; " + "z-index:0; " + "}\n", + id + ) != 0) + { + mssError(0, "HTCMP", "Failed to write dynamic component CSS."); + goto end_free; + } } - htrRenderSubwidgets(s, tree, z+1); + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto end_free; + + /** End the enclosing div. **/ + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTCMP", "Failed to write HTML closing tag."); + goto end_free; + } + /** Success. **/ rval = 0; - out: + end_free: + if (rval != 0) + { + mssError(0, "HTCMP", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + } + /** Clean up **/ - if (new_sec_context) cxssPopContext(); - if (params) - stFreeInf_ne(params); + if (new_sec_context) check(cxssPopContext()); /* Failure ignored. */ + if (params) check(stFreeInf_ne(params)); /* Failure ignored. */ if (s->IsDynamic == 0 && old_is_dynamic) s->IsDynamic = 1; if (s->GraftPoint && old_graft) @@ -441,10 +634,10 @@ htcmpRender(pHtSession s, pWgtrNode tree, int z) s->Params = old_params; old_params = NULL; } - if (cmp_obj) objClose(cmp_obj); - if (cmp_tree) wgtrFree(cmp_tree); + if (cmp_obj != NULL) check(objClose(cmp_obj)); /* Failure ignored. */ + if (cmp_tree != NULL) wgtrFree(cmp_tree); - return rval; + return rval; } @@ -464,13 +657,6 @@ htcmpInitialize() strcpy(drv->WidgetName,"component"); drv->Render = htcmpRender; - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - /** Register. **/ htrRegisterDriver(drv); @@ -481,4 +667,3 @@ htcmpInitialize() return 0; } - diff --git a/centrallix/htmlgen/htdrv_componentdecl.c b/centrallix/htmlgen/htdrv_componentdecl.c index 3a3cb0e0e..ad6b2ebba 100644 --- a/centrallix/htmlgen/htdrv_componentdecl.c +++ b/centrallix/htmlgen/htdrv_componentdecl.c @@ -1,23 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "hints.h" -#include "cxlib/cxsec.h" -#include "cxlib/util.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2003 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -47,16 +32,46 @@ /* an application. */ /************************************************************************/ +#include +#include +#include -/** structure defining a param to the component **/ -typedef struct +#include "cxlib/cxsec.h" +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "ht_render.h" +#include "wgtr.h" + + +/** globals **/ +static struct + { + unsigned int id_count; + } + HTCMPD = {0u}; + + +/*** htcmpd_internal_GetJSFnName - get the name for the JS function to declare + *** the given component-decl-TYPE sub-widget. Returns NULL if the widget + *** type isn't handled here. + ***/ +static char* +htcmpd_internal_GetJSFnName(pWgtrNode node) { - pObjPresentationHints Hints; - TObjData TypedObjData; - char* StrVal; - char* Name; + char* ptr; + + wgtrGetPropertyValue(node, "outer_type", DATA_T_STRING, POD(&ptr)); + if (strncmp(ptr, "widget/component-decl-", 22) != 0) return NULL; + char* decl_type = ptr + 22; + + if (strcmp(decl_type, "action") == 0) return "addAction"; + if (strcmp(decl_type, "event") == 0) return "addEvent"; + if (strcmp(decl_type, "cprop") == 0) return "addProp"; + + return NULL; } - HTCmpdParam, *pHTCmpdParam; /*** htcmpdRender - generate the HTML code for the component. @@ -71,52 +86,33 @@ htcmpdRender(pHtSession s, pWgtrNode tree, int z) char expose_actions_for[64] = ""; char expose_props_for[64] = ""; char apply_hints_to[64] = ""; - /*char* nptr;*/ -// pObject subobj = NULL; pWgtrNode sub_tree = NULL; pWgtrNode conn_tree = NULL; - XArray attrs; - pHTCmpdParam param; - int i, j; - int rval = 0; int is_visual = 1; - char gbuf[256]; + char gbuf[256] = ""; char* gname; - char* xptr; - char* yptr; - int xoffs, yoffs; - /** Verify capabilities **/ - if(!s->Capabilities.Dom0NS && !(s->Capabilities.Dom1HTML && s->Capabilities.CSS1)) + /** Get an id for this. **/ + const unsigned int id = (HTCMPD.id_count++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTCMPD","Either Netscape DOM or W3C DOM1 HTML and W3C CSS support required"); - return -1; + mssError(1, "HTCMPD", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } /** Is this a visual component? **/ - if ((is_visual = htrGetBoolean(tree, "visual", 1)) < 0) - { - return -1; - } + is_visual = htrGetBoolean(tree, "visual", 1); + if (is_visual < 0) goto err; /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto err; strtcpy(name, ptr, sizeof(name)); if (cxsecVerifySymbol(name) < 0) { mssError(1,"HTCMPD","Invalid name '%s' for component", name); - return -1; - } - - /** Need to relocate the widgets via xoffset/yoffset? **/ - xptr = htrParamValue(s, "cx__xoffset"); - yptr = htrParamValue(s, "cx__yoffset"); - if (xptr || yptr) - { - xoffs = yoffs = 0; - if (xptr) xoffs = strtoi(xptr, NULL, 10); - if (yptr) yoffs = strtoi(yptr, NULL, 10); - wgtrMoveChildren(tree, xoffs, yoffs); + goto err; } /** Expose all events on a subwidget? **/ @@ -135,169 +131,116 @@ htcmpdRender(pHtSession s, pWgtrNode tree, int z) if (wgtrGetPropertyValue(tree, "apply_hints_to", DATA_T_STRING, POD(&ptr)) == 0) strtcpy(apply_hints_to, ptr, sizeof apply_hints_to); - /** Write named global **/ - /*nptr = (char*)nmMalloc(strlen(name)+1); - strcpy(nptr,name);*/ - /*htrAddScriptGlobal(s, nptr, "null", HTR_F_NAMEALLOC);*/ - /** Include the js module **/ - htrAddScriptInclude(s, "/sys/js/htdrv_componentdecl.js", 0); - - /** parameters for this component **/ - xaInit(&attrs, 16); + if (htrAddScriptInclude(s, "/sys/js/htdrv_componentdecl.js", 0) != 0) goto err; /** DOM Linkages **/ if (s->GraftPoint) { + bool graft_successful = false; strtcpy(gbuf, s->GraftPoint, sizeof(gbuf)); gname = strchr(gbuf,':'); if (!gname) { mssError(1,"HTCMPD", "Invalid graft point"); - goto htcmpd_cleanup; + goto graft_done; } *(gname++) = '\0'; if (strpbrk(gname,"'\"\\<>\r\n\t ") || strspn(gbuf,"w0123456789abcdef_") != strlen(gbuf)) { mssError(1,"HTCMPD", "Invalid graft point"); - goto htcmpd_cleanup; + goto graft_done; + } + + /** Add linkage. **/ + if (htrAddWgtrCtrLinkage_va(s, tree, + "wgtrGetContainer(wgtrGetNode('%STR&SYM', '%STR&SYM'))", + gbuf, gname + ) != 0) + { + mssError(0, "HTCMPD", "Failed to add container linkage."); + goto graft_done; + } + + /** Add HTML DOM node. **/ + if (htrAddBodyItem_va(s, + "", + s->Namespace->DName + ) != 0) + { + mssError(0, "HTCMPD", "Failed to write HTML linkage node."); + goto graft_done; + } + + /** Success. **/ + graft_successful = true; + + graft_done: + if (!graft_successful) + { + mssError(0, "HTCMPD", "Failed to mount graft point: \"%s\"", s->GraftPoint); + goto err; } - htrAddWgtrCtrLinkage_va(s, tree, - "wgtrGetContainer(wgtrGetNode(\"%STR&SYM\",\"%STR&SYM\"))", gbuf, gname); - htrAddBodyItem_va(s, "", s->Namespace->DName); } else { strcpy(gbuf,""); gname=""; - htrAddWgtrCtrLinkage(s, tree, "_parentctr"); + if (htrAddWgtrCtrLinkage(s, tree, "_parentctr") != 0) + { + mssError(0, "HTCMPD", "Failed to add container linkage."); + goto err; + } } - /** Init component **/ - htrAddScriptInit_va(s, " cmpd_init(wgtrGetNodeRef(ns,\"%STR&SYM\"), {vis:%POS, gns:%[\"%STR&SYM\"%]%[null%], gname:'%STR&SYM'%[, expe:'%STR&SYM'%]%[, expa:'%STR&SYM'%]%[, expp:'%STR&SYM'%]%[, applyhint:'%STR&SYM'%]});\n", - name, is_visual, *gbuf, gbuf, !*gbuf, gname, *expose_events_for, expose_events_for, *expose_actions_for, expose_actions_for, - *expose_props_for, expose_props_for, *apply_hints_to, apply_hints_to); -#if 0 - for (i=0;iChildren));i++) + /** Warning message. **/ + if (gname[0] == '\0') { - /** Loop through each param we get **/ - sub_tree = xaGetItem(&(tree->Children), i); - if (!strcmp(sub_tree->Type, "system/parameter")) - { - param = (pHTCmpdParam)nmMalloc(sizeof(HTCmpdParam)); - if (!param) break; - xaAddItem(&attrs, param); - - /** Get component parameter name **/ - wgtrGetPropertyValue(sub_tree, "name", DATA_T_STRING, POD(&ptr)); - param->Name = nmSysStrdup(ptr); - if (cxsecVerifySymbol(param->Name) < 0) - { - mssError(1,"HTCMPD","Invalid name '%s' for parameter in component '%s'", param->Name, name); - rval = -1; - goto htcmpd_cleanup; - } - - /** Does the param have a value? **/ - param->StrVal = htrParamValue(s, param->Name); - - /** Get component type **/ - if (wgtrGetPropertyValue(sub_tree, "type", DATA_T_STRING, POD(&ptr)) == 0) - { - t = objTypeID(ptr); - if (t < 0) - { - mssError(1,"HTCMPD","Component '%s' parameter '%s' has unknown/invalid data type '%s'", name, param->Name, ptr); - rval = -1; - goto htcmpd_cleanup; - } - param->TypedObjData.DataType = t; - } - else - { - /** default type is string **/ - param->TypedObjData.DataType = DATA_T_STRING; - } - - /** Get hints **/ - param->Hints = wgtrWgtToHints(sub_tree); - if (!param->Hints) - { - rval = -1; - goto htcmpd_cleanup; - } - - /** Close the object **/ - sub_tree = NULL; - } - subobj_qy = NULL; + fprintf(stderr, + "Warning: No graft point, so gname is empty, but it is required " + "to be a valid symbol. Expect this component to fail to render.\n" + ); } -#endif - /** Build the typed pod values for each data value **/ - for(i=0;iStrVal) - { - param->TypedObjData.Flags = DATA_TF_NULL; - } - else - { - param->TypedObjData.Flags = 0; - switch(param->TypedObjData.DataType) - { - case DATA_T_STRING: - param->TypedObjData.Data.String = param->StrVal; - break; - case DATA_T_INTEGER: - if (!param->StrVal[0]) - { - mssError(1,"HTCMPD","Failed to convert empty string for param '%s' to integer", param->Name); - rval = -1; - goto htcmpd_cleanup; - } - param->TypedObjData.Data.Integer = strtoi(param->StrVal,&ptr,10); - if (*ptr) - { - mssError(1,"HTCMPD","Failed to convert value '%s' for param '%s' to integer", param->StrVal, param->Name); - rval = -1; - goto htcmpd_cleanup; - } - break; - case DATA_T_DOUBLE: - if (!param->StrVal[0]) - { - mssError(1,"HTCMPD","Failed to convert empty string for param '%s' to double", param->Name); - rval = -1; - goto htcmpd_cleanup; - } - param->TypedObjData.Data.Double = strtod(param->StrVal,&ptr); - if (*ptr) - { - mssError(1,"HTCMPD","Failed to convert value '%s' for param '%s' to double", param->StrVal, param->Name); - rval = -1; - goto htcmpd_cleanup; - } - break; - default: - mssError(1,"HTCMPD","Unsupported type for param '%s'", param->Name); - rval= -1; - goto htcmpd_cleanup; - } - } + mssError(0, "HTCMPD", "Failed to write the start of the JS init call."); + goto err; + } - /** Verify the thing **/ - if (hntVerifyHints(param->Hints, &(param->TypedObjData), &ptr, NULL, s->ObjSession) < 0) - { - mssError(1,"HTCMPD","Invalid value '%s' for component '%s' param '%s': %s", param->StrVal, name, param->Name, ptr); - rval = -1; - goto htcmpd_cleanup; - } + /** Init component **/ + const bool has_gbuf = (gbuf[0] != '\0'); + if (htrAddScriptInit_va(s, + "\tcmpd_init(wgtrGetNodeRef(ns, '%STR&SYM'), { " + "vis:%POS, " + "gns:%['%STR&SYM'%]%[null%], " + "gname:'%STR&SYM'" + "%[, expe:'%STR&SYM'%]" + "%[, expa:'%STR&SYM'%]" + "%[, expp:'%STR&SYM'%]" + "%[, applyhint:'%STR&SYM'%]" + "});\n", + name, + is_visual, + (has_gbuf), gbuf, (!has_gbuf), + gname, + (expose_events_for[0] != '\0'), expose_events_for, + (expose_actions_for[0] != '\0'), expose_actions_for, + (expose_props_for[0] != '\0'), expose_props_for, + (apply_hints_to[0] != '\0'), apply_hints_to + ) != 0) + { + mssError(0, "HTCMPD", "Failed to write JS init call."); + goto err; } - /** Add actions, events, and client properties **/ - for (i=0;iChildren));i++) + /** Declare actions, events, and cprops for use in connectors. **/ + const unsigned int n_sub_trees = xaCount(&(tree->Children)); + for (unsigned int i = 0u; i < n_sub_trees; i++) { sub_tree = xaGetItem(&(tree->Children), i); @@ -307,78 +250,76 @@ htcmpdRender(pHtSession s, pWgtrNode tree, int z) if (cxsecVerifySymbol(subobj_name) < 0) { mssError(1,"HTCMPD","Invalid name '%s' for action/event/cprop in component '%s'", subobj_name, name); - rval = -1; - goto htcmpd_cleanup; + goto err; } - /** Get type **/ - wgtrGetPropertyValue(sub_tree, "outer_type", DATA_T_STRING, POD(&ptr)); - if (!strcmp(ptr,"widget/component-decl-action")) - { - htrAddScriptInit_va(s, " wgtrGetNodeRef(ns,\"%STR&SYM\").addAction('%STR&SYM');\n", name, subobj_name); - sub_tree->RenderFlags |= HT_WGTF_NOOBJECT; - } - else if (!strcmp(ptr,"widget/component-decl-event")) - { - htrAddScriptInit_va(s, " wgtrGetNodeRef(ns,\"%STR&SYM\").addEvent('%STR&SYM');\n", name, subobj_name); - sub_tree->RenderFlags |= HT_WGTF_NOOBJECT; - } - else if (!strcmp(ptr,"widget/component-decl-cprop")) + /** Only component-decl-TYPE sub-widgets are declared here. **/ + char* fn_name = htcmpd_internal_GetJSFnName(sub_tree); + if (fn_name == NULL) continue; + + /** Write the requested declaration function call. **/ + if (htrAddScriptInit_va(s, "\tdecl%POS.%STR('%STR&SYM');\n", id, fn_name, subobj_name) != 0) { - htrAddScriptInit_va(s, " wgtrGetNodeRef(ns,\"%STR&SYM\").addProp('%STR&SYM');\n", name, subobj_name); - sub_tree->RenderFlags |= HT_WGTF_NOOBJECT; + mssError(0, "HTCMPD", "Failed to write JS function init call."); + goto err; } - - sub_tree = NULL; + sub_tree->RenderFlags |= HT_WGTF_NOOBJECT; } - /** Do subwidgets **/ - /*htrRenderSubwidgets(s, tree, z+2);*/ - for (i=0;iChildren));i++) + /** Render sub-widgets. **/ + for (unsigned int i = 0u; i < n_sub_trees; i++) { sub_tree = xaGetItem(&(tree->Children), i); - wgtrGetPropertyValue(sub_tree, "outer_type", DATA_T_STRING, POD(&ptr)); - if (strcmp(ptr,"widget/component-decl-action") && - strcmp(ptr,"widget/component-decl-event") && - strcmp(ptr,"widget/component-decl-cprop")) + char* fn_name = htcmpd_internal_GetJSFnName(sub_tree); + + /** Widget isn't a component-decl type we handle, render normally. **/ + if (fn_name == NULL) { - htrRenderWidget(s, sub_tree, z+2); + if (htrRenderWidget(s, sub_tree, z + 2) != 0) + { + mssError(0, "HTCMPD", "Failed to render widget in declared component."); + goto err; + } + continue; } - else if (strcmp(ptr,"widget/component-decl-action") == 0) + + /** Search for connectors inside component-decl-action widgets. **/ + if (strcmp(fn_name, "addAction") == 0) { - /** allow connectors inside component-decl-action **/ - for (j=0;jChildren));j++) + const unsigned int n_con_trees = xaCount(&(sub_tree->Children)); + for (unsigned int j = 0u; j < n_con_trees; j++) { conn_tree = xaGetItem(&(sub_tree->Children), j); wgtrGetPropertyValue(conn_tree, "outer_type", DATA_T_STRING, POD(&ptr)); if (strcmp(ptr,"widget/connector") == 0) { - htrRenderWidget(s, conn_tree, z+2); + /** Render connector. **/ + if (htrRenderWidget(s, conn_tree, z + 2) != 0) + { + mssError(0, "HTCMPD", "Failed to render connector in component-decl-action widget."); + goto err; + } } } } - sub_tree = NULL; } - /** End init for component **/ - htrAddScriptInit_va(s, " cmpd_endinit(wgtrGetNodeRef(ns,\"%STR&SYM\"));\n", name); - - htcmpd_cleanup: -// if (subobj) objClose(subobj); -// if (subobj_qy) objQueryClose(subobj_qy); - for(i=0;iHints) objFreeHints(param->Hints); - if (param->Name) nmSysFree(param->Name); - nmFree(param, sizeof(HTCmpdParam)); - } + mssError(0, "HTCMPD", "Failed to write the end of the JS init call."); + goto err; } - xaDeInit(&attrs); - return rval; + /** Success. **/ + return 0; + + err: + mssError(0, "HTCMPD", + "Failed to render \"%s\":\"%s\" (id: %u).", + tree->Name, tree->Type, id + ); + return -1; } @@ -398,13 +339,6 @@ htcmpdInitialize() strcpy(drv->WidgetName,"component-decl"); drv->Render = htcmpdRender; - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_connector.c b/centrallix/htmlgen/htdrv_connector.c index d16d198f6..54860f3ed 100644 --- a/centrallix/htmlgen/htdrv_connector.c +++ b/centrallix/htmlgen/htdrv_connector.c @@ -1,22 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/xstring.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -44,6 +30,18 @@ /* specified widget. */ /************************************************************************/ +#include +#include + +#include "cxlib/check.h" +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xstring.h" +#include "expression.h" +#include "ht_render.h" +#include "wgtr.h" + /*** htconnRender - generate the HTML code for the page. ***/ @@ -51,31 +49,26 @@ int htconnRender(pHtSession s, pWgtrNode tree, int z) { char* ptr; - char* vstr; - int vint; - double vdbl; char name[64]; char event[32] = ""; char target[128]; char source[128]; char action[32] = ""; - int i; - XString xs; - pExpression code; - int first; - int inside_action = 0; - /*char rpt_context[128];*/ + XString xs = { AllocLen: 0 }; + int rval = -1; - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML ) + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTCONN","Netscape DOM or W3C DOM1HTML support required"); - return -1; + mssError(1, "HTCONN", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto end; } /** Inside a component-decl-action? **/ + bool inside_action = false; if (tree->Parent && wgtrGetPropertyValue(tree->Parent, "outer_type", DATA_T_STRING, POD(&ptr)) == 0 && !strcmp(ptr, "widget/component-decl-action")) { - inside_action = 1; + inside_action = true; wgtrGetPropertyValue(tree->Parent, "name", DATA_T_STRING, POD(&ptr)); strtcpy(event, ptr, sizeof(event)); } @@ -86,14 +79,14 @@ htconnRender(pHtSession s, pWgtrNode tree, int z) if (inside_action) { mssError(1,"HTCONN","Connector inside componentdecl-action cannot have an 'event' property"); - return -1; + goto end; } strtcpy(event,ptr,sizeof(event)); } if (!*event) { mssError(1,"HTCONN","Connector must have an 'event' property"); - return -1; + goto end; } if (wgtrGetPropertyValue(tree,"action",DATA_T_STRING,POD(&ptr)) == 0) @@ -103,25 +96,13 @@ htconnRender(pHtSession s, pWgtrNode tree, int z) if (!*action) { mssError(1,"HTCONN","Connector must have an 'action' property"); - return -1; + goto end; } /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto end; strtcpy(name,ptr,sizeof(name)); - /** Are we inside a widget/repeat context? **/ - /*rpt_context[0] = '\0'; - if (!strncmp(name, WGTR_REPEAT_PREFIX, strlen(WGTR_REPEAT_PREFIX))) - { - ptr = strchr(name+strlen(WGTR_REPEAT_PREFIX),'_'); - if (ptr) - { - strtcpy(rpt_context, name, sizeof(rpt_context)); - rpt_context[ptr - name] = '\0'; - } - }*/ - /** Source/target **/ if (wgtrGetPropertyValue(tree,"target",DATA_T_STRING,POD(&ptr)) != 0) target[0] = '\0'; @@ -135,106 +116,149 @@ htconnRender(pHtSession s, pWgtrNode tree, int z) if (inside_action) { mssError(1,"HTCONN","Connector inside componentdecl-action cannot have a 'source' property"); - return -1; + goto end; } strtcpy(source, ptr, sizeof(source)); } /** Build the param list **/ - xsInit(&xs); - first = 1; + if (check(xsInit(&xs)) != 0) goto end; + bool first = true; for(ptr = wgtrFirstPropertyName(tree); ptr; ptr = wgtrNextPropertyName(tree)) { if (!strcmp(ptr, "event") || !strcmp(ptr, "target") || !strcmp(ptr, "action") || !strcmp(ptr, "source") || !strcmp(ptr, "condition")) continue; - if (!first) xsConcatenate(&xs, ",", 1); - first = 0; - switch(wgtrGetPropertyType(tree, ptr)) - { + const int property_type = wgtrGetPropertyType(tree, ptr); + if (!first && check(xsConcatenate(&xs, ",", 1)) != 0) goto err_param; + first = false; + switch (property_type) + { case DATA_T_CODE: + { + pExpression code; wgtrGetPropertyValue(tree, ptr, DATA_T_CODE, POD(&code)); xsConcatQPrintf(&xs,"%STR&SYM:{type:'exp', value:function(_context,_this,ep) { with(ep) { return ", ptr); expGenerateText(code, NULL, xsWrite, &xs, '\0', "javascript", EXPR_F_RUNCLIENT); xsConcatenate(&xs,"; } } }",7); break; + } + case DATA_T_INTEGER: - wgtrGetPropertyValue(tree, ptr, DATA_T_INTEGER,POD(&vint)); - xsConcatQPrintf(&xs,"%STR&SYM:{type:'int', value:%INT}",ptr,vint); + { + int vint; + wgtrGetPropertyValue(tree, ptr, DATA_T_INTEGER, POD(&vint)); + if (xsConcatQPrintf(&xs, "%STR&SYM:{type:'int', value:%INT}", ptr, vint) < 0) + { + mssError(1, "HTCONN", "Failed to write int JSON."); + goto err_param; + } break; + } + case DATA_T_DOUBLE: + { + double vdbl; wgtrGetPropertyValue(tree, ptr, DATA_T_DOUBLE,POD(&vdbl)); - xsConcatQPrintf(&xs,"%STR&SYM:{type:'dbl', value:%DBL}", ptr, vdbl); + if (xsConcatQPrintf(&xs, "%STR&SYM:{type:'dbl', value:%DBL}", ptr, vdbl) < 0) + { + mssError(1, "HTCONN", "Failed to write double JSON."); + goto err_param; + } break; + } + case DATA_T_STRING: - wgtrGetPropertyValue(tree, ptr, DATA_T_STRING,POD(&vstr)); - if (!strpbrk(vstr," !@#$%^&*()-=+`~;:,.<>/?'\"[]{}\\|")) - { - xsConcatQPrintf(&xs, "%STR&SYM:{type:'sym', value:'%STR&SYM', namespace:ns}", ptr, vstr); + { + char* vstr; + wgtrGetPropertyValue(tree, ptr, DATA_T_STRING, POD(&vstr)); + if (strpbrk(vstr, " !@#$%^&*()-=+`~;:,.<>/?'\"[]{}\\|") == NULL) + { + if (xsConcatQPrintf(&xs, "%STR&SYM:{type:'sym', value:'%STR&SYM', namespace:ns}", ptr, vstr) < 0) + { + mssError(1, "HTCONN", "Failed to write string symbol parameter JSON."); + goto err_param; + } } else - { - xsConcatQPrintf(&xs, "%STR&SYM:{type:'str', value:'%STR&JSSTR'}", ptr, vstr); + { + if (xsConcatQPrintf(&xs, "%STR&SYM:{type:'str', value:'%STR&JSSTR'}", ptr, vstr) < 0) + { + mssError(1, "HTCONN", "Failed to write string parameter JSON."); + goto err_param; + } } break; + } + + default: + mssError(1, "HTCONN", "Unsupported type %d.", property_type); + goto err_param; } - } - /** Add a script init to install the connector **/ -#if 00 - if (*rpt_context && *source) - { - /** Try repeat-specific node first, then normally named node **/ - htrAddScriptInit_va(s, " var src=nodes[\"%STR&SYM_%STR&SYM\"];\n", - rpt_context, - source); - htrAddScriptInit_va(s, " if(!src) src=nodes[\"%STR&SYM\"];\n", - source); + /** Success. **/ + continue; + + err_param: + mssError(0, "HTCONN", + "Failed to write JSON for connector parameter %s of type %d.", + ptr, property_type + ); + goto end; } - else - { - htrAddScriptInit_va(s, " var src=%[wgtrGetParent(nodes[\"%STR&SYM\"])%]%[nodes[\"%STR&SYM\"]%];\n", -#endif - /*htrAddScriptInit_va(s, " var src=%[wgtrGetParent(wgtrGetNodeRef(ns,\"%STR&SYM\"))%]%[wgtrGetNodeRef(ns, \"%STR&SYM\")%];\n", - !*source, name, - *source, source);*/ -#if 00 - } - if (*rpt_context && *target) + + /** Determine the target in a new scope. **/ + const int no_source = (source == NULL || source[0] == '\0'); + if (htrAddScriptInit_va(s, "\t{ " + "let target = wgtrGetNodeRef(ns, '%STR&SYM'); " + "%[target = wgtrGetParent(target); %]" + "%[target = wgtrGetParent(target); %]", + (no_source) ? name : source, + (no_source), + (inside_action) // only true when no_source is true. + ) != 0) { - htrAddScriptInit_va(s, " var tgt=\"%STR&SYM_%STR&SYM\";\n", - rpt_context, - target); - htrAddScriptInit_va(s, " if(!nodes[tgt]) tgt='%STR&SYM';\n", - target); + mssError(0, "HTCONN", "Failed to write JS target."); + goto end; } - else + + /** Call the functions on the target, and close the scope. **/ + const int no_target = (target == NULL || target[0] == '\0'); + if (htrAddScriptInit_va(s, "target" + ".ifcProbe(ifEvent)" + ".Connect('%STR&SYM', '%STR&SYM', '%STR&SYM', {%STR}, ns); " + "}\n", + event, (no_target) ? tree->Parent->Name : target, action, xs.String + ) != 0) { -#endif - /*htrAddScriptInit_va(s, " var tgt=%['%STR&SYM'%]%[wgtrGetName(wgtrGetParent(wgtrGetNodeRef(ns,\"%STR&SYM\")))%];\n", - *target, target, - !*target, name);*/ -#if 00 + mssError(0, "HTCONN", "Failed to write JS event connect."); + goto end; } -#endif - //htrAddScriptInit_va(s, " src.ifcProbe(ifEvent).Connect('%STR&SYM', tgt, '%STR&SYM', {%STR});\n", - htrAddScriptInit_va(s, " %[wgtrGetParent(wgtrGetParent(wgtrGetNodeRef(ns,\"%STR&SYM\")))%]%[wgtrGetParent(wgtrGetNodeRef(ns,\"%STR&SYM\"))%]%[wgtrGetNodeRef(ns, \"%STR&SYM\")%].ifcProbe(ifEvent).Connect('%STR&SYM', %['%STR&SYM'%]%[wgtrGetName(wgtrGetParent(wgtrGetNodeRef(ns,\"%STR&SYM\")))%], '%STR&SYM', {%STR}, ns);\n", - inside_action, name, - !*source && !inside_action, name, - *source && !inside_action, source, - event, - *target, target, !*target, name, - action, - xs.String); xsDeInit(&xs); + xs.AllocLen = 0; - htrAddScriptInclude(s, "/sys/js/htdrv_connector.js", 0); + /** Include the connector library. **/ + if (htrAddScriptInclude(s, "/sys/js/htdrv_connector.js", 0) != 0) goto end; tree->RenderFlags |= HT_WGTF_NOOBJECT; - /** Check for more sub-widgets within the conn entity. **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+1); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto end; - return 0; + /** Success. **/ + rval = 0; + + end: + if (rval != 0) + { + mssError(0, "HTCONN", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); + } + + /** Clean up. **/ + if (xs.AllocLen != 0) check(xsDeInit(&xs)); /** Failure ignored. **/ + + return rval; } @@ -244,8 +268,6 @@ int htconnInitialize() { pHtDriver drv; - /*pHtEventAction action; - pHtParam param;*/ /** Allocate the driver **/ drv = htrAllocDriver(); diff --git a/centrallix/htmlgen/htdrv_datetime.c b/centrallix/htmlgen/htdrv_datetime.c index 703ccbf5f..b2dab3a5c 100644 --- a/centrallix/htmlgen/htdrv_datetime.c +++ b/centrallix/htmlgen/htdrv_datetime.c @@ -1,21 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" -#include "cxlib/qprintf.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,6 +28,18 @@ /* Description: HTML driver for a 'date time' widget */ /************************************************************************/ +#include +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "obj.h" +#include "wgtr.h" + + /** globals **/ static struct { @@ -65,8 +64,7 @@ htdtRender(pHtSession s, pWgtrNode tree, int z) char fieldname[HT_FIELDNAME_SIZE]; char form[64]; int type; - int x,y,w,h,w2=184,h2=190; - int id, i; + int x,y,w,h; int rval; int search_by_range; int date_only = 0; @@ -76,15 +74,16 @@ htdtRender(pHtSession s, pWgtrNode tree, int z) pObjQuery qy; pObject qy_obj; - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML) + /** Get an id for this. **/ + const int id = (HTDT.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTDT","Netscape or W3C DOM support required"); - return -1; + mssError(1, "HTDT", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTDT.idcnt++); - /** Get x,y,w,h of this object **/ if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) { @@ -119,7 +118,6 @@ htdtRender(pHtSession s, pWgtrNode tree, int z) /** Is this a date-only control rather than date-time combined? **/ date_only = htrGetBoolean(tree, "date_only", 0); - if (date_only == 1) h2 = 156; /** If no time is entered (or if date-only), what is the default time? **/ if (wgtrGetPropertyValue(tree,"default_time",DATA_T_STRING,POD(&ptr)) == 0) @@ -175,93 +173,166 @@ htdtRender(pHtSession s, pWgtrNode tree, int z) if (strlen(initialdate)) { objDataToDateTime(DATA_T_STRING, initialdate, &dt, NULL); - qpfPrintf(NULL, initialdate, sizeof(initialdate), "%STR %INT %INT, %INT:%INT:%INT", obj_short_months[dt.Part.Month], - dt.Part.Day+1, - dt.Part.Year+1900, - dt.Part.Hour, - dt.Part.Minute, - dt.Part.Second); + snprintf( + initialdate, sizeof(initialdate), + "%s %d %d, %d:%d:%d", + obj_short_months[dt.Part.Month], + dt.Part.Day + 1, + dt.Part.Year + 1900, + dt.Part.Hour, + dt.Part.Minute, + dt.Part.Second + ); } /** Get colors **/ htrGetBackground(tree, NULL, 1, bgcolor, sizeof(bgcolor)); if (!*bgcolor) strcpy(bgcolor,"background-color:white;"); - //else strcpy(bgcolor, "bgcolor=green"); - -/////////////////////////////////////// -// if (wgtrGetPropertyValue(tree,"bgcolor",DATA_T_STRING,POD(&ptr)) == 0) -// sprintf(bgcolor,"%.40s",ptr); -// else { - //mssError(1,"HTDD","Date Time widget must have a 'bgcolor' property"); - //return -1; - //strcpy(bgcolor,"blue"); -// } if (wgtrGetPropertyValue(tree,"fgcolor",DATA_T_STRING,POD(&ptr)) == 0) strtcpy(fgcolor,ptr,sizeof(fgcolor)); else strcpy(fgcolor,"black"); - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#dt%POSbtn { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; cursor:default; border:1px outset #e0e0e0; %STR }\n",id,x,y,w,h,z, bgcolor); - htrAddStylesheetItem_va(s,"\t#dt%POScon1 { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:inherit; LEFT:1px; TOP:1px; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,w-20,h-2,z+1); - htrAddStylesheetItem_va(s,"\t#dt%POScon2 { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:hidden; LEFT:1px; TOP:1px; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,w-20,h-2,z+1); + /** Write style headers. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#dt%POSbtn { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "cursor:pointer; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "border:1px outset #e0e0e0; " + "%STR " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z, + bgcolor + ) != 0) + { + mssError(0, "HTDT", "Failed to write datetime button CSS."); + goto err; + } + /*** The text area of the control. We use 1px padding around the 18px + *** icon. The client reads these sizes from the CSS. + ***/ + if (htrAddStylesheetItem_va(s, + "\t\t.dt%POScon { " + "position:absolute; " + "overflow:hidden; " + "left:1px; " + "top:1px; " + "width:calc(100%% - 20px); " + "height:calc(100%% - 2px); " + "z-index:%POS; " + "}\n", + id, + z + 1 + ) != 0) + { + mssError(0, "HTDT", "Failed to write datetime con CSS."); + goto err; + } + if (htrAddStylesheetItem(s, + "\t\t.dt_dropdown { " + "cursor:default; " + "}\n" + ) != 0) + { + mssError(0, "HTDT", "Failed to write datetime dropdown CSS."); + goto err; + } - /** Write named global **/ - htrAddScriptGlobal(s, "dt_current", "null", 0); - htrAddScriptGlobal(s, "dt_timeout", "null", 0); - htrAddScriptGlobal(s, "dt_timeout_fn", "null", 0); - htrAddScriptGlobal(s, "dt_img_y", "0", 0); + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "dt%POSbtn", id) != 0) + { + mssError(0, "HTDT", "Failed to add object linkage."); + goto err; + } - htrAddWgtrObjLinkage_va(s, tree, "dt%POSbtn",id); + /** Write named globals. **/ + if (htrAddScriptGlobal(s, "dt_current", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "dt_img_y", "0", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "dt_timeout_fn", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "dt_timeout", "null", 0) != 0) goto err; /** Script includes **/ - htrAddScriptInclude(s, "/sys/js/ht_utils_date.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - htrAddScriptInclude(s, "/sys/js/htdrv_datetime.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - - /** Script initialization call. **/ - htrAddScriptInit_va(s, " dt_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"),c1:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"),\"dt%POScon1\"),c2:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"),\"dt%POScon2\"),id:\"%STR&JSSTR\", background:\"%STR&JSSTR\", foreground:\"%STR&JSSTR\", fieldname:\"%STR&JSSTR\", form:\"%STR&JSSTR\", width:%INT, height:%INT, width2:%INT, height2:%INT, sbr:%INT, donly:%INT, dtime:\"%STR&JSSTR\"})\n", - name, - name,id, - name,id, - initialdate, bgcolor, fgcolor, fieldname, form, - w-20, h, w2,h2, + if (htrAddScriptInclude(s, "/sys/js/ht_utils_date.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_datetime.js", 0) != 0) goto err; + + /** Add the event handling scripts. **/ + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "dt","dt_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "dt","dt_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "dt","dt_mouseout") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "dt","dt_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "dt","dt_mouseup") != 0) goto err; + + /** Write the initialization call in its own scope. **/ + if (htrAddScriptInit_va(s, "\t{ " + "const layer = wgtrGetNodeRef(ns, '%STR&SYM'); " + "dt_init({ " + "layer, " + "c1:htr_subel(layer, 'dt%POScon1'), " + "c2:htr_subel(layer, 'dt%POScon2'), " + "id:'%STR&JSSTR', " + "background:'%STR&JSSTR', " + "foreground:'%STR&JSSTR', " + "fieldname:'%STR&JSSTR', " + "form:'%STR&JSSTR', " + "sbr:%INT, " + "donly:%INT, " + "dtime:'%STR&JSSTR', " + "}); }\n", + name, id, id, + initialdate, + bgcolor, fgcolor, fieldname, form, search_by_range, - date_only, default_time); - - /** HTML body
elements for the layers. **/ - htrAddBodyItem_va(s,"
\n" - "\n", id); - /*htrAddBodyItem_va(s,"\n",w); - htrAddBodyItem(s, " \n"); - htrAddBodyItem_va(s," \n",w-2); - htrAddBodyItem(s, " \n"); - htrAddBodyItem_va(s," \n",h-2); - htrAddBodyItem(s, " \n"); - htrAddBodyItem_va(s," \n",h-2); - htrAddBodyItem(s, " \n"); - htrAddBodyItem_va(s," \n",w-2); - htrAddBodyItem(s, " \n"); - htrAddBodyItem(s, "
\n");*/ - htrAddBodyItem_va(s,"
\n",id); - htrAddBodyItem_va(s,"
\n",id); - htrAddBodyItem(s, "
\n"); - - /** Add the event handling scripts **/ - htrAddEventHandlerFunction(s, "document","MOUSEDOWN","dt","dt_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEUP","dt","dt_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE","dt","dt_mousemove"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER","dt","dt_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEOUT","dt","dt_mouseout"); - - /** Check for more sub-widgets within the datetime. **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+1); + date_only, default_time + ) != 0) + { + mssError(0, "HTDT", "Failed to write datetime initialization script."); + goto err; + } - return 0; + /** Write HTML. **/ + if (htrAddBodyItem_va(s, + "
" + "icon" + "
" + "" + "
\n", + id, + id, id, + id, id + ) != 0) + { + mssError(0, "HTDT", "Failed to write datetime HTML."); + goto err; + } + + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto err; + + /** Success. **/ + return 0; + + err: + mssError(0, "HTDT", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } @@ -281,17 +352,6 @@ htdtInitialize() strcpy(drv->WidgetName,"datetime"); drv->Render = htdtRender; - /** Register events **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - htrAddEvent(drv,"DataChange"); - htrAddEvent(drv,"GetFocus"); - htrAddEvent(drv,"LoseFocus"); - /** Register. **/ htrRegisterDriver(drv); @@ -301,4 +361,3 @@ htdtInitialize() return 0; } - diff --git a/centrallix/htmlgen/htdrv_dropdown.c b/centrallix/htmlgen/htdrv_dropdown.c index 335fe7f48..b24e79638 100644 --- a/centrallix/htmlgen/htdrv_dropdown.c +++ b/centrallix/htmlgen/htdrv_dropdown.c @@ -1,21 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 2000-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 2000-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,11 +28,31 @@ /* Description: HTML Widget driver for a drop down list */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "cxlib/xstring.h" +#include "ht_render.h" +#include "obj.h" +#include "wgtr.h" + + /** globals **/ static struct { int idcnt; } HTDD; +/** Dropdown modes. **/ +#define HTDD_STATIC 0 +#define HTDD_DYNAMIC_SERVER 1 +#define HTDD_DYNAMIC 2 +#define HTDD_DYNAMIC_CLIENT HTDD_DYNAMIC +#define HTDD_OBJECTSOURCE 3 +#define HTDD_EASTER_EGG_4 4 /* htddRender - generate the HTML code for the page. @@ -53,7 +60,7 @@ static struct { int htddRender(pHtSession s, pWgtrNode tree, int z) { char bgstr[HT_SBUF_SIZE]; char textcolor[HT_SBUF_SIZE]; - char hilight[HT_SBUF_SIZE]; + char highlight[HT_SBUF_SIZE]; char string[HT_SBUF_SIZE]; char fieldname[30]; char form[64]; @@ -65,25 +72,30 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { char *attr; int type, rval, mode, flag=0; int x,y,w,h; - int id, i; int num_disp; int query_multiselect; int invalid_select_default; int pop_w; ObjData od; XString xs; - pObjQuery qy; - pObject qy_obj; + pXString items_xs = NULL; + pObjQuery qy = NULL; + pObject qy_obj = NULL; pWgtrNode subtree; + int ret = -1; + + /** Get an id for this. **/ + const int id = (HTDD.idcnt++); - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML) - { - mssError(1,"HTDD","Netscape or W3C DOM support required"); - return -1; - } + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) + { + mssError(1, "HTDD", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto end; + } - /** Get an id for this. **/ - id = (HTDD.idcnt++); + /** Backwards compat: 'hilight' was corrected to 'highlight'. **/ + wgtrRenameProperty(tree, "hilight", "highlight"); /** Get x,y of this object **/ if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; @@ -93,7 +105,7 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { h = s->ClientInfo->ParagraphHeight+2; if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { mssError(1,"HTDD","Drop Down widget must have a 'width' property"); - return -1; + goto end; } pop_w = w; @@ -105,18 +117,18 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { if (wgtrGetPropertyValue(tree,"numdisplay",DATA_T_INTEGER,POD(&num_disp)) != 0) num_disp=3; - if (wgtrGetPropertyValue(tree,"hilight",DATA_T_STRING,POD(&ptr)) == 0) { - strtcpy(hilight,ptr,sizeof(hilight)); + if (wgtrGetPropertyValue(tree,"highlight",DATA_T_STRING,POD(&ptr)) == 0) { + strtcpy(highlight,ptr,sizeof(highlight)); } else { - mssError(1,"HTDD","Drop Down widget must have a 'hilight' property"); - return -1; + mssError(1,"HTDD","Drop Down widget must have a 'highlight' property"); + goto end; } if (wgtrGetPropertyValue(tree,"bgcolor",DATA_T_STRING,POD(&ptr)) == 0) { strtcpy(bgstr,ptr,sizeof(bgstr)); } else { mssError(1,"HTDD","Drop Down widget must have a 'bgcolor' property"); - return -1; + goto end; } if (wgtrGetPropertyValue(tree,"textcolor",DATA_T_STRING,POD(&ptr)) == 0) { @@ -141,65 +153,160 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { osrc[0]='\0'; /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto end; strtcpy(name,ptr,sizeof(name)); - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#dd%POSbtn { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; HEIGHT:%POSpx; WIDTH:%POSpx; Z-INDEX:%POS; cursor:default; background-color: %STR&CSSVAL; border:1px outset #e0e0e0;}\n",id,x,y,h,w,z,bgstr); - if (*textcolor) { - htrAddStylesheetItem_va(s,"\t#dd%POSbtn { color: %STR&CSSVAL; }\n",id,textcolor); - } - htrAddStylesheetItem_va(s,"\t#dd%POScon1 { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:inherit; LEFT:1px; TOP:1px; WIDTH:1024px; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,h-2,z+1); - htrAddStylesheetItem_va(s,"\t#dd%POScon2 { OVERFLOW:hidden; POSITION:absolute; VISIBILITY:hidden; LEFT:1px; TOP:1px; WIDTH:1024px; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,h-2,z+1); - - htrAddScriptGlobal(s, "dd_current", "null", 0); - htrAddScriptGlobal(s, "dd_lastkey", "null", 0); - htrAddScriptGlobal(s, "dd_target_img", "null", 0); - htrAddScriptGlobal(s, "dd_thum_y","0",0); - htrAddScriptGlobal(s, "dd_timeout","null",0); - htrAddScriptGlobal(s, "dd_click_x","0",0); - htrAddScriptGlobal(s, "dd_click_y","0",0); - htrAddScriptGlobal(s, "dd_incr","0",0); - htrAddScriptGlobal(s, "dd_cur_mainlayer","null",0); - htrAddWgtrObjLinkage_va(s, tree, "dd%POSbtn", id); - - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0); - htrAddScriptInclude(s, "/sys/js/htdrv_dropdown.js", 0); - - htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "dd", "dd_mousemove"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER", "dd", "dd_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEUP", "dd", "dd_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "dd", "dd_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEOUT", "dd", "dd_mouseout"); - if (s->Capabilities.Dom1HTML) - htrAddEventHandlerFunction(s, "document", "CONTEXTMENU", "dd", "dd_contextmenu"); + /** Write basic element CSS. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#dd%POSbtn { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "cursor:pointer; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "background-color: %STR&CSSVAL; " + "border:1px outset #e0e0e0; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z, + bgstr + ) != 0) + { + mssError(0, "HTDD", "Failed to write base btn CSS."); + goto end; + } + if (*textcolor) + { + if (htrAddStylesheetItem_va(s, + "\t\t#dd%POSbtn { " + "color:%STR&CSSVAL; " + "}\n", + id, + textcolor + ) != 0) + { + mssError(0, "HTDD", "Failed to write btn CSS text color."); + goto end; + } + } + if (htrAddStylesheetItem_va(s, + "\t\t.dd%POScon { " + "position:absolute; " + "overflow:hidden; " + "display:flex; " + "align-items:center; " + "left:2px; " + "top:1px; " + "width:calc(100%% - 21px); " + "height:calc(100%% - 2px); " + "z-index:%POS; " + "}\n", + id, + z + 1 + ) != 0) + { + mssError(0, "HTDD", "Failed to write con CSS."); + goto end; + } + + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "dd%POSbtn", id) != 0) + { + mssError(0, "HTDD", "Failed to add object linkage."); + goto end; + } + + /** Write JS globals. **/ + if (htrAddScriptGlobal(s, "dd_click_x", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "dd_click_y", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "dd_cur_mainlayer", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "dd_current", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "dd_incr", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "dd_lastkey", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "dd_target_img", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "dd_thum_y", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "dd_timeout", "null", 0) != 0) goto end; + + /** Write JS script includes. **/ + if (htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/htdrv_dropdown.js", 0) != 0) goto end; + + /** Register JS event handlers. **/ + if (htrAddEventHandlerFunction(s, "document", "CONTEXTMENU", "dd", "dd_contextmenu") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "dd", "dd_mousedown") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "dd", "dd_mousemove") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "dd", "dd_mouseout") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "dd", "dd_mouseover") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "dd", "dd_mouseup") != 0) goto end; /** Get the mode (default to 1, dynamicpage) **/ - mode = 0; - if (wgtrGetPropertyValue(tree,"mode",DATA_T_STRING,POD(&ptr)) == 0) { - if (!strcmp(ptr,"static")) mode = 0; - else if (!strcmp(ptr,"dynamic_server")) mode = 1; - else if (!strcmp(ptr,"dynamic")) mode = 2; - else if (!strcmp(ptr,"dynamic_client")) mode = 2; - else if (!strcmp(ptr,"objectsource")) mode = 3; - else { - mssError(1,"HTDD","Dropdown widget has not specified a valid mode."); - return -1; + mode = HTDD_STATIC; + if (wgtrGetPropertyValue(tree,"mode",DATA_T_STRING,POD(&ptr)) == 0) + { + if (strcmp(ptr, "static") == 0) mode = HTDD_STATIC; + else if (strcmp(ptr, "dynamic_server") == 0) mode = HTDD_DYNAMIC_SERVER; + else if (strcmp(ptr, "dynamic") == 0) mode = HTDD_DYNAMIC; + else if (strcmp(ptr, "dynamic_client") == 0) mode = HTDD_DYNAMIC_CLIENT; + else if (strcmp(ptr, "objectsource") == 0) mode = HTDD_OBJECTSOURCE; + else + { + mssError(1, "HTDD", "Invalid dropdown widget 'mode' value: \"%s\"", ptr); + goto end; + } } - } - sql = 0; - if (wgtrGetPropertyValue(tree,"sql",DATA_T_STRING,POD(&sql)) != 0 && mode != 0 && mode != 3) { + sql = NULL; + if (wgtrGetPropertyValue(tree,"sql",DATA_T_STRING,POD(&sql)) != 0 && mode != HTDD_STATIC && mode != HTDD_OBJECTSOURCE) + { mssError(1, "HTDD", "SQL parameter was not specified for dropdown widget"); - return -1; - } + goto end; + } htrCheckAddExpression(s,tree,name,"sql"); - /** Script initialization call. **/ - htrAddScriptInit_va(s," dd_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), c1:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"), \"dd%POScon1\"), c2:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"), \"dd%POScon2\"), background:'%STR&JSSTR', highlight:'%STR&JSSTR', fieldname:'%STR&JSSTR', numDisplay:%INT, mode:%INT, sql:'%STR&JSSTR', width:%INT, height:%INT, form:'%STR&JSSTR', osrc:'%STR&JSSTR', qms:%INT, ivs:%INT, popup_width:%INT});\n", name, name, id, name, id, bgstr, hilight, fieldname, num_disp, mode, sql?sql:"", w, h, form, osrc, query_multiselect, invalid_select_default, pop_w); + /** Write the initialization call in its own scope. **/ + if (htrAddScriptInit_va(s, "\t{ " + "const layer = wgtrGetNodeRef(ns, '%STR&SYM'); " + "dd_init({ " + "layer, " + "c1:htr_subel(layer, 'dd%POScon1'), " + "c2:htr_subel(layer, 'dd%POScon2'), " + "background:'%STR&JSSTR', " + "highlight:'%STR&JSSTR', " + "fieldname:'%STR&JSSTR', " + "numDisplay:%INT, " + "mode:%INT, " + "sql:'%STR&JSSTR', " + "form:'%STR&JSSTR', " + "osrc:'%STR&JSSTR', " + "qms:%INT, " + "ivs:%INT, " + "width:%INT, " + "height:%INT, " + "popup_width:%INT, " + "}); }\n", + name, id, id, + bgstr, highlight, + fieldname, num_disp, mode, + (sql != NULL) ? sql : "", + form, osrc, query_multiselect, + invalid_select_default, + w, h, pop_w + ) != 0) + { + mssError(0, "HTDD", "Failed to write JS init call."); + goto end; + } /** HTML body
element for the layers. **/ htrAddBodyItem_va(s,"
\n" @@ -215,12 +322,12 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { htrAddBodyItem_va(s," \n",w-2); htrAddBodyItem(s, " \n"); htrAddBodyItem(s, "\n");*/ - htrAddBodyItem_va(s,"
\n",id); - htrAddBodyItem_va(s,"
\n",id); + htrAddBodyItem_va(s,"
\n", id, id); + htrAddBodyItem_va(s,"\n", id, id); htrAddBodyItem(s, "
\n"); /* Read and initialize the dropdown items */ - if (mode == 1) { + if (mode == HTDD_DYNAMIC_SERVER) { /** The result set from this SQL query can take two forms: positional or named. ** For Positional, the params are: label, value, selected, group, hidden. ** For Named, the above names can appear in any order. @@ -228,15 +335,13 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { **/ if ((qy = objMultiQuery(s->ObjSession, sql, NULL, 0))) { flag=0; - htrAddScriptInit_va(s," dd_add_items(wgtrGetNodeRef(ns,\"%STR&SYM\"), [",name); + htrAddScriptInit_va(s, "\tdd_add_items(wgtrGetNodeRef(ns, '%STR&SYM'), [",name); while ((qy_obj = objQueryFetch(qy, O_RDONLY))) { // Label attr = objGetFirstAttr(qy_obj); if (!attr) { - objClose(qy_obj); - objQueryClose(qy); mssError(1, "HTDD", "SQL query must have at least two attributes: label and value."); - return -1; + goto end; } type = objGetAttrType(qy_obj, attr); rval = objGetAttrValue(qy_obj, attr, type,&od); @@ -244,9 +349,10 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { // Check if anything was returned if(rval != 0){ objClose(qy_obj); + qy_obj = NULL; continue; } - + if (type == DATA_T_INTEGER || type == DATA_T_DOUBLE) { str = objDataToStringTmp(type, (void*)(&od), DATA_F_QUOTED); } else { @@ -254,13 +360,12 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { } if (flag) htrAddScriptInit(s,","); htrAddScriptInit_va(s,"{wname:null, label:%STR,",str); + // Value attr = objGetNextAttr(qy_obj); if (!attr) { - objClose(qy_obj); - objQueryClose(qy); mssError(1, "HTDD", "SQL query must have at least two attributes: label and value."); - return -1; + goto end; } type = objGetAttrType(qy_obj, attr); @@ -306,40 +411,44 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { } objClose(qy_obj); + qy_obj = NULL; flag=1; } htrAddScriptInit(s,"]);\n"); objQueryClose(qy); + qy = NULL; } } - else if(mode==3) { + else if (mode == HTDD_OBJECTSOURCE) { /* get objects from form */ } - flag=0; - for (i=0;iChildren));i++) + const int n_children = xaCount(&(tree->Children)); + for (int i = 0; i < n_children; i++) { subtree = xaGetItem(&(tree->Children), i); if (!strcmp(subtree->Type, "widget/dropdownitem")) subtree->RenderFlags |= HT_WGTF_NOOBJECT; - if (!strcmp(subtree->Type,"widget/dropdownitem") && mode == 0) + + /** Write JS to render dropdown items in static mode. **/ + if (!strcmp(subtree->Type,"widget/dropdownitem") && mode == HTDD_STATIC) { if (wgtrGetPropertyValue(subtree,"label",DATA_T_STRING,POD(&ptr)) != 0) { - mssError(1,"HTDD","Drop Down widget must have a 'width' property"); - return -1; + mssError(1,"HTDD","Drop Down Item widget must have a 'label' property"); + goto end; } strtcpy(string, ptr, sizeof(string)); - if (flag) + if (items_xs != NULL) { xsConcatenate(&xs, ",", 1); } - else + else { - xsInit(&xs); + if (xsInit(&xs) < 0) goto end; + items_xs = &xs; xsConcatQPrintf(&xs, " dd_add_items(wgtrGetNodeRef(ns,\"%STR&SYM\"), [", name); - flag=1; } wgtrGetPropertyValue(subtree,"name",DATA_T_STRING,POD(&ptr)); xsConcatQPrintf(&xs,"{wname:'%STR&SYM', label:'%STR&JSSTR',", ptr, string); @@ -351,26 +460,38 @@ int htddRender(pHtSession s, pWgtrNode tree, int z) { if (wgtrGetPropertyValue(subtree,"value",DATA_T_STRING,POD(&ptr)) != 0) { - mssError(1,"HTDD","Drop Down widget must have a 'value' property"); - return -1; + mssError(1,"HTDD","Drop Down Item widget must have a 'value' property"); + goto end; } strtcpy(string,ptr, sizeof(string)); xsConcatQPrintf(&xs,"value:'%STR&JSSTR'}", string); } else { - htrRenderWidget(s, subtree, z+1); + if (htrRenderWidget(s, subtree, z + 1) != 0) goto end; } } - if (flag) + if (items_xs != NULL) { xsConcatenate(&xs, "]);\n", 4); htrAddScriptInit(s,xs.String); - xsDeInit(&xs); } + /** Success. **/ + ret = 0; - return 0; + end: + if (ret != 0) + { + mssError(0, "HTDD", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + } + if (qy_obj != NULL) objClose(qy_obj); + if (qy != NULL) objQueryClose(qy); + if (items_xs != NULL) xsDeInit(items_xs); + return ret; } @@ -390,16 +511,6 @@ int htddInitialize() { drv->Render = htddRender; xaAddItem(&(drv->PseudoTypes), "dropdownitem"); - /** Register events **/ - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - htrAddEvent(drv,"DataChange"); - htrAddEvent(drv,"GetFocus"); - htrAddEvent(drv,"LoseFocus"); - /** Register. **/ htrRegisterDriver(drv); @@ -409,4 +520,3 @@ int htddInitialize() { return 0; } - diff --git a/centrallix/htmlgen/htdrv_editbox.c b/centrallix/htmlgen/htdrv_editbox.c index 0223038f9..666ffb49e 100644 --- a/centrallix/htmlgen/htdrv_editbox.c +++ b/centrallix/htmlgen/htdrv_editbox.c @@ -1,21 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,6 +28,15 @@ /* Description: HTML Widget driver for a single-line editbox. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/newmalloc.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -61,50 +57,54 @@ htebRender(pHtSession s, pWgtrNode tree, int z) char descfg[64]; char descr[128]; int x=-1,y=-1,w,h; - int id, i; + int rval = -1; int is_readonly = 0; int is_raised = 0; - char* tooltip; - int maxchars; + char* tooltip = NULL; + int max_chars; char fieldname[HT_FIELDNAME_SIZE]; char form[64]; - int box_offset; - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom0IE && !s->Capabilities.Dom2Events) + /** Get an id for this. **/ + const int id = (HTEB.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTEB","Netscape, IE, or Dom2Events support required"); - return -1; + mssError(1, "HTEB", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto end; } - /** Get an id for this. **/ - id = (HTEB.idcnt++); - /** Get x,y,w,h of this object **/ if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { mssError(1,"HTEB","Editbox widget must have a 'width' property"); - return -1; + goto end; } if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) { mssError(1,"HTEB","Editbox widget must have a 'height' property"); - return -1; + goto end; } - /** Maximum characters to accept from the user **/ - if (wgtrGetPropertyValue(tree,"maxchars",DATA_T_INTEGER,POD(&maxchars)) != 0) maxchars=255; + /** Maximum characters to accept from the user. Negative means no limit. **/ + if (wgtrGetPropertyValue(tree,"max_chars",DATA_T_INTEGER,POD(&max_chars)) != 0) max_chars = -1; /** Readonly flag **/ - if (wgtrGetPropertyValue(tree,"readonly",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"yes")) is_readonly = 1; + is_readonly = htrGetBoolean(tree, "readonly", 0); /** Background color/image **/ strcpy(main_bg,""); htrGetBackground(tree, NULL, s->Capabilities.CSS2?1:0, main_bg, sizeof(main_bg)); /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) + { + mssError(1,"HTEB","Editbox widget must have a 'name' property"); + goto end; + } strtcpy(name,ptr,sizeof(name)); /** Get description color **/ @@ -137,72 +137,191 @@ htebRender(pHtSession s, pWgtrNode tree, int z) tooltip=nmSysStrdup(ptr); else tooltip=nmSysStrdup(""); + if (tooltip == NULL) + { + mssError(1, "HTEB", "Failed to allocate tooltip."); + goto end; + } if (wgtrGetPropertyValue(tree,"form",DATA_T_STRING,POD(&ptr)) == 0) strtcpy(form,ptr,sizeof(form)); else form[0]='\0'; - if (s->Capabilities.CSSBox) - box_offset = 1; - else - box_offset = 0; - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#eb%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; overflow:hidden; }\n",id,x,y,w-2*box_offset,z); - htrAddStylesheetItem_va(s,"\t#eb%POScon1 { VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; border:none; }\n",id,5,0,w-10,z+1); - - /** Write named global **/ - htrAddWgtrObjLinkage_va(s, tree, "eb%POSbase",id); - - /** Global for ibeam cursor layer **/ - htrAddScriptGlobal(s, "text_metric", "null", 0); - htrAddScriptGlobal(s, "eb_current", "null", 0); + if (htrAddStylesheetItem_va(s, + "\t\t#eb%POSbase { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + /** border-box: the border below is drawn inside the width/height. **/ + "box-sizing:border-box; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + ht_flex(x, ht_get_parent_w(tree), ht_get_fl_x(tree)), + ht_flex(y, ht_get_parent_h(tree), ht_get_fl_y(tree)), + ht_flex(w, ht_get_parent_w(tree), ht_get_fl_w(tree)), + z + ) != 0) + { + mssError(0, "HTEB", "Failed to write base CSS."); + goto end; + } + if (htrAddStylesheetItem_va(s, + "\t\t#eb%POScon1 { " + "position:absolute; " + "visibility:inherit; " + "box-sizing:border-box; " + "left:6px; " + "top:0px; " + "width:calc(100%% - 12px); " + "height:100%%; " + "border:none; " + "padding:0px; " + "z-index:%POS; " + "}\n", + id, + z + 1 + ) != 0) + { + mssError(0, "HTEB", "Failed to write con1 CSS."); + goto end; + } - /** Script include to get functions **/ - htrAddScriptInclude(s, "/sys/js/htdrv_editbox.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_cursor.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0); + /*** Write CSS for the "more text this way" arrows. These are pinned + *** to the edges of the base layer, since the input above is out of + *** the normal flow. + ***/ + if (htrAddStylesheetItem_va(s, + "\t\t#eb%POSbase > img[data-side] { " + "position:absolute; " + "top:0px; " + "bottom:0px; " + "margin:auto 0px; " + "}\n" + "\t\t#eb%POSbase > img[data-side='left'] { left:0px; }\n" + "\t\t#eb%POSbase > img[data-side='right'] { right:0px; }\n", + id, id, id + ) != 0) + { + mssError(0, "HTEB", "Failed to write edge arrow CSS."); + goto end; + } - htrAddEventHandlerFunction(s, "document","MOUSEUP", "eb", "eb_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "eb", "eb_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER", "eb", "eb_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEOUT", "eb", "eb_mouseout"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "eb", "eb_mousemove"); - htrAddEventHandlerFunction(s, "document","PASTE", "eb", "eb_paste"); + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "eb%POSbase", id) != 0) goto end; + + /** Write JS globals. **/ + if (htrAddScriptGlobal(s, "eb_current", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "text_metric", "null", 0) != 0) goto end; + + /** Write JS script includes. **/ + if (htrAddScriptInclude(s, "/sys/js/ht_utils_cursor.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/htdrv_editbox.js", 0) != 0) goto end; + + /** Register JS event handlers. **/ + if (htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "eb", "eb_mousedown") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "eb", "eb_mousemove") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document","MOUSEOUT", "eb", "eb_mouseout") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document","MOUSEOVER", "eb", "eb_mouseover") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document","MOUSEUP", "eb", "eb_mouseup") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document","PASTE", "eb", "eb_paste") != 0) goto end; /** Script initialization call. **/ - htrAddScriptInit_va(s, " eb_init({layer:wgtrGetNodeRef(ns,'%STR&SYM'), c1:document.getElementById(\"eb%POScon1\"), form:\"%STR&JSSTR\", fieldname:\"%STR&JSSTR\", isReadOnly:%INT, mainBackground:\"%STR&JSSTR\", tooltip:\"%STR&JSSTR\", desc_fgcolor:\"%STR&JSSTR\", empty_desc:\"%STR&JSSTR\"});\n", - name, id, - form, fieldname, is_readonly, main_bg, - tooltip, descfg, descr); + if (htrAddScriptInit_va(s, + "\teb_init({ " + "layer:wgtrGetNodeRef(ns,'%STR&SYM'), " + "c1:document.getElementById('eb%POScon1'), " + "form:'%STR&JSSTR', " + "fieldname:'%STR&JSSTR', " + "isReadOnly:%INT, " + "mainBackground:'%STR&JSSTR', " + "tooltip:'%STR&JSSTR', " + "desc_fgcolor:'%STR&JSSTR', " + "empty_desc:'%STR&JSSTR', " + "});\n", + name, id, + form, fieldname, + is_readonly, + main_bg, tooltip, + descfg, descr + ) != 0) + { + mssError(0, "HTEB", "Failed to write JS init call."); + goto end; + } /** HTML body
element for the base layer. **/ - htrAddBodyItem_va(s, "
\n",id); + if (htrAddBodyItem_va(s, "
", id) != 0) + { + mssError(0, "HTEB", "Failed to write base HTML."); + goto end; + } /** Use CSS border for drawing **/ - if (is_raised) - htrAddStylesheetItem_va(s,"\t#eb%POSbase { border-style:solid; border-width:1px; border-color: white gray gray white; %STR }\n",id, main_bg); - else - htrAddStylesheetItem_va(s,"\t#eb%POSbase { border-style:solid; border-width:1px; border-color: gray white white gray; %STR }\n",id, main_bg); - if (h >= 0) - htrAddStylesheetItem_va(s,"\t#eb%POSbase { height:%POSpx; }\n\t#eb%POScon1 { height:%POSpx; }\n", id, h-2*box_offset, id, h-2*box_offset-2); + const char* border_colors = (is_raised) ? "white gray gray white" : "gray white white gray"; + if (htrAddStylesheetItem_va(s, + "\t\t#eb%POSbase { " + "border-style:solid; " + "border-width:1px; " + "border-color:%STR; " + "%[height:"ht_flex_format"; %]" + "%STR " + "}\n", + id, + border_colors, + (h >= 0), ht_flex_h(h, tree), + main_bg + ) != 0) + { + mssError(0, "HTEB", "Failed to write base border CSS."); + goto end; + } - //htrAddBodyItem_va(s, "
 
\n", w-2, h-2); - //htrAddBodyItem_va(s, "
\n",id); - htrAddBodyItem_va(s, "\n",id); + if (htrAddBodyItem_va(s, + "" + "" + "", + id, + (max_chars > 0), max_chars + ) != 0) + { + mssError(0, "HTEB", "Failed to write edge and input HTML."); + goto end; + } - /** Check for more sub-widgets **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+1); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto end; /** End the containing layer. **/ - htrAddBodyItem(s, "
\n"); + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTEB", "Failed to write HTML closing tag."); + goto end; + } - nmSysFree(tooltip); + /** Success. **/ + rval = 0; - return 0; + end: + if (rval != 0) + { + mssError(0, "HTEB", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + } + + /** Clean up. **/ + if (tooltip != NULL) nmSysFree(tooltip); + + return rval; } @@ -222,27 +341,6 @@ htebInitialize() strcpy(drv->WidgetName,"editbox"); drv->Render = htebRender; - /** Events **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - htrAddEvent(drv,"DataChange"); - htrAddEvent(drv,"GetFocus"); - htrAddEvent(drv,"LoseFocus"); - - /** Add a 'set value' action **/ - htrAddAction(drv,"SetValue"); - htrAddParam(drv,"SetValue","Value",DATA_T_STRING); /* value to set it to */ - htrAddParam(drv,"SetValue","Trigger",DATA_T_INTEGER); /* whether to trigger the Modified event */ - - /** Value-modified event **/ - htrAddEvent(drv,"Modified"); - htrAddParam(drv,"Modified","NewValue",DATA_T_STRING); - htrAddParam(drv,"Modified","OldValue",DATA_T_STRING); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_execmethod.c b/centrallix/htmlgen/htdrv_execmethod.c index 31d930902..9769421ae 100644 --- a/centrallix/htmlgen/htdrv_execmethod.c +++ b/centrallix/htmlgen/htdrv_execmethod.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -43,6 +31,14 @@ /* general purpose osml widget. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /*** htexRender - generate the HTML code for the timer nonvisual widget. ***/ @@ -55,10 +51,11 @@ htexRender(pHtSession s, pWgtrNode tree, int z) char* methodname = NULL; char* methodparam = NULL; - if(!s->Capabilities.Dom0NS) + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTTEX","Netscape DOM support required"); - return -1; + mssError(1, "HTEX", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } /** Get params. **/ @@ -71,15 +68,34 @@ htexRender(pHtSession s, pWgtrNode tree, int z) strtcpy(name, ptr, sizeof(name)); /** Script initialization call. **/ - htrAddScriptInit_va(s, " ex_init({node:wgtrGetNodeRef(ns,\"%STR&SYM\"), objname:'%STR&SYM', methname:'%STR&SYM', methparam:'%STR&JSSTR'});\n", - name, objname, methodname, methodparam); + if (htrAddScriptInit_va(s, + "\tex_init({ " + "node:wgtrGetNodeRef(ns, '%STR&SYM'), " + "objname:'%STR&SYM', " + "methname:'%STR&SYM', " + "methparam:'%STR&JSSTR', " + "});\n", + name, objname, methodname, methodparam + ) != 0) + { + mssError(1, "HTEX", "Failed to write JS init call."); + goto err; + } - /** Check for objects within the exec method object. **/ - htrRenderSubwidgets(s, tree, z+2); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 2) != 0) goto err; - htrAddScriptInclude(s,"/sys/js/htdrv_execmethod.js",0); + if (htrAddScriptInclude(s, "/sys/js/htdrv_execmethod.js", 0) != 0) goto err; - return 0; + /** Success. **/ + return 0; + + err: + mssError(0, "HTEX", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); + return -1; } @@ -99,12 +115,6 @@ htexInitialize() strcpy(drv->WidgetName,"execmethod"); drv->Render = htexRender; - /** Add a 'executemethod' action **/ - htrAddAction(drv,"ExecuteMethod"); - htrAddParam(drv,"ExecuteMethod","Objname",DATA_T_STRING); - htrAddParam(drv,"ExecuteMethod","Method",DATA_T_STRING); - htrAddParam(drv,"ExecuteMethod","Parameter",DATA_T_STRING); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_fileupload.c b/centrallix/htmlgen/htdrv_fileupload.c index a3ed1a5c2..296df17ad 100755 --- a/centrallix/htmlgen/htdrv_fileupload.c +++ b/centrallix/htmlgen/htdrv_fileupload.c @@ -1,21 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,6 +28,15 @@ /* Description: HTML Widget driver for a file uploader. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + + /** globals **/ static struct { @@ -55,20 +51,27 @@ htfuRender(pHtSession s, pWgtrNode tree, int z) { char* ptr; char name[64]; - int id, i; int multiselect; char target[512]; char fieldname[HT_FIELDNAME_SIZE]; char form[64]; /** Get an id for this. **/ - id = (HTFU.idcnt++); + const int id = (HTFU.idcnt++); - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(name,ptr,sizeof(name)); - - if (wgtrGetPropertyValue(tree,"target",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(target,ptr,sizeof(target)); + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&ptr)) != 0) + { + mssError(1, "HTFU", "Fileupload widget must have a 'name' property."); + goto err; + } + strtcpy(name, ptr, sizeof(name)); + + if (wgtrGetPropertyValue(tree, "target", DATA_T_STRING, POD(&ptr)) != 0) + { + mssError(1, "HTFU", "Fileupload widget must have a 'target' property."); + goto err; + } + strtcpy(target, ptr, sizeof(target)); multiselect = htrGetBoolean(tree, "multiselect", 0); //default = 0; @@ -82,26 +85,72 @@ htfuRender(pHtSession s, pWgtrNode tree, int z) else fieldname[0]='\0'; - /** Write named global **/ - htrAddWgtrObjLinkage_va(s, tree, "fu%POSbase",id); + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "fu%POSbase", id) != 0) + { + mssError(0, "HTFU", "Failed to add object linkage."); + goto err; + } - htrAddEventHandlerFunction(s, "document","CHANGE", "fu", "fu_change"); + if (htrAddEventHandlerFunction(s, "document", "CHANGE", "fu", "fu_change") != 0) goto err; /** Include the javascript code for the file uploader **/ - htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0); - htrAddScriptInclude(s, "/sys/js/htdrv_fileupload.js", 0); + if (htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_fileupload.js", 0) != 0) goto err; - htrAddScriptInit_va(s, " fu_init({layer:wgtrGetNodeRef(ns,'%STR&SYM'), pane:document.getElementById(\"fu%POSform\"), input:document.getElementById(\"fu%POSinput\"), iframe:document.getElementById(\"fu%POSiframe\"), target:\"%STR&JSSTR\"});\n", name, id, id, id, target); + if (htrAddScriptInit_va(s, + "\tfu_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "pane:document.getElementById('fu%POSform'), " + "input:document.getElementById('fu%POSinput'), " + "iframe:document.getElementById('fu%POSiframe'), " + "target:'%STR&JSSTR', " + "});\n", + name, id, id, id, target + ) != 0) + { + mssError(0, "HTFU", "Failed to write JS init call."); + goto err; + } /** style header items **/ - htrAddStylesheetItem_va(s,"#fu%POSbase { POSITION:absolute; VISIBILITY:hidden; }\n", id); - htrAddBodyItem_va(s,"
", id, id, id, id, id, id, id, multiselect?"MULTIPLE":""); + if (htrAddStylesheetItem_va(s, + "\t\t#fu%POSbase { " + "position:absolute; " + "visibility:hidden; " + "}\n", + id + ) != 0) + { + mssError(0, "HTFU", "Failed to write base CSS."); + goto err; + } + if (htrAddBodyItem_va(s, + "
" + "" + "" + "
", + id, id, id, + id, id, + id, id, + (multiselect) + ) != 0) + { + mssError(0, "HTFU", "Failed to write base HTML."); + goto err; + } /** Check for more sub-widgets **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z); + if (htrRenderSubwidgets(s, tree, z) != 0) goto err; return 0; + + err: + mssError(0, "HTFU", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; }//end htfuRender @@ -121,19 +170,6 @@ htfuInitialize() strcpy(drv->Name,"DHTML File Upload Driver"); strcpy(drv->WidgetName,"fileupload"); drv->Render = htfuRender; - - /** Add actions **/ - htrAddAction(drv,"Reset"); - htrAddAction(drv,"Prompt"); - htrAddAction(drv,"Submit"); - - /** Add event **/ - htrAddEvent(drv,"DataChange"); - htrAddParam(drv,"DataChange","NewValue",DATA_T_STRING); - htrAddParam(drv,"DataChange","OldValue",DATA_T_STRING); - - htrAddEvent(drv,"Success"); - htrAddParam(drv,"Success","data",DATA_T_ARRAY); /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_form.c b/centrallix/htmlgen/htdrv_form.c index e3cc04932..a943bfaab 100644 --- a/centrallix/htmlgen/htdrv_form.c +++ b/centrallix/htmlgen/htdrv_form.c @@ -1,21 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -43,6 +30,15 @@ /* See centrallix-sysdoc/HTFormsInterface.md for more information. */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /*** htformRender - generate the HTML code for the form 'glue' ***/ @@ -210,42 +206,73 @@ htformRender(pHtSession s, pWgtrNode tree, int z) strtcpy(name,ptr,sizeof(name)); /** create our instance variable **/ - htrAddWgtrCtrLinkage(s, tree, "_parentctr"); + if (htrAddWgtrCtrLinkage(s, tree, "_parentctr") != 0) + { + mssError(0, "HTFORM", "Failed to add container linkage."); + goto err; + } /** Script include to add functions **/ - htrAddScriptInclude(s, "/sys/js/htdrv_form.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0); + if (htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0)) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_form.js", 0)) goto err; /** Write out the init line for this instance of the form ** the name of this instance was defined to be global up above ** and fm_current is defined in htdrv_page.c **/ - htrAddScriptInit_va(s," form_init(wgtrGetNodeRef(ns,\"%STR&SYM\"), {aq:%INT, an:%INT, am:%INT, av:%INT, and:%INT, ad:%INT, cd:%INT, cdis:%INT, amrg:%INT, me:%INT, name:'%STR&SYM', _3b:%[wgtrGetNodeRef(ns,\"%STR&SYM\")%]%[null%], ro:%INT, ao:%INT, af:%INT, osrc:%['%STR&SYM'%]%[null%], tro:%INT, em:%INT, nf:%['%STR&SYM'%]%[null%], nfw:%['%STR&SYM'%]%[null%], pf:%['%STR&SYM'%]%[null%], pfw:%['%STR&SYM'%]%[null%], il:'%STR&JSSTR'});\n", - name,allowquery,allownew,allowmodify,allowview,allownodata,allowdelete,confirmdelete, confirmdiscard, allowmerge, - multienter,name, - strcmp(_3bconfirmwindow,"null") != 0, _3bconfirmwindow, strcmp(_3bconfirmwindow,"null") == 0, - readonly,allowobscure,autofocus, - *osrc != '\0', osrc, *osrc == '\0', - tro, enter_mode, - *link_next != '\0', link_next, *link_next == '\0', - *link_next_within != '\0', link_next_within, *link_next_within == '\0', - *link_prev != '\0', link_prev, *link_prev == '\0', - *link_prev_within != '\0', link_prev_within, *link_prev_within == '\0', - interlock_with - ); - htrAddScriptInit_va(s," wgtrGetNodeRef(ns,\"%STR&SYM\").ChangeMode('NoData');\n",name); - - /** Check for and render all subobjects. **/ - /** non-visual, don't consume a z level **/ - for (i=0;iChildren));i++) + const int null_confirm_window = (strcmp(_3bconfirmwindow, "null") == 0); + const int no_osrc = (osrc == NULL || osrc[0] == '\0'); + const int no_link_next = (link_next == NULL || link_next[0] == '\0'); + const int no_link_next_within = (link_next_within == NULL || link_next_within[0] == '\0'); + const int no_link_prev = (link_prev == NULL || link_prev[0] == '\0'); + const int no_link_prev_within = (link_prev_within == NULL || link_prev_within[0] == '\0'); + if (htrAddScriptInit_va(s, + "\t{ " + "const node = wgtrGetNodeRef(ns, '%STR&SYM'); " + "form_init(node, { " + "aq:%INT, an:%INT, am:%INT, av:%INT, and:%INT, ad:%INT, " + "cd:%INT, cdis:%INT, amrg:%INT, me:%INT, name:'%STR&SYM', " + "_3b:%[wgtrGetNodeRef(ns, '%STR&SYM')%]%[null%], " + "ro:%INT, ao:%INT, af:%INT, " + "osrc:%['%STR&SYM'%]%[null%], " + "tro:%INT, em:%INT, " + "nf:%['%STR&SYM'%]%[null%], " + "nfw:%['%STR&SYM'%]%[null%], " + "pf:%['%STR&SYM'%]%[null%], " + "pfw:%['%STR&SYM'%]%[null%], " + "il:'%STR&JSSTR', " + "});" + "node.ChangeMode('NoData');" + "}\n", + name, + allowquery, allownew, allowmodify, allowview, allownodata, allowdelete, + confirmdelete, confirmdiscard, allowmerge, multienter, name, + (!null_confirm_window), _3bconfirmwindow, (null_confirm_window), + readonly, allowobscure, autofocus, + (!no_osrc), osrc, (no_osrc), + tro, enter_mode, + (!no_link_next), link_next, (no_link_next), + (!no_link_next_within), link_next_within, (no_link_next_within), + (!no_link_prev), link_prev, (no_link_prev), + (!no_link_prev_within), link_prev_within, (no_link_prev_within), + interlock_with + ) != 0) { - if (strcmp(tree->Type, "widget/connector") == 0) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z); - else - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z); + mssError(0, "HTFORM", "Failed to write JS init call."); + goto err; } - - return 0; + + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z) != 0) goto err; + + return 0; + + err: + mssError(0, "HTFORM", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); + return -1; } @@ -265,30 +292,6 @@ htformInitialize() strcpy(drv->WidgetName,"form"); drv->Render = htformRender; - /** Add our actions **/ - htrAddAction(drv,"Clear"); - htrAddAction(drv,"Delete"); - htrAddAction(drv,"Discard"); - htrAddAction(drv,"Edit"); - htrAddAction(drv,"First"); - htrAddAction(drv,"Last"); - htrAddAction(drv,"New"); - htrAddAction(drv,"Next"); - htrAddAction(drv,"Prev"); - htrAddAction(drv,"Query"); - htrAddAction(drv,"QueryExec"); - htrAddAction(drv,"QueryToggle"); - htrAddAction(drv,"Save"); - - /* these don't really do much, since the form doesn't have a layer, so nothing can find it... */ - htrAddEvent(drv,"StatusChange"); - htrAddEvent(drv,"DataChange"); - htrAddEvent(drv,"NoData"); - htrAddEvent(drv,"View"); - htrAddEvent(drv,"Modify"); - htrAddEvent(drv,"Query"); - htrAddEvent(drv,"QueryExec"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_formbar.c b/centrallix/htmlgen/htdrv_formbar.c deleted file mode 100644 index 9c326dcbc..000000000 --- a/centrallix/htmlgen/htdrv_formbar.c +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - -/************************************************************************/ -/* Centrallix Application Server System */ -/* Centrallix Core */ -/* */ -/* Copyright (C) 2000-2001 LightSys Technology Services, Inc. */ -/* */ -/* This program 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 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program 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 this program; if not, write to the Free Software */ -/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ -/* 02111-1307 USA */ -/* */ -/* A copy of the GNU General Public License has been included in this */ -/* distribution in the file "COPYING". */ -/* */ -/* Module: htdrv_formstatus.c */ -/* Author: Matt McGill (MJM) */ -/* Creation: July 19, 2004 */ -/* Description: HTML Widget driver for a form bar, basically a tech */ -/* test for widgets adding subwidgets on the fly. */ -/************************************************************************/ - - - -/* - htfbRender - generate the HTML code for the page. -*/ -int htfbRender(pHtSession s, pWgtrNode tree, int z) { - int i; - - if(!s->Capabilities.Dom0NS && !(s->Capabilities.Dom1HTML && s->Capabilities.CSS1)) - { - mssError(1,"HTFS","Netscape DOM or W3C DOM1 HTML and CSS1 support required"); - return -1; - } - - htrAddWgtrCtrLinkage(s, tree, "_parentctr"); - - /** mark this node as not being associated with a DHTML object **/ - tree->RenderFlags |= HT_WGTF_NOOBJECT; - - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+2); - - return 0; -} - - -/* - htfsInitialize - register with the ht_render module. -*/ -int htfbInitialize() { - pHtDriver drv; - /*pHtEventAction action; - pHtParam param;*/ - - /** Allocate the driver **/ - drv = htrAllocDriver(); - if (!drv) return -1; - - /** Fill in the structure. **/ - strcpy(drv->Name,"DHTML Form Bar Driver"); - strcpy(drv->WidgetName,"formbar"); - drv->Render = htfbRender; - -/* - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); -*/ - /** Register. **/ - htrRegisterDriver(drv); - - htrAddSupport(drv, "dhtml"); - - return 0; -} diff --git a/centrallix/htmlgen/htdrv_formstatus.c b/centrallix/htmlgen/htdrv_formstatus.c index 33258e3c1..88432b640 100644 --- a/centrallix/htmlgen/htdrv_formstatus.c +++ b/centrallix/htmlgen/htdrv_formstatus.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 2000-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 2000-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,14 @@ /* Description: HTML Widget driver for a form status */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct { @@ -52,21 +48,21 @@ static struct { */ int htfsRender(pHtSession s, pWgtrNode tree, int z) { int x=-1,y=-1; - int id; char name[64]; char form[64]; char* ptr; char* style; int w; - if(!s->Capabilities.Dom0NS && !(s->Capabilities.Dom1HTML && s->Capabilities.CSS1)) - { - mssError(1,"HTFS","Netscape DOM or W3C DOM1 HTML and CSS1 support required"); - return -1; - } - /** Get an id for this. **/ - id = (HTFS.idcnt++); + const int id = (HTFS.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) + { + mssError(1, "HTFS", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; + } /** Get x,y of this object **/ if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; @@ -74,46 +70,94 @@ int htfsRender(pHtSession s, pWgtrNode tree, int z) { /** Get optional style **/ if (wgtrGetPropertyValue(tree,"style",DATA_T_STRING,POD(&style)) != 0) style = ""; - if (!strcmp(style,"large") || !strcmp(style,"largeflat")) - w = 90; - else - w = 13; + w = (strcmp(style, "large") == 0 || strcmp(style, "largeflat") == 0) ? 90 : 13; /** Write named global **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto err; strtcpy(name,ptr,sizeof(name)); if (wgtrGetPropertyValue(tree,"form",DATA_T_STRING,POD(&ptr)) != 0) form[0] = '\0'; else strtcpy(form,ptr,sizeof(form)); - htrAddWgtrObjLinkage_va(s, tree, "fs%POSmain", id); - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#fs%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; HEIGHT:13px; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z); + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "fs%POSmain", id) != 0) + { + mssError(0, "HTFS", "Failed to add object linkage."); + goto err; + } - htrAddScriptInclude(s, "/sys/js/htdrv_formstatus.js", 0); + /** Ok, write the style header items. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#fs%POSmain { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:%POSpx; " + "height:13px; " + "z-index:%POS; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + w, + z + ) != 0) + { + mssError(0, "HTFS", "Failed to write main CSS."); + goto err; + } + + if (htrAddScriptInclude(s, "/sys/js/htdrv_formstatus.js", 0) != 0) goto err; /** Script initialization call. **/ - htrAddScriptInit_va(s," fs_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"),form:\"%STR&JSSTR\",style:\"%STR&JSSTR\"});\n", - name, form, style); + if (htrAddScriptInit_va(s, + "\tfs_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "form:'%STR&JSSTR', " + "style:'%STR&JSSTR', " + "});\n", + name, form, style + ) != 0) + { + mssError(0, "HTFS", "Failed to write JS init call."); + goto err; + } /** HTML body
element for the layers. **/ - if (!strcmp(style,"large")) - htrAddBodyItem_va(s,"
\n", id); - else if (!strcmp(style,"largeflat")) - htrAddBodyItem_va(s,"
\n", id); - else - htrAddBodyItem_va(s,"
\n", id); - - htrAddEventHandlerFunction(s,"document","MOUSEDOWN","fs","fs_mousedown"); - htrAddEventHandlerFunction(s,"document","MOUSEUP", "fs","fs_mouseup"); - htrAddEventHandlerFunction(s,"document","MOUSEOVER","fs","fs_mouseover"); - htrAddEventHandlerFunction(s,"document","MOUSEOUT", "fs","fs_mouseout"); - htrAddEventHandlerFunction(s,"document","MOUSEMOVE","fs","fs_mousemove"); - - htrRenderSubwidgets(s, tree, z+2); - - return 0; + const char* type = ""; + const char* ext = "gif"; + if (strcmp(style, "large") == 0) { type = "L"; ext = "png"; } + else if (strcmp(style, "largeflat") == 0) { type = "LF"; ext = "png"; } + if (htrAddBodyItem_va(s, + "
\n", + id, type, ext + ) != 0) + { + mssError(0, "HTFS", "Failed to write HTML for the status image."); + goto err; + } + + /** Register JS event handler functions. **/ + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN","fs", "fs_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE","fs", "fs_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "fs", "fs_mouseout") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER","fs", "fs_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "fs", "fs_mouseup") != 0) goto err; + + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 2) != 0) goto err; + + /** Success. **/ + return 0; + + err: + mssError(0, "HTFS", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } @@ -122,8 +166,6 @@ int htfsRender(pHtSession s, pWgtrNode tree, int z) { */ int htfsInitialize() { pHtDriver drv; - /*pHtEventAction action; - pHtParam param;*/ /** Allocate the driver **/ drv = htrAllocDriver(); @@ -134,13 +176,6 @@ int htfsInitialize() { strcpy(drv->WidgetName,"formstatus"); drv->Render = htfsRender; - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_frameset.c b/centrallix/htmlgen/htdrv_frameset.c index 972f6561c..0689948be 100644 --- a/centrallix/htmlgen/htdrv_frameset.c +++ b/centrallix/htmlgen/htdrv_frameset.c @@ -1,16 +1,3 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "cxlib/qprintf.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ @@ -42,6 +29,15 @@ /* widget/page item at the top level. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/qprintf.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "ht_render.h" +#include "wgtr.h" /*** htsetRender - generate the HTML code for the page. @@ -58,7 +54,7 @@ htsetRender(pHtSession s, pWgtrNode tree, int z) /** Check for a title. **/ if (wgtrGetPropertyValue(tree,"title",DATA_T_STRING,POD(&ptr)) == 0) { - htrAddHeaderItem_va(s," %STR&HTE\n",ptr); + htrAddHeaderItem_va(s, "\t%STR&HTE\n", ptr); } /** Loop through the frames (widget/page items) for geometry data **/ diff --git a/centrallix/htmlgen/htdrv_hints.c b/centrallix/htmlgen/htdrv_hints.c index 35118bb51..e6bd1b54d 100644 --- a/centrallix/htmlgen/htdrv_hints.c +++ b/centrallix/htmlgen/htdrv_hints.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/xstring.h" -#include "cxlib/mtsession.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2004 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -42,6 +30,15 @@ /* with a form element widget */ /************************************************************************/ +#include + +#include "cxlib/mtsession.h" +#include "cxlib/xstring.h" +#include "hints.h" +#include "ht_render.h" +#include "obj.h" +#include "wgtr.h" + /*** hthintRender - generate the HTML code for the page. ***/ @@ -62,8 +59,10 @@ hthintRender(pHtSession s, pWgtrNode tree, int z) /** Serialize the hints data and add the script init for it **/ xsInit(&xs); hntEncodeHints(hints, &xs); - htrAddScriptInit_va(s, " cx_set_hints(wgtrGetParent(wgtrGetNodeRef(ns,\"%STR&SYM\")), '%STR&JSSTR', 'app');\n", - wgtrGetName(tree), xs.String); + htrAddScriptInit_va(s, + "\tcx_set_hints(wgtrGetParent(wgtrGetNodeRef(ns, '%STR&SYM')), '%STR&JSSTR', 'app');\n", + wgtrGetName(tree), xs.String + ); xsDeInit(&xs); /** mark this node as not being associated with a DHTML object **/ diff --git a/centrallix/htmlgen/htdrv_html.c b/centrallix/htmlgen/htdrv_html.c index 9b899802a..623a93a35 100644 --- a/centrallix/htmlgen/htdrv_html.c +++ b/centrallix/htmlgen/htdrv_html.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,6 +29,20 @@ /* immediate string or pulled from an ObjectSystem source */ /************************************************************************/ +#include +#include +#include +#include + +#include "cxlib/check.h" +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/newmalloc.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "obj.h" +#include "wgtr.h" + /** globals **/ static struct @@ -56,103 +58,159 @@ int hthtmlRender(pHtSession s, pWgtrNode tree, int z) { char* ptr; - char name[64]; - char sbuf[320]; - char src[128] = ""; - int x=-1,y=-1,w,h; - int id,cnt, i; - int mode = 0; - pObject content_obj; - - if(!s->Capabilities.Dom0NS && !(s->Capabilities.Dom1HTML && s->Capabilities.CSS1)) + + /** Get an id for this. **/ + const int id = (HTHTML.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTHTML","NS4 or W3C DOM support required"); - return -1; + mssError(1, "HTHTML", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTHTML.idcnt++); - - /** Get x,y,w,h of this object **/ + /** Get x, y, w, & h. **/ + int x = -1, y = -1, w, h; if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=-1; if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=-1; if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { mssError(1,"HTHTML","HTML widget must have a 'width' property"); - return -1; + goto err; } if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) h = -1; + if (tree->Flags & WGTR_F_AUTOHEIGHT) h = -1; /** let htdrv_html.js fit the document **/ - /** Get source html objectsystem entry. **/ + /** Get the HTML source path. **/ + char src[256] = ""; if (wgtrGetPropertyValue(tree,"source",DATA_T_STRING,POD(&ptr)) == 0) { - strtcpy(src,ptr,sizeof(src)); + if (strtcpy(src, ptr, sizeof(src)) < 0) + { + /** Data truncated: Send warning. **/ + fprintf(stderr, "Warning! Source path truncated.\n"); + } } /** Check for a 'mode' - dynamic or static. Default is static. **/ + int mode = 0; if (wgtrGetPropertyValue(tree,"mode",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"dynamic")) mode = 1; /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + char name[64]; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto err; strtcpy(name,ptr,sizeof(name)); /** Ok, write the style header items. **/ if (mode == 1) { /** Only give x and y if supplied. **/ - if (x < 0 || y < 0) - { - htrAddStylesheetItem_va(s,"\t#ht%POSpane { POSITION:relative; VISIBILITY:inherit; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,w,z); - htrAddStylesheetItem_va(s,"\t#ht%POSpane2 { POSITION:relative; VISIBILITY:hidden; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,w,z); - htrAddStylesheetItem_va(s,"\t#ht%POSfader { POSITION:relative; VISIBILITY:hidden; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,w,z+1); - } - else - { - htrAddStylesheetItem_va(s,"\t#ht%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z); - htrAddStylesheetItem_va(s,"\t#ht%POSpane2 { POSITION:absolute; VISIBILITY:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z); - htrAddStylesheetItem_va(s,"\t#ht%POSfader { POSITION:absolute; VISIBILITY:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,z+1); - } - - if (s->Capabilities.CSS1) - { - htrAddStylesheetItem_va(s,"\t#ht%POSpane { overflow:hidden; }\n",id); - htrAddStylesheetItem_va(s,"\t#ht%POSpane2 { overflow:hidden; }\n",id); - htrAddStylesheetItem_va(s,"\t#ht%POSfader { overflow:hidden; }\n",id); - htrAddStylesheetItem_va(s,"\t#ht%POSloader { overflow:hidden; visibility:hidden; position:absolute; top:0px; left:0px; width:0px; height:0px; }\n", id); - } - - /** Write named global **/ - htrAddWgtrObjLinkage_va(s, tree, "ht%POSpane",id); - - htrAddScriptGlobal(s, "ht_fadeobj", "null", 0); - - htrAddScriptInclude(s, "/sys/js/htdrv_html.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); + if (htrAddStylesheetItem_va(s, + "\t\t#ht%POSpane, #ht%POSpane2, #ht%POSfader { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "%[left:"ht_flex_format"; " + "top:"ht_flex_format"; %]" + "width:"ht_flex_format"; " + "z-index:%POS; " + "}\n" + "\t\t#ht%POSpane2, #ht%POSfader { visibility:hidden; }\n", + id, id, id, + (x >= 0 && y >= 0), + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + z, + id, id + ) != 0) + { + mssError(0, "HTHTML", "Failed to add CSS."); + goto err; + } + if (htrAddStylesheetItem_va(s, + "\t\t#ht%POSloader { " + "position:absolute; " + "visibility:hidden; " + "overflow:hidden; " + "top:0px; " + "left:0px; " + "width:0px; " + "height:0px; " + "}\n", + id + ) != 0) + { + mssError(0, "HTHTML", "Failed to add loader CSS."); + goto err; + } + + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "ht%POSpane", id) != 0) goto err; + /** Declare globals. **/ + if (htrAddScriptGlobal(s, "ht_fadeobj", "null", 0) != 0) goto err; + + /** Include JS dependencies. **/ + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_html.js", 0) != 0) goto err; + /** Event handler for click-on-link. **/ - htrAddEventHandlerFunction(s, "document","CLICK","ht","ht_click"); - htrAddEventHandlerFunction(s,"document","MOUSEOVER","ht","ht_mouseover"); - htrAddEventHandlerFunction(s,"document","MOUSEOUT", "ht", "ht_mouseout"); - htrAddEventHandlerFunction(s,"document","MOUSEMOVE","ht", "ht_mousemove"); - htrAddEventHandlerFunction(s,"document","MOUSEDOWN","ht", "ht_mousedown"); - htrAddEventHandlerFunction(s,"document","MOUSEUP", "ht", "ht_mouseup"); - - /** Script initialization call. **/ - if (s->Capabilities.Dom0NS) - htrAddScriptInit_va(s," ht_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), layer2:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")),\"ht%POSpane2\"), faderLayer:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")),\"ht%POSfader\"), source:\"%STR&JSSTR\", width:%INT, height:%INT, loader:null});\n", - name, name, id, name, id, - src, w,h); - else - htrAddScriptInit_va(s," ht_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), layer2:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")),\"ht%POSpane2\"), faderLayer:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")),\"ht%POSfader\"), source:\"%STR&JSSTR\", width:%INT, height:%INT, loader:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")), \"ht%POSloader\")});\n", - name, name, id, name, id, - src, w,h, name, id); + if (htrAddEventHandlerFunction(s, "document", "CLICK", "ht", "ht_click") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "ht", "ht_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "ht", "ht_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "ht", "ht_mouseout") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "ht", "ht_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "ht", "ht_mouseup") != 0) goto err; + + /** Write script initialization call in its own scope. **/ + if (htrAddScriptInit_va(s, "\t{ " + "const layer = wgtrGetNodeRef(ns, '%STR&SYM'); " + "const layer_container = wgtrGetParentContainer(layer); " + "ht_init({" + "layer, " + "layer2:htr_subel(layer_container, 'ht%POSpane2'), " + "faderLayer:htr_subel(layer_container, 'ht%POSfader'), " + "source:'%STR&JSSTR', " + "width:%INT, " + "height:%INT, " + "%[loader:htr_subel(layer_container, 'ht%POSloader'), %]" + "}); }\n", + name, id, id, src, w, h, + (s->Capabilities.Dom1HTML), id + ) != 0) + { + mssError(0, "HTHTML", "Failed to write JS init call."); + goto err; + } /** HTML body
element for the layer. **/ - htrAddBodyItem_va(s,"
",id); - htrAddBodyItemLayer_va(s, 0, "ht%POSpane2", id, NULL, ""); - if (!s->Capabilities.Dom0NS) - htrAddBodyItemLayer_va(s, HTR_LAYER_F_DYNAMIC, "ht%POSloader", id, NULL, ""); - htrAddBodyItemLayerStart(s, 0, "ht%POSpane", id, NULL); + if (htrAddBodyItem_va(s, + "
", + id + ) != 0) + { + mssError(0, "HTHTML", "Failed to write HTML background fader."); + goto err; + } + if (htrAddBodyItemLayer_va(s, 0, "ht%POSpane2", id, NULL, "") != 0) + { + mssError(0, "HTHTML", "Failed to write HTML opening tag."); + goto err; + } + if (htrAddBodyItemLayer_va(s, HTR_LAYER_F_DYNAMIC, "ht%POSloader", id, NULL, "") != 0) + { + mssError(0, "HTHTML", "Failed to write HTML loader."); + goto err; + } + if (htrAddBodyItemLayerStart(s, 0, "ht%POSpane", id, NULL) != 0) + { + mssError(0, "HTHTML", "Failed to write HTML start tag."); + goto err; + } } else { @@ -162,44 +220,145 @@ hthtmlRender(pHtSession s, pWgtrNode tree, int z) /** If prefix text given, put it. **/ if (wgtrGetPropertyValue(tree, "prologue", DATA_T_STRING,POD(&ptr)) == 0) { - htrAddBodyItem(s, ptr); + if (htrAddBodyItem(s, ptr) != 0) + { + mssError(0, "HTHTML", "Failed to write prologue HTML."); + goto err; + } } /** If full text given, put it. **/ if (wgtrGetPropertyValue(tree, "content", DATA_T_STRING,POD(&ptr)) == 0) { - htrAddBodyItem(s, ptr); + if (htrAddBodyItem(s, ptr) != 0) + { + mssError(0, "HTHTML", "Failed to write provided HTML content."); + goto err; + } } /** If source is an objectsystem entry... **/ - if (src[0] && strncmp(src,"http:",5) && strncmp(src,"debug:",6)) + if (src[0] && strncmp(src, "http:", 5) != 0 && strncmp(src, "debug:", 6) != 0) { - content_obj = objOpen(s->ObjSession,src,O_RDONLY,0600,"text/html"); - if (content_obj) - { - while((cnt = objRead(content_obj, sbuf, 159,0,0)) > 0) - { - sbuf[cnt]=0; - htrAddBodyItem(s, sbuf); - } - objClose(content_obj); - } + pObject content_obj = NULL; + char* page_buf = NULL; + bool successful = false; + + /** Open the content object. **/ + content_obj = objOpen(s->ObjSession, src, O_RDONLY, 0600, "text/html"); + if (content_obj == NULL) + { + mssError(0, "HTHTML", "Failed to open content object."); + goto end_reading; + } + + /** Get object info. **/ + pObjectInfo obj_info = objInfo(content_obj); + if (obj_info == NULL) + { + mssError(0, "HTHTML", "Failed to get content object info."); + goto end_reading; + } + + /** Inspect object info. **/ + if (obj_info->Flags & OBJ_INFO_F_CANT_HAVE_CONTENT) + { + mssError(1, "HTHTML", "FAIL: Provided object source cannot have content!"); + goto end_reading; + } + if (obj_info->Flags & OBJ_INFO_F_NO_CONTENT) + { + /** Nothing to read. **/ + successful = true; + goto end_reading; + } + + /** Allocate a buffer for reading HTML content. **/ + const size_t page_buf_len = BUFSIZ; + page_buf = checkPtr(nmSysMalloc(page_buf_len * sizeof(char))); + if (page_buf == NULL) goto end_reading; + + /* read content until we run out.*/ + int n_chars_read = 0; + while (1) + { + /** Read some data. **/ + const int max_read = page_buf_len - 1lu; + n_chars_read = objRead(content_obj, page_buf, max_read, 0, 0); + + /** Edge cases. **/ + if (n_chars_read == 0) break; /* Done! */ + if (n_chars_read < 0) + { + char error_code[32] = {'\0'}; + if (n_chars_read != -1) + snprintf(error_code, sizeof(error_code), " (error code: %d)", n_chars_read); + mssError(0, "HTHTML", "Failed to read from content object%s.", error_code); + goto end_reading; + } + if (n_chars_read > max_read) + { + mssError(1, "HTHTML", + "Driver read too many characters (%d/%d).", + n_chars_read, max_read + ); + goto end_reading; + } + + /** Write the data to the page. **/ + page_buf[n_chars_read] = '\0'; + if (htrAddBodyItem(s, page_buf) != 0) + { + mssError(0, "HTHTML", "Failed to write HTML chunk: \"%s\"", page_buf); + goto end_reading; + } + } + + /** Success. **/ + successful = true; + + end_reading: + /** Clean up. **/ + if (content_obj != NULL) objClose(content_obj); + if (page_buf != NULL) nmSysFree(page_buf); + + /** Handle failure. **/ + if (!successful) + { + mssError(0, "HTHTML", "Failed to write HTML from object source: \"%s\"", src); + goto err; + } } /** If post text given, put it. **/ if (wgtrGetPropertyValue(tree, "epilogue", DATA_T_STRING, POD(&ptr)) == 0) { - htrAddBodyItem(s, ptr); + if (htrAddBodyItem(s, ptr) != 0) + { + mssError(0, "HTHTML", "Failed to write epilogue HTML."); + goto err; + } } - /** render subwidgets **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+3); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 3) != 0) goto err; /** End the containing layer. **/ - if (mode == 1) htrAddBodyItemLayerEnd(s, 0); - - return 0; + if (mode == 1 && htrAddBodyItemLayerEnd(s, 0) != 0) + { + mssError(0, "HTHTML", "Failed to add HTML end."); + goto err; + } + + /** Success. **/ + return 0; + + err: + mssError(0, "HTHTML", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } @@ -219,18 +378,6 @@ hthtmlInitialize() strcpy(drv->WidgetName,"html"); drv->Render = hthtmlRender; - htrAddEvent(drv, "MouseUp"); - htrAddEvent(drv, "MouseDown"); - htrAddEvent(drv, "MouseOver"); - htrAddEvent(drv, "MouseOut"); - htrAddEvent(drv, "MouseMove"); - - /** Add the 'load page' action **/ - htrAddAction(drv,"LoadPage"); - htrAddParam(drv,"LoadPage","Mode",DATA_T_STRING); - htrAddParam(drv,"LoadPage","Source",DATA_T_STRING); - htrAddParam(drv,"LoadPage","Transition",DATA_T_STRING); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_image.c b/centrallix/htmlgen/htdrv_image.c index 7ea3b9d47..b2dda1db3 100644 --- a/centrallix/htmlgen/htdrv_image.c +++ b/centrallix/htmlgen/htdrv_image.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2017 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,15 @@ /* Description: HTML Widget driver for an image. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/newmalloc.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -56,10 +53,23 @@ htimgSetup(pHtSession s) { /** Global style code for all image widget "img" tags **/ - htrAddStylesheetItem(s, " img.wimage { display:block; position:relative; left:0px; top:0px; }\n"); + htrAddStylesheetItem(s, + "\t\timg.wimage { " + "display:block; " + "position:relative; " + "left:0px; " + "top:0px; " + "}\n" + ); /** Global style code for all image widget "div" containers **/ - htrAddStylesheetItem(s, " div.wimage { visibility:inherit; position:absolute; overflow:hidden; }\n"); + htrAddStylesheetItem(s, + "\t\tdiv.wimage { " + "visibility:inherit; " + "position:absolute; " + "overflow:hidden; " + "}\n" + ); return 0; } @@ -74,57 +84,52 @@ htimgRender(pHtSession s, pWgtrNode tree, int z) char name[64]; char src[256]; int x=-1,y=-1,w,h; - int id, i; - char *text; + int rval = -1; char fieldname[HT_FIELDNAME_SIZE]; char form[64]; - char* aspect; + char* alt_text = NULL; + char* aspect = NULL; + char* default_alt_text = ""; + char* default_aspect = "stretch"; + + /** Get an id for this. **/ + const int id = (HTIMG.idcnt++); - if(!(s->Capabilities.Dom0NS || s->Capabilities.Dom1HTML)) + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTTBL","Netscape DOM support or W3C DOM Level 1 HTML required"); - return -1; + mssError(1, "HTIMG", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto end; } - /** Get an id for this. **/ - id = (HTIMG.idcnt++); - /** Get x,y,w,h of this object **/ if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { mssError(1,"HTIMG","Image widget must have a 'width' property"); - return -1; + goto end; } if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) { mssError(1,"HTIMG","Image widget must have a 'height' property"); - return -1; + goto end; } if(wgtrGetPropertyValue(tree,"text",DATA_T_STRING,POD(&ptr)) == 0) - text=nmSysStrdup(ptr); - else - text=nmSysStrdup(""); + alt_text=nmSysStrdup(ptr); + if (alt_text == NULL) alt_text = default_alt_text; /** Image aspect scaling: stretch or preserve **/ if(wgtrGetPropertyValue(tree,"aspect",DATA_T_STRING,POD(&ptr)) == 0) aspect=nmSysStrdup(ptr); - else - aspect=nmSysStrdup("stretch"); + if (aspect == NULL) aspect = default_aspect; /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto end; strtcpy(name,ptr,sizeof(name)); /** image source **/ - /*if (!htrCheckAddExpression(s, tree, name, "source") && wgtrGetPropertyValue(tree,"source",DATA_T_STRING,POD(&ptr)) != 0) - { - mssError(1,"HTIMG","Image widget must have a 'source' property"); - nmSysFree(text); - return -1; - }*/ ptr = ""; htrCheckAddExpression(s, tree, name, "source"); wgtrGetPropertyValue(tree,"source",DATA_T_STRING,POD(&ptr)); @@ -144,43 +149,107 @@ htimgRender(pHtSession s, pWgtrNode tree, int z) else form[0]='\0'; - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#img%POS { left:%INTpx; top:%INTpx; width:%POSpx; height:%POSpx; z-index:%POS; text-align:center; }\n",id,x,y,w,h,z); - - /** Init image widget (?) **/ - htrAddWgtrObjLinkage_va(s, tree, "img%POS",id); - htrAddScriptInit_va(s, " im_init(wgtrGetNodeRef(ns,'%STR&SYM'), {field:'%STR&JSSTR', form:'%STR&JSSTR'});\n", - name, fieldname, form); - htrAddScriptInclude(s, "/sys/js/htdrv_image.js", 0); + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "img%POS", id) != 0) goto end; + + /** Initialize image scripts. **/ + if (htrAddScriptInclude(s, "/sys/js/htdrv_image.js", 0) != 0) goto end; + if (htrAddScriptInit_va(s, + "\tim_init(wgtrGetNodeRef(ns, '%STR&SYM'), { " + "field:'%STR&JSSTR', " + "form:'%STR&JSSTR', " + "});\n", + name, + fieldname, form + ) != 0) + { + mssError(0, "HTIMG", "Failed to write JS init call."); + goto end; + } /** Event Handlers **/ - htrAddEventHandlerFunction(s, "document","MOUSEUP", "img", "im_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "img", "im_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER", "img", "im_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEOUT", "img", "im_mouseout"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "img", "im_mousemove"); - - /** HTML body
element for the base layer. **/ - if (!strcmp(aspect, "stretch")) + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "img", "im_mousedown") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "img", "im_mousemove") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "img", "im_mouseout") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "img", "im_mouseover") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "img", "im_mouseup") != 0) goto end; + + /** Write the style for the image container div. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#img%POS { " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "text-align:center; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z + ) != 0) { - htrAddBodyItemLayer_va(s, 0, "img%POS", id, "wimage", - "\n\n", - id, w, h, src); + mssError(0, "HTIMG", "Failed to write image CSS."); + goto end; } - else // "preserve" + + /** Use a style that honors the aspect ratio attribute. **/ + char* style = (strcmp(aspect, "stretch") == 0) + ? /* "stretch" */ + "width:100%; " + "height:100%; " + : /* "preserve" */ + "width:100%; " + "height:100%; " + "object-fit:contain; "; + + /** Write image HTML, including the containing div. **/ + if (htrAddBodyItemLayer_va(s, 0, + "img%POS", id, "wimage", + "\n\n", + id, + w, + h, + style, + src, + alt_text + ) != 0) { - htrAddBodyItemLayer_va(s, 0, "img%POS", id, "wimage", - "\n\n", - id, w, h, src); + mssError(0, "HTIMG", "Failed to write image HTML."); + goto end; } - /** Check for more sub-widgets **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+1); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto end; - nmSysFree(text); + /** Success. **/ + rval = 0; - return 0; + end: + if (rval != 0) + { + mssError(0, "HTIMG", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + } + + /** Clean up. **/ + if (alt_text != NULL && alt_text != default_alt_text) nmSysFree(alt_text); + if (aspect != NULL && aspect != default_aspect) nmSysFree(aspect); + + return rval; } @@ -201,14 +270,6 @@ htimgInitialize() drv->Setup = htimgSetup; drv->Render = htimgRender; - /** Events **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_imagebutton.c b/centrallix/htmlgen/htdrv_imagebutton.c deleted file mode 100644 index 64cfe92eb..000000000 --- a/centrallix/htmlgen/htdrv_imagebutton.c +++ /dev/null @@ -1,222 +0,0 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - -/************************************************************************/ -/* Centrallix Application Server System */ -/* Centrallix Core */ -/* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ -/* */ -/* This program 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 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program 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 this program; if not, write to the Free Software */ -/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ -/* 02111-1307 USA */ -/* */ -/* A copy of the GNU General Public License has been included in this */ -/* distribution in the file "COPYING". */ -/* */ -/* Module: htdrv_imagebutton.c */ -/* Author: Greg Beeley (GRB) */ -/* Creation: January 4, 1998 */ -/* Description: HTML Widget driver for an 'image button', or a button */ -/* comprised of a set of three images - one the default, */ -/* second the image when pointed to, and third the image */ -/* when clicked. */ -/************************************************************************/ - - -/** globals **/ -static struct - { - int idcnt; - } - HTIBTN; - - -/*** htibtnRender - generate the HTML code for the page. - ***/ -int -htibtnRender(pHtSession s, pWgtrNode tree, int z) - { - char* ptr; - char name[64]; - char n_img[128]; - char p_img[128]; - char c_img[128]; - char d_img[128]; - int is_enabled = 1; - int button_repeat = 0; - int x,y,w,h; - int id, i; - pExpression code; - char* tooltip = NULL; - - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom0IE && !(s->Capabilities.Dom1HTML && s->Capabilities.Dom2CSS)) - { - mssError(1,"HTIBTN","Netscape DOM or W3C DOM1 HTML and DOM2 CSS support required"); - return -1; - } - - /** Get an id for this. **/ - id = (HTIBTN.idcnt++); - - /** Get x,y,w,h of this object **/ - if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) - { - mssError(1,"HTIBTN","ImageButton must have an 'x' property"); - return -1; - } - if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) - { - mssError(1,"HTIBTN","ImageButton must have a 'y' property"); - return -1; - } - if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) - { - mssError(1,"HTIBTN","ImageButton must have a 'width' property"); - return -1; - } - if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) h = -1; - - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(name,ptr,sizeof(name)); - - /** Get normal, point, and click images **/ - if (wgtrGetPropertyValue(tree,"image",DATA_T_STRING,POD(&ptr)) != 0) - { - mssError(1,"HTIBTN","ImageButton must have an 'image' property"); - return -1; - } - strtcpy(n_img,ptr,sizeof(n_img)); - - if (wgtrGetPropertyValue(tree,"pointimage",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(p_img,ptr,sizeof(p_img)); - else - strcpy(p_img, n_img); - - if (wgtrGetPropertyValue(tree,"clickimage",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(c_img,ptr,sizeof(c_img)); - else - strcpy(c_img, p_img); - - if (wgtrGetPropertyValue(tree,"disabledimage",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(d_img,ptr,sizeof(d_img)); - else - strcpy(d_img, n_img); - - if(wgtrGetPropertyValue(tree,"tooltip",DATA_T_STRING,POD(&ptr)) == 0) - tooltip=nmSysStrdup(ptr); - else - tooltip=nmSysStrdup(""); - - if (wgtrGetPropertyType(tree,"enabled") == DATA_T_STRING && wgtrGetPropertyValue(tree,"enabled",DATA_T_STRING,POD(&ptr)) == 0 && ptr) - { - if (!strcasecmp(ptr,"false") || !strcasecmp(ptr,"no")) is_enabled = 0; - } - - button_repeat = htrGetBoolean(tree, "repeat", 0); - - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#ib%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; cursor:pointer; }\n",id,x,y,w,z); - - htrAddScriptGlobal(s, "ib_cur_img", "null", 0); - htrAddWgtrObjLinkage_va(s, tree, "ib%POSpane", id); - - htrAddScriptInclude(s, "/sys/js/htdrv_imagebutton.js", 0); - - /** User requesting expression for enabled? **/ - if (wgtrGetPropertyType(tree,"enabled") == DATA_T_CODE) - { - wgtrGetPropertyValue(tree,"enabled",DATA_T_CODE,POD(&code)); - is_enabled = 0; - htrAddExpression(s, name, "enabled", code); - } - - htrAddScriptInit_va(s," ib_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), n:'%STR&JSSTR', p:'%STR&JSSTR', c:'%STR&JSSTR', d:'%STR&JSSTR', width:%INT, height:%INT, name:'%STR&SYM', enable:%INT, tooltip:'%STR&JSSTR', repeat:%INT});\n", - name, n_img, p_img, c_img, d_img, w, h, name,is_enabled, tooltip, button_repeat); - - /** HTML body
elements for the layers. **/ - if (h < 0) - if(is_enabled) - htrAddBodyItem_va(s,"
\n",id,n_img); - else - htrAddBodyItem_va(s,"
\n",id,d_img); - else - if(is_enabled) - htrAddBodyItem_va(s,"
\n",id,n_img,w,h); - else - htrAddBodyItem_va(s,"
\n",id,d_img,w,h); - - /** Add the event handling scripts **/ - htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "ib", "ib_mousedown"); - htrAddEventHandlerFunction(s, "document", "MOUSEUP", "ib", "ib_mouseup"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "ib", "ib_mouseover"); - htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "ib", "ib_mouseout"); - htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "ib", "ib_mousemove"); - - /** Check for more sub-widgets within the imagebutton. **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+1); - - if (tooltip) nmSysFree(tooltip); - - return 0; - } - - -/*** htibtnInitialize - register with the ht_render module. - ***/ -int -htibtnInitialize() - { - pHtDriver drv; - - /** Allocate the driver **/ - drv = htrAllocDriver(); - if (!drv) return -1; - - /** Fill in the structure. **/ - strcpy(drv->Name,"HTML ImageButton Widget Driver"); - strcpy(drv->WidgetName,"imagebutton"); - drv->Render = htibtnRender; - - htrAddAction(drv,"Enable"); - htrAddAction(drv,"Disable"); - - /** Add the 'click' event **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - - /** Register. **/ - htrRegisterDriver(drv); - - htrAddSupport(drv, "dhtml"); - - HTIBTN.idcnt = 0; - - return 0; - } diff --git a/centrallix/htmlgen/htdrv_label.c b/centrallix/htmlgen/htdrv_label.c index d7140489f..beac27abe 100644 --- a/centrallix/htmlgen/htdrv_label.c +++ b/centrallix/htmlgen/htdrv_label.c @@ -1,21 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "cxlib/qprintf.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,6 +28,16 @@ /* Description: HTML Widget driver for a single-line label. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/newmalloc.h" +#include "cxlib/strtcpy.h" +#include "expression.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -66,32 +63,29 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) char fieldname[HT_FIELDNAME_SIZE]; char form[64]; int x=-1,y=-1,w,h; - int id; - /*int fontsize;*/ + int rval = -1; int font_size; - char *text; - char* tooltip; - char stylestr[128]; + char *text = NULL; + char* tooltip = NULL; int is_bold = 0; int is_link = 0; int is_italic = 0; int allow_break = 0; int overflow_ellipsis = 0; - pExpression code; - int n; int auto_height=0; - if(!(s->Capabilities.Dom0NS || s->Capabilities.Dom1HTML)) + /** Get an id for this. **/ + const int id = (HTLBL.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTTBL","Netscape DOM support or W3C DOM Level 1 HTML required"); - return -1; + mssError(1, "HTLBL", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto end; } - /** Get an id for this. **/ - id = (HTLBL.idcnt++); - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto end; strtcpy(name,ptr,sizeof(name)); /** Get x,y,w,h of this object **/ @@ -100,18 +94,21 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { mssError(1,"HTLBL","Label widget must have a 'width' property"); - return -1; + goto end; } if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) { mssError(1,"HTLBL","Label widget must have a 'height' property"); - return -1; + goto end; } /** auto height? **/ + int n; if (wgtrGetPropertyValue(tree,"r_height",DATA_T_INTEGER,POD(&n)) == 0 && n == -1) auto_height = 1; + /** Get label value. **/ + pExpression code; if (wgtrGetPropertyType(tree,"value") == DATA_T_CODE) { wgtrGetPropertyValue(tree,"value",DATA_T_CODE,POD(&code)); @@ -125,6 +122,7 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) else text=nmSysStrdup(""); + /** Get tooltip. **/ if(wgtrGetPropertyValue(tree,"tooltip",DATA_T_STRING,POD(&ptr)) == 0) tooltip=nmSysStrdup(ptr); else @@ -197,43 +195,156 @@ htlblRender(pHtSession s, pWgtrNode tree, int z) form[0]='\0'; /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#lbl%POS { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; %[HEIGHT:%POSpx; %]WIDTH:%POSpx; Z-INDEX:%POS; cursor:default; %[font-weight:bold; %]%[color:%STR&CSSVAL; %]%[font-size:%POSpx; %]text-align:%STR&CSSVAL; vertical-align:%STR&CSSVAL; %[white-space:nowrap; %]%[text-overflow:ellipsis; overflow:hidden; %]%[font-style:italic; %]}\n", - id,x,y, - !auto_height, h, - w,z, - is_bold, *fgcolor, fgcolor, font_size > 0, font_size, align, valign, - !allow_break, overflow_ellipsis, is_italic); + if (htrAddStylesheetItem_va(s, + "\t\t#lbl%POS { " + "position:absolute; " + "visibility:inherit; " + "cursor:default; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "%[height:"ht_flex_format"; %]" + "z-index:%POS; " + "%[font-weight:bold; %]" + "%[color:%STR&CSSVAL; %]" + "%[font-size:%POSpx; %]" + "text-align:%STR&CSSVAL; " + "vertical-align:%STR&CSSVAL; " + "%[white-space:nowrap; %]" + "%[text-overflow:ellipsis; overflow:hidden; %]" + "%[font-style:italic; %]" + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + (!auto_height), ht_flex_h(h, tree), + z, + (is_bold), + (*fgcolor), fgcolor, + (font_size > 0), font_size, + align, + valign, + (!allow_break), + (overflow_ellipsis), + (is_italic) + ) != 0) + { + mssError(0, "HTLBL", "Failed to write base label CSS."); + goto end; + } + if (is_link) - htrAddStylesheetItem_va(s,"\t#lbl%POS:hover { %[color:%STR&CSSVAL; %]text-decoration:underline; cursor:pointer; }\n", id, *pfgcolor, pfgcolor); + { + if (htrAddStylesheetItem_va(s, + "\t\t#lbl%POS:hover { " + "%[color:%STR&CSSVAL; %]" + "text-decoration:underline; " + "cursor:pointer; " + "}\n", + id, + (*pfgcolor), pfgcolor + ) != 0) + { + mssError(0, "HTLBL", "Failed to write label hover CSS."); + goto end; + } + } if (is_link && *cfgcolor) - htrAddStylesheetItem_va(s,"\t#lbl%POS:active { color:%STR&CSSVAL; text-decoration:underline; cursor:pointer; }\n", id, cfgcolor); - htrAddStylesheetItem_va(s,"\t#lbl%POS p { text-align:%STR&CSSVAL; %[position:relative; top:50%%; transform:translateY(-50%%); %]padding:0px; margin:0px; border-spacing:0px; width:%POSpx; }\n", id, align, !strcmp(valign, "middle"), w); + { + if (htrAddStylesheetItem_va(s, + "\t\t#lbl%POS:active { " + "color:%STR&CSSVAL; " + "text-decoration:underline; " + "cursor:pointer; " + "}\n", + id, + cfgcolor + ) != 0) + { + mssError(0, "HTLBL", "Failed to write active label (click) CSS."); + goto end; + } + } - htrAddWgtrObjLinkage_va(s, tree, "lbl%POS",id); - stylestr[0] = '\0'; - htrAddScriptInit_va(s, " lbl_init(wgtrGetNodeRef(ns,'%STR&SYM'), {field:'%STR&JSSTR', form:'%STR&JSSTR', text:'%STR&JSSTR', style:'%STR&JSSTR', tooltip:'%STR&JSSTR', link:%POS, pfg:'%STR&JSSTR'});\n", - name, fieldname, form, text, stylestr, tooltip, is_link, pfgcolor); + if (htrAddStylesheetItem_va(s, + "\t\t#lbl%POS p { " + "text-align:%STR&CSSVAL; " + "%[position:relative; top:50%%; transform:translateY(-50%%); %]" + "padding:0px; " + "margin:0px; " + "border-spacing:0px; " + "width:100%%; " + "}\n", + id, + align, + (strcmp(valign, "middle") == 0) + ) != 0) + { + mssError(0, "HTLBL", "Failed to write label text CSS."); + goto end; + } + + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "lbl%POS", id) != 0) goto end; + + /** Write the initialization script call. **/ + if (htrAddScriptInit_va(s, + "\tlbl_init(wgtrGetNodeRef(ns,'%STR&SYM'), { " + "field:'%STR&JSSTR', " + "form:'%STR&JSSTR', " + "text:'%STR&JSSTR', " + "style:'', " + "tooltip:'%STR&JSSTR', " + "link:%POS, " + "pfg:'%STR&JSSTR', " + "});\n", + name, + fieldname, form, text, + tooltip, is_link, pfgcolor + ) != 0) + { + mssError(0, "HTLBL", "Failed to write label init script."); + goto end; + } /** Script include to get functions **/ - htrAddScriptInclude(s, "/sys/js/htdrv_label.js", 0); + if (htrAddScriptInclude(s, "/sys/js/htdrv_label.js", 0) != 0) goto end; /** Event Handlers **/ - htrAddEventHandlerFunction(s, "document","MOUSEUP", "lbl", "lbl_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "lbl", "lbl_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER", "lbl", "lbl_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEOUT", "lbl", "lbl_mouseout"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "lbl", "lbl_mousemove"); + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "lbl", "lbl_mousedown") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "lbl", "lbl_mousemove") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "lbl", "lbl_mouseout") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "lbl", "lbl_mouseover") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "lbl", "lbl_mouseup") != 0) goto end; /** HTML body
element for the base layer. **/ - htrAddBodyItemLayer_va(s, 0, "lbl%POS", id, NULL, "

%STR&HTENLBR

", text); + if (htrAddBodyItemLayer_va(s, 0, "lbl%POS", id, NULL, "

%STR&HTENLBR

", text) != 0) + { + mssError(0, "HTLBL", "Failed to write label HTML."); + goto end; + } - /** Check for more sub-widgets **/ - htrRenderSubwidgets(s, tree, z+1); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto end; - nmSysFree(text); - nmSysFree(tooltip); + /** Success. **/ + rval = 0; - return 0; + end: + if (rval != 0) + { + mssError(0, "HTLBL", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + } + + /** Clean up. **/ + if (text != NULL) nmSysFree(text); + if (tooltip != NULL) nmSysFree(tooltip); + + return rval; } @@ -253,14 +364,6 @@ htlblInitialize() strcpy(drv->WidgetName,"label"); drv->Render = htlblRender; - /** Events **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_map.c b/centrallix/htmlgen/htdrv_map.c index 73f0d5518..06b0d28ce 100755 --- a/centrallix/htmlgen/htdrv_map.c +++ b/centrallix/htmlgen/htdrv_map.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 2012 LightSys Technology Services, Inc. */ +/* Copyright (C) 2012-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,15 @@ /* Description: HTML Widget driver for maps using OpenLayers */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + + /** globals **/ static struct { @@ -55,17 +52,17 @@ int htmapRender(pHtSession s, pWgtrNode map_node, int z) char main_bg[128]; char osrc[64]; int x = -1, y = -1, w, h; - int id; int allow_select, show_select; - if (!s->Capabilities.Dom0NS && !(s->Capabilities.Dom1HTML && s->Capabilities.CSS1)) - { - mssError(1, "HTMAP", "Netscape DOM or W3C DOM1 HTML and CSS support required"); - return -1; - } - /** Get an id for this. **/ - id = (HTMAP.idcnt++); + const int id = (HTMAP.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) + { + mssError(1, "HTMAP", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; + } /** Get x,y,w,h of this object **/ if (wgtrGetPropertyValue(map_node, "x", DATA_T_INTEGER, POD(&x)) != 0) @@ -104,39 +101,86 @@ int htmapRender(pHtSession s, pWgtrNode map_node, int z) strtcpy(name, ptr, sizeof(name)); /** Add css item for the layer **/ - htrAddStylesheetItem_va(s, "\t#map%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; overflow: hidden; %STR}\n", id, x, y, w, h, z, main_bg); - - htrAddWgtrObjLinkage_va(s, map_node, "map%POSbase", id); - - /** Include our necessary supporting js files **/ - htrAddScriptInclude(s, "/sys/js/htdrv_map.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - htrAddScriptInclude(s, "/sys/js/openlayers/build/ol.js", 0); - htrAddScriptInclude(s, "/sys/js/openlayers/build/ol.js.map", 0); - //htrAddScriptInclude(s, "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js", 0); - //htrAddScriptInclude(s, "https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js", 0); - - /** Event Handlers **/ - htrAddEventHandlerFunction(s, "document", "MOUSEUP", "map", "map_mouseup"); - htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "map", "map_mousedown"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "map", "map_mouseover"); - htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "map", "map_mousemove"); - htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "map", "map_mouseout"); + if (htrAddStylesheetItem_va(s, + "\t\t#map%POSbase { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "%STR " + "}\n", + id, x, y, w, h, z, main_bg + ) != 0) + { + mssError(0, "HTMAP", "Failed to write base CSS."); + goto err; + } + + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, map_node, "map%POSbase", id) != 0) goto err; + + /** Include supporting JS files, **/ + if (htrAddScriptInclude(s, "/sys/js/htdrv_map.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/openlayers/build/ol.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/openlayers/build/ol.js.map", 0) != 0) goto err; + //if (htrAddScriptInclude(s, "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js", 0) != 0) goto err; + //if (htrAddScriptInclude(s, "https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js", 0) != 0) goto err; + + /** Add event handlers. **/ + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "map", "map_mouseup") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "map", "map_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "map", "map_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "map", "map_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "map", "map_mouseout") != 0) goto err; /** Script initialization call. **/ - htrAddScriptInit_va(s, " map_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), osrc:%[wgtrGetNodeRef(ns,\"%STR&SYM\")%]%[null%], allow_select:%INT, show_select:%INT, name:\"%STR&SYM\"});\n", - name, *osrc, osrc, !*osrc, allow_select, show_select, name); + if (htrAddScriptInit_va(s, + "\tmap_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "osrc:%[wgtrGetNodeRef(ns, '%STR&SYM')%]%[null%], " + "name:'%STR&SYM', " + "allow_select:%INT, " + "show_select:%INT, " + "});\n", + name, (osrc[0] != '\0'), osrc, (osrc[0] == '\0'), name, + allow_select, show_select + ) != 0) + { + mssError(0, "HTMAP", "Failed to write JS init call."); + goto err; + } /** HTML body
element to be used by the OpenLayers map. **/ - htrAddBodyItem_va(s, "
\n", id); + if (htrAddBodyItem_va(s, "
\n", id) != 0) + { + mssError(0, "HTMAP", "Failed to write HTML for map open tag."); + goto err; + } - /** Check for widgets within the map. **/ - htrRenderSubwidgets(s, map_node, z + 2); + /** Render children. **/ + if (htrRenderSubwidgets(s, map_node, z + 2) != 0) goto err; /** End the containing div. **/ - htrAddBodyItem(s, "
\n"); + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTMAP", "Failed to write HTML for map closing tag."); + goto err; + } + /** Success. **/ return 0; + + err: + mssError(0, "HTMAP", + "Failed to render \"%s\":\"%s\" (id: %d).", + map_node->Name, map_node->Type, id + ); + return -1; } /*** htmapInitialize - register with the ht_render module. @@ -155,12 +199,6 @@ int htmapInitialize() strcpy(drv->WidgetName, "map"); drv->Render = htmapRender; - htrAddEvent(drv, "MouseUp"); - htrAddEvent(drv, "MouseDown"); - htrAddEvent(drv, "MouseOver"); - htrAddEvent(drv, "MouseOut"); - htrAddEvent(drv, "MouseMove"); - htrAddEvent(drv, "Click"); /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_menu.c b/centrallix/htmlgen/htdrv_menu.c index a400c885e..5d5d2627a 100644 --- a/centrallix/htmlgen/htdrv_menu.c +++ b/centrallix/htmlgen/htdrv_menu.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 2000-2005 LightSys Technology Services, Inc. */ +/* Copyright (C) 2000-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,6 +29,19 @@ /* vertical, or horizontal. */ /************************************************************************/ +#include + +#include "cxlib/check.h" +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "cxlib/xstring.h" +#include "expression.h" +#include "ht_render.h" +#include "wgtr.h" + + /** globals **/ static struct { @@ -49,10 +50,23 @@ static struct HTMN; +/*** htmenu_internal_AddDot() - write the image that marks where a menu row was + *** drawn, so htdrv_menu.js can find that row at runtime. + *** + *** Every mcnt gets exactly one of these, and one row. mn_scan_images() in + *** htdrv_menu.js indexes its coords array by mcnt, and wgtmenuVerify() in + *** wgtdrv_menu.c counts rows the same way, so a new kind of row has to call + *** this too. + ***/ int htmenu_internal_AddDot(pHtSession s, int mcnt, char* nptr, int is_horizontal, int row_height) { - htrAddBodyItem_va(s,"", ((mcnt&1) || !is_horizontal)?"top":"bottom", ((mcnt&1) || !is_horizontal)?"top":"bottom", nptr, mcnt, ((mcnt&1) || !is_horizontal)?(row_height?row_height:1):1); + if (htrAddBodyItem_va(s,"", ((mcnt&1) || !is_horizontal)?"top":"bottom", ((mcnt&1) || !is_horizontal)?"top":"bottom", nptr, mcnt, ((mcnt&1) || !is_horizontal)?(row_height?row_height:1):1) != 0) + { + mssError(0, "HTMENU", "Failed to write position tracking image for item %d.", mcnt); + return -1; + } + return 0; } @@ -66,39 +80,47 @@ htmenu_internal_AddItem(pHtSession s, pWgtrNode menu_item, int is_horizontal, in xsPrintf(xs, "enabled:1, onright:%d", is_onright); if (!is_horizontal) - htrAddBodyItem(s, ""); + { + if (htrAddBodyItem(s, "") != 0) goto error; + } /** image used to track position **/ - htmenu_internal_AddDot(s, mcnt, nptr, is_horizontal, row_h); + if (htmenu_internal_AddDot(s, mcnt, nptr, is_horizontal, row_h) != 0) goto error; strtcpy(name, wgtrGetName(menu_item), sizeof(name)); /** icon **/ if (wgtrGetPropertyValue(menu_item,"icon",DATA_T_STRING,POD(&ptr)) == 0) { - htrAddBodyItem_va(s, "", ptr); + if (htrAddBodyItem_va(s, "", ptr) != 0) goto error; xsConcatQPrintf(xs, ", icon:'%STR&JSSTR'", ptr); } else - htrAddBodyItem(s, " "); + { + if (htrAddBodyItem(s, " ") != 0) goto error; + } /** checkbox **/ if ( (rval=htrGetBoolean(menu_item, "checked", -1)) >= 0) { - htrAddBodyItem_va(s, "", mcnt, rval?"checked":"unchecked"); - xsConcatQPrintf(xs, ", check:%STR", rval?"true":"false"); + if (htrAddBodyItem_va(s, "", nptr, mcnt, rval?"checked":"unchecked") != 0) goto error; + xsConcatQPrintf(xs, ", check:%BOOL", rval); } else - htrAddBodyItem(s, ""); + { + if (htrAddBodyItem(s, "") != 0) goto error; + } /** Text **/ if (wgtrGetPropertyValue(menu_item,"label",DATA_T_STRING,POD(&ptr)) == 0) { - htrAddBodyItem_va(s, "%STR&HTE", ptr); + if (htrAddBodyItem_va(s, "%STR&HTE", ptr) != 0) goto error; xsConcatQPrintf(xs, ", label:'%STR&JSSTR'", ptr); } else - htrAddBodyItem(s, ""); + { + if (htrAddBodyItem(s, "") != 0) goto error; + } if (wgtrGetPropertyValue(menu_item, "value", DATA_T_STRING, POD(&ptr)) == 0) xsConcatQPrintf(xs, ", value:'%STR&JSSTR'", ptr); else if (wgtrGetPropertyValue(menu_item, "value", DATA_T_INTEGER, POD(&n)) == 0) @@ -107,13 +129,17 @@ htmenu_internal_AddItem(pHtSession s, pWgtrNode menu_item, int is_horizontal, in /** Submenu arrow **/ if (is_submenu && !is_horizontal) { - htrAddBodyItem(s, ""); + if (htrAddBodyItem(s, "") != 0) goto error; } else - htrAddBodyItem(s, " "); + { + if (htrAddBodyItem(s, " ") != 0) goto error; + } if (!is_horizontal) - htrAddBodyItem(s, ""); + { + if (htrAddBodyItem(s, "") != 0) goto error; + } if (is_submenu) { @@ -121,11 +147,28 @@ htmenu_internal_AddItem(pHtSession s, pWgtrNode menu_item, int is_horizontal, in } if (is_submenu) - htrAddScriptInit_va(s, " wgtrGetNodeRef(ns,\"%STR&SYM\").AddItem({%STR});\n", nptr, xs->String); + { + if (htrAddScriptInit_va(s, + "\twgtrGetNodeRef(ns, '%STR&SYM').AddItem({ %STR });\n", + nptr, xs->String + ) != 0) + goto error; + } else - htrAddScriptInit_va(s, " wgtrReplaceNode(wgtrGetNodeRef(\"%STR&SYM\",\"%STR&SYM\"), wgtrGetNodeRef(ns,\"%STR&SYM\").AddItem({%STR}));\n", wgtrGetNamespace(menu_item), name, nptr, xs->String); + { + if (htrAddScriptInit_va(s, + "\twgtrReplaceNode(wgtrGetNodeRef('%STR&SYM', '%STR&SYM'), wgtrGetNodeRef(ns, '%STR&SYM').AddItem({ %STR }));\n", + wgtrGetNamespace(menu_item), name, nptr, xs->String + ) != 0) + goto error; + } return 0; + + error: + mssError(0, "HTMENU", "Failed to write menu item %d.", mcnt); + + return -1; } @@ -134,22 +177,29 @@ htmenu_internal_AddSep(pHtSession s, int is_horizontal, int row_h, int mcnt, cha { if (!is_horizontal) - htrAddBodyItem(s, ""); + { + if (htrAddBodyItem(s, "") != 0) goto error; + } - htmenu_internal_AddDot(s, mcnt, nptr, is_horizontal, row_h); + if (htmenu_internal_AddDot(s, mcnt, nptr, is_horizontal, row_h) != 0) goto error; /** If vertical, add a separating line. If horiz, just add some space **/ - if (is_horizontal) + if (is_horizontal) { - htrAddBodyItem(s, "   "); + if (htrAddBodyItem(s, "   ") != 0) goto error; } else { - htrAddBodyItem(s, ""); + if (htrAddBodyItem(s, "") != 0) goto error; } - htrAddScriptInit_va(s, " wgtrGetNodeRef(ns,\"%STR&SYM\").AddItem({sep:true});\n", nptr); + if (htrAddScriptInit_va(s, "\twgtrGetNodeRef(ns, '%STR&SYM').AddItem({ sep:true });\n", nptr) != 0) goto error; return 0; + + error: + mssError(0, "HTMENU", "Failed to write menu separator %d.", mcnt); + + return -1; } @@ -159,17 +209,24 @@ htmenu_internal_AddTitle(pHtSession s, pWgtrNode menu_title, int is_horizontal, char* ptr; if (!is_horizontal) - htrAddBodyItem(s, ""); + { + if (htrAddBodyItem(s, "") != 0) goto error; + } - htmenu_internal_AddDot(s, mcnt, nptr, is_horizontal, row_h); + if (htmenu_internal_AddDot(s, mcnt, nptr, is_horizontal, row_h) != 0) goto error; if (wgtrGetPropertyValue(menu_title,"label",DATA_T_STRING,POD(&ptr)) != 0) ptr = ""; - htrAddBodyItem_va(s, "%STR&HTE", ptr); - htrAddScriptInit_va(s, " wgtrGetNodeRef(ns,\"%STR&SYM\").AddItem({sep:true});\n", nptr); + if (htrAddBodyItem_va(s, "%STR&HTE", ptr) != 0) goto error; + if (htrAddScriptInit_va(s, "\twgtrGetNodeRef(ns, '%STR&SYM').AddItem({ sep:true });\n", nptr) != 0) goto error; return 0; + + error: + mssError(0, "HTMENU", "Failed to write menu title %d.", mcnt); + + return -1; } @@ -185,19 +242,20 @@ htmenu_internal_CheckAddItem(pHtSession s, pWgtrNode sub_tree, int is_horizontal is_submenu = !strcmp(ptr,"widget/menu"); if (is_onright ^ !(is_horizontal && htrGetBoolean(sub_tree, "onright", 0) == 1)) { - htmenu_internal_AddItem(s, sub_tree, is_horizontal, is_popup, is_submenu, - is_onright, row_h, *mcnt, name, xs); + if (htmenu_internal_AddItem(s, sub_tree, is_horizontal, is_popup, is_submenu, + is_onright, row_h, *mcnt, name, xs) != 0) + return -1; (*mcnt)++; } } else if (!strcmp(ptr,"widget/menusep")) { - htmenu_internal_AddSep(s, is_horizontal, row_h, *mcnt, name); + if (htmenu_internal_AddSep(s, is_horizontal, row_h, *mcnt, name) != 0) return -1; (*mcnt)++; } else if (!strcmp(ptr,"widget/menutitle")) { - htmenu_internal_AddTitle(s, sub_tree, is_horizontal, row_h, *mcnt, name); + if (htmenu_internal_AddTitle(s, sub_tree, is_horizontal, row_h, *mcnt, name) != 0) return -1; (*mcnt)++; } @@ -218,31 +276,33 @@ htmenuRender(pHtSession s, pWgtrNode menu, int z) char *ptr; int x,y,w,h; int col_w, row_h; - int id, i, j, cnt, cntj, mcnt; + int i, j, cnt, cntj, mcnt; int is_horizontal; int is_popup; /*int is_submenu;*/ pWgtrNode sub_tree; pWgtrNode sub_tree_child; - pXString xs; - int bx = 0; + pXString xs = NULL; + int rval = -1; int shadow_offset, shadow_radius; char shadow_color[128]; + const int border_w = 1; /** wgtdrv_menu.c declares this as an inset **/ - if(!s->Capabilities.Dom0NS && !s->Capabilities.CSS2) + /** Get an id for this. **/ + const int id = (HTMN.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTMENU","Netscape 4 DOM or W3C CSS2 support required"); - return -1; + mssError(1, "HTMENU", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto end; } - if (s->Capabilities.CSS2) bx = 1; - - /** Get an id for this. **/ - id = (HTMN.idcnt++); /** Get x,y,height,& width of this object **/ if (wgtrGetPropertyValue(menu,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; if (wgtrGetPropertyValue(menu,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; if (wgtrGetPropertyValue(menu,"height",DATA_T_INTEGER,POD(&h)) != 0) h = -1; + if (menu->Flags & WGTR_F_AUTOHEIGHT) h = -1; /** let htdrv_menu.js fit the rows **/ if (wgtrGetPropertyValue(menu,"width",DATA_T_INTEGER,POD(&w)) != 0) w = -1; if (wgtrGetPropertyValue(menu,"column_width",DATA_T_INTEGER,POD(&col_w)) != 0) col_w = 0; if (wgtrGetPropertyValue(menu,"row_height",DATA_T_INTEGER,POD(&row_h)) != 0) row_h = 0; @@ -263,11 +323,11 @@ htmenuRender(pHtSession s, pWgtrNode menu, int z) } /** Colors/Backgrounds **/ - if (htrGetBackground(menu, NULL, s->Capabilities.CSS2, bgstr, sizeof(bgstr)) < 0) + if (htrGetBackground(menu, NULL, s->Capabilities.CSS2, bgstr, sizeof(bgstr)) != 0) strcpy(bgstr, ""); - if (htrGetBackground(menu, "highlight", s->Capabilities.CSS2, highlight, sizeof(highlight)) < 0) + if (htrGetBackground(menu, "highlight", s->Capabilities.CSS2, highlight, sizeof(highlight)) != 0) strcpy(highlight, bgstr); - if (htrGetBackground(menu, "active", s->Capabilities.CSS2, active, sizeof(highlight)) < 0) + if (htrGetBackground(menu, "active", s->Capabilities.CSS2, active, sizeof(highlight)) != 0) strcpy(active, highlight); if (wgtrGetPropertyValue(menu, "fgcolor", DATA_T_STRING, POD(&ptr)) != 0) strcpy(textcolor, "black"); @@ -284,86 +344,170 @@ htmenuRender(pHtSession s, pWgtrNode menu, int z) is_popup = htrGetBoolean(menu, "popup", 0); if (is_popup < 0) is_popup = 0; - /** Write the main style header item. **/ - htrAddStylesheetItem_va(s,"\t#mn%POSmain { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; %[HEIGHT:%POSpx; %]%[WIDTH:%POSpx; %]Z-INDEX:%POS; }\n", id,is_popup?"hidden":"inherit", x, y, h != -1, h-2*bx, w != -1, w-2*bx, z); + /** Write styles for the main DOM element. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#mn%POSmain { " + "position:absolute; " + "visibility:%STR; " + "overflow:hidden; " + "box-sizing:border-box; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "%[height:"ht_flex_format"; %]" + "%[width:"ht_flex_format"; %]" + "color:%STR; %STR" + "border-style:solid; " + "border-width:%POSpx; " + "border-color:white gray gray white; " + "z-index:%POS; " + "}\n", + id, + (is_popup) ? "hidden" : "inherit", + ht_flex_x(x, menu), + ht_flex_y(y, menu), + (h != -1), ht_flex_h(h, menu), + (w != -1), ht_flex_w(w, menu), + textcolor, bgstr, + border_w, + z + ) != 0) + { + mssError(0, "HTMENU", "Failed to write main CSS."); + goto end; + } if (shadow_radius > 0) { - htrAddStylesheetItem_va(s,"\t#mn%POSmain { box-shadow: %POSpx %POSpx %POSpx %STR&CSSVAL; }\n", id, shadow_offset, shadow_offset, shadow_radius, shadow_color); + if (htrAddStylesheetItem_va(s, + "\t\t#mn%POSmain { " + "box-shadow: %POSpx %POSpx %POSpx %STR&CSSVAL; " + "}\n", + id, + shadow_offset, shadow_offset, shadow_radius, shadow_color + ) != 0) + { + mssError(0, "HTMENU", "Failed to write shadow CSS."); + goto end; + } + } + + /** Write styles for the content container. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#mn%POScontent { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "cursor:default; " + "left:0px; " + "top:0px; " + "height:100%%; " + "width:100%%; " + "z-index:%POS; " + "}\n", + id, + z + 1 + ) != 0) + { + mssError(0, "HTMENU", "Failed to write content CSS."); + goto end; } - htrAddStylesheetItem_va(s,"\t#mn%POScontent { POSITION:absolute; VISIBILITY: inherit; LEFT:0px; TOP:0px; %[HEIGHT:%POSpx; %]%[WIDTH:%POSpx; %]Z-INDEX:%POS; }\n", id, h != -1, h-2*bx, w != -1, w-2*bx, z+1); - if (s->Capabilities.CSS2) - htrAddStylesheetItem_va(s,"\t#mn%POSmain { overflow:hidden; border-style: solid; border-width: 1px; border-color: white gray gray white; color:%STR; %STR }\n", id, textcolor, bgstr); - - /** content layer **/ - if (s->Capabilities.CSS2) - htrAddStylesheetItem_va(s,"\t#mn%POScontent { overflow:hidden; cursor:default; }\n", id ); - /** highlight bar **/ - htrAddStylesheetItem_va(s, "\t#mn%POShigh { POSITION:absolute; VISIBILITY: hidden; LEFT:0px; TOP:0px; Z-INDEX:%POS; }\n", id, z); - if (s->Capabilities.CSS2) - htrAddStylesheetItem_va(s,"\t#mn%POShigh { overflow:hidden; }\n", id ); + /** Write styles for the highlight bar. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#mn%POShigh { " + "position:absolute; " + "visibility:hidden; " + "overflow:hidden; " + "left:0px; " + "top:0px; " + "z-index:%POS; " + "}\n", + id, + z + ) != 0) + { + mssError(0, "HTMENU", "Failed to write highlight CSS."); + goto end; + } /** Get name **/ - if (wgtrGetPropertyValue(menu,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(menu, "name", DATA_T_STRING, POD(&ptr)) != 0) + { + mssError(1, "HTMENU", "Failed to get name!?."); + goto end; + } strtcpy(name,ptr,sizeof(name)); - - /** Globals **/ - htrAddScriptGlobal(s, "mn_active", "new Array()", 0); - htrAddScriptGlobal(s, "mn_current", "null", 0); - htrAddScriptGlobal(s, "mn_deactivate_tmout", "null", 0); - htrAddScriptGlobal(s, "mn_submenu_tmout", "null", 0); - htrAddScriptGlobal(s, "mn_pop_x", "0", 0); - htrAddScriptGlobal(s, "mn_pop_y", "0", 0); - htrAddScriptGlobal(s, "mn_mouseangle", "0", 0); - htrAddWgtrObjLinkage_va(s, menu, "mn%POSmain",id); - htrAddWgtrCtrLinkage_va(s, menu, "htr_subel(_obj, \"mn%POScontent\")",id); - - /** Scripts **/ - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - htrAddScriptInclude(s, "/sys/js/htdrv_menu.js", 0); - - /** Initialization **/ - htrAddScriptInit_va(s," mn_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), clayer:wgtrGetContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")), hlayer:htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"), \"mn%POShigh\"), bgnd:\"%STR&JSSTR\", high:\"%STR&JSSTR\", actv:\"%STR&JSSTR\", txt:\"%STR&JSSTR\", w:%INT, h:%INT, horiz:%INT, pop:%INT, name:\"%STR&SYM\"});\n", - name, name, name, id, - bgstr, highlight, active, textcolor, - w, h, is_horizontal, is_popup, name); - - /** Event handlers **/ - htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "mn", "mn_mousemove"); - htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "mn", "mn_mouseout"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "mn", "mn_mouseover"); - htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "mn", "mn_mousedown"); + + /** Write widget object and container linkage. **/ + if (htrAddWgtrObjLinkage_va(s, menu, "mn%POSmain", id) != 0) goto end; + if (htrAddWgtrCtrLinkage_va(s, menu, "htr_subel(_obj, 'mn%POScontent')", id) != 0) goto end; + + /** Write JS globals. **/ + if (htrAddScriptGlobal(s, "mn_active", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "mn_current", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "mn_deactivate_tmout", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "mn_mouseangle", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "mn_pop_x", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "mn_pop_y", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "mn_submenu_tmout", "null", 0) != 0) goto end; + + /** Write script includes. **/ + if (htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/htdrv_menu.js", 0) != 0) goto end; + + /** Event handlers. **/ + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "mn", "mn_mousedown") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "mn", "mn_mousemove") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "mn", "mn_mouseout") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "mn", "mn_mouseover") != 0) goto end; + + /** Write script initialization. **/ + if (htrAddScriptInit_va(s, "\t{ " + "const layer = wgtrGetNodeRef(ns, '%STR&SYM'); " + "mn_init({ " + "layer, " + "clayer:wgtrGetContainer(layer), " + "hlayer:htr_subel(layer, 'mn%POShigh'), " + "name:'%STR&SYM', " + "bgnd:'%STR&JSSTR', " + "high:'%STR&JSSTR', " + "actv:'%STR&JSSTR', " + "txt:'%STR&JSSTR', " + "w:%INT, " + "h:%INT, " + "bw:%POS, " + "horiz:%INT, " + "pop:%INT, " + "}); }\n", + name, id, name, + bgstr, highlight, active, textcolor, + w, h, border_w, + is_horizontal, is_popup + ) != 0) + { + mssError(0, "HTMENU", "Failed to write JS init code."); + goto end; + } /** Beginning of code for menu **/ - htrAddBodyItem_va(s,"
", id); - if (s->Capabilities.Dom0NS) - htrAddBodyItem_va(s,"",bgstr); - htrAddBodyItem_va(s,"
\n", id, s->Capabilities.Dom0NS?"":"width=\"100%\" height=\"100%\""); - - /** Only draw border if it is NS4 **/ - if (s->Capabilities.Dom0NS) + if (htrAddBodyItem_va(s, + "
" + "
" + "
\n" + ""); - if (w != -1) - htrAddBodyItem_va(s,"", w-2); - else - htrAddBodyItem(s,""); - htrAddBodyItem(s,"\n"); - if (h != -1) - htrAddBodyItem_va(s,"\n") != 0) goto end; } - else - htrAddBodyItem(s,""); + if (htrAddBodyItem(s,"
", + id, id + ) != 0) { - htrAddBodyItem(s,"
", h-2); - else - htrAddBodyItem(s,"
"); + mssError(0, "HTMENU", "Failed to write HTML for menu."); + goto end; } - else - htrAddBodyItem(s,"
"); - /** Add 'meat' of menu... **/ - xs = (pXString)nmMalloc(sizeof(XString)); - xsInit(xs); + /** Add menu items and dropdowns. **/ + xs = checkPtr(xsNew()); + if (xs == NULL) goto end; mcnt=0; - htrAddBodyItem(s,"", is_horizontal) != 0) goto end; if (is_horizontal) { - htrAddBodyItem(s,"\n", h-2); - else - htrAddBodyItem(s,"\n"); - htrAddBodyItem(s,""); - if (w != -1) - htrAddBodyItem_va(s,"", w-2); - else - htrAddBodyItem(s,""); - htrAddBodyItem(s,"\n"); + if (htrAddBodyItem(s,"
\n"); - htrAddBodyItem_va(s,"%[%]\n", is_horizontal); + if (htrAddBodyItem(s,"
", is_horizontal); + if (htrAddBodyItem_va(s,"%[%]
\n") != 0) goto end; + if (htrAddBodyItem_va(s,"%[%]\n", is_horizontal) != 0) goto end; cnt = xaCount(&(menu->Children)); for (i=0;iChildren), j); - htmenu_internal_CheckAddItem(s, sub_tree_child, is_horizontal, is_popup, 0, row_h, &mcnt, name, xs); + if (htmenu_internal_CheckAddItem(s, sub_tree_child, is_horizontal, is_popup, 0, row_h, &mcnt, name, xs) != 0) goto end; } } else { - htmenu_internal_CheckAddItem(s, sub_tree, is_horizontal, is_popup, 0, row_h, &mcnt, name, xs); + if (htmenu_internal_CheckAddItem(s, sub_tree, is_horizontal, is_popup, 0, row_h, &mcnt, name, xs) != 0) goto end; } /*wgtrGetPropertyValue(sub_tree,"outer_type",DATA_T_STRING,POD(&ptr)); if (!strcmp(ptr,"widget/menuitem") || !strcmp(ptr,"widget/menu")) @@ -398,19 +542,21 @@ htmenuRender(pHtSession s, pWgtrNode menu, int z) }*/ } if (is_horizontal) - htmenu_internal_AddDot(s, mcnt, name, is_horizontal, 1); + { + if (htmenu_internal_AddDot(s, mcnt, name, is_horizontal, 1) != 0) goto end; + } else { - htrAddBodyItem(s, ""); - htmenu_internal_AddDot(s, mcnt, name, is_horizontal, 1); - htrAddBodyItem(s, ""); + if (htrAddBodyItem(s, "") != 0) goto end; + if (htmenu_internal_AddDot(s, mcnt, name, is_horizontal, 1) != 0) goto end; + if (htrAddBodyItem(s, "") != 0) goto end; } mcnt++; - htrAddBodyItem_va(s,"%[%]
\n"); - htrAddBodyItem(s,"\n"); + if (htrAddBodyItem(s,"\n"); - } - htrAddBodyItem(s,"
\n") != 0) goto end; + if (htrAddBodyItem(s,"\n") != 0) goto end; for (i=0;iChildren), i); @@ -420,12 +566,12 @@ htmenuRender(pHtSession s, pWgtrNode menu, int z) for(j=0;jChildren), j); - htmenu_internal_CheckAddItem(s, sub_tree_child, is_horizontal, is_popup, 1, row_h, &mcnt, name, xs); + if (htmenu_internal_CheckAddItem(s, sub_tree_child, is_horizontal, is_popup, 1, row_h, &mcnt, name, xs) != 0) goto end; } } else { - htmenu_internal_CheckAddItem(s, sub_tree, is_horizontal, is_popup, 1, row_h, &mcnt, name, xs); + if (htmenu_internal_CheckAddItem(s, sub_tree, is_horizontal, is_popup, 1, row_h, &mcnt, name, xs) != 0) goto end; } /*wgtrGetPropertyValue(sub_tree,"outer_type",DATA_T_STRING,POD(&ptr)); if (!strcmp(ptr,"widget/menuitem") || !strcmp(ptr,"widget/menu")) @@ -443,60 +589,32 @@ htmenuRender(pHtSession s, pWgtrNode menu, int z) mcnt++; }*/ } - htmenu_internal_AddDot(s, mcnt, name, is_horizontal, 1); + if (htmenu_internal_AddDot(s, mcnt, name, is_horizontal, 1) != 0) goto end; mcnt++; - htrAddBodyItem(s,"
\n"); - - /** closing border for NS4 **/ - if (s->Capabilities.Dom0NS) - { - if (h != -1) - htrAddBodyItem_va(s,"
\n") != 0) goto end; + if (htrAddBodyItem_va(s,"
\n", id) != 0) goto end; - /** Ending of layer **/ - if (s->Capabilities.Dom0NS) - htrAddBodyItem_va(s,"
", id); - else - htrAddBodyItem_va(s,"
\n", id); + /** Render children. **/ + if (htrRenderSubwidgets(s, menu, z + 1) != 0) goto end; - xsDeInit(xs); - nmFree(xs, sizeof(XString)); + /** Success. **/ + rval = 0; - /* Read and initialize the menu items */ - /*cnt = xaCount(&(menu->Children)); - for (i=0;iChildren), i); - wgtrGetPropertyValue(sub_tree,"outer_type",DATA_T_STRING,POD(&ptr)); - if (!strcmp(ptr,"widget/menuitem")) - { - htrRenderSubwidgets(s, sub_tree, z+1); - } - else if (!strcmp(ptr,"widget/menusep")) - { - sub_tree->RenderFlags |= HT_WGTF_NOOBJECT; - } - else - { - htrRenderWidget(s, sub_tree, z+1); - } - }*/ - htrRenderSubwidgets(s, menu, z+1); - - return 0; + mssError(0, "HTMENU", + "Failed to render \"%s\":\"%s\" (id: %d).", + menu->Name, menu->Type, id + ); + } + + /** Clean up. **/ + if (xs != NULL) xsFree(xs); + + return rval; } int @@ -553,16 +671,6 @@ htmenuInitialize() strcpy(drv->WidgetName,"menu"); drv->Render = htmenuRender; - /** Register events **/ - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - htrAddEvent(drv,"DataChange"); - htrAddEvent(drv,"GetFocus"); - htrAddEvent(drv,"LoseFocus"); - /** Register. **/ htrRegisterDriver(drv); @@ -596,5 +704,3 @@ htmenuInitialize() return 0; } - - diff --git a/centrallix/htmlgen/htdrv_multiscroll.c b/centrallix/htmlgen/htdrv_multiscroll.c index c9c5676f0..b99a248c4 100644 --- a/centrallix/htmlgen/htdrv_multiscroll.c +++ b/centrallix/htmlgen/htdrv_multiscroll.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2007 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -43,6 +31,14 @@ /* when it comes to scrolling. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -51,6 +47,8 @@ static struct } HTMS; +#define HTMS_MAX_PARTS 32 + /*** htmsRender - generate the HTML code for the page. ***/ @@ -60,23 +58,20 @@ htmsRender(pHtSession s, pWgtrNode tree, int z) char* ptr; char name[64]; char main_bg[128]; - int x=-1,y=-1,w,h,ch,total_h,top_h,total_h_accum,cy; - int id; - int i, cnt; int always_visible; - pWgtrNode childlist[32]; - pWgtrNode child; - if(!s->Capabilities.Dom0NS && !(s->Capabilities.Dom1HTML && s->Capabilities.CSS1)) + /** Get an id for this. **/ + const int id = (HTMS.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTMS","Netscape DOM or W3C DOM1 HTML and CSS support required"); - return -1; + mssError(1, "HTMS", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTMS.idcnt++); - /** Get x,y,w,h of this object **/ + int x = -1, y = -1, w, h; if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) @@ -98,86 +93,190 @@ htmsRender(pHtSession s, pWgtrNode tree, int z) strtcpy(name,ptr,sizeof(name)); /** Ok, write the style header items. **/ - if(s->Capabilities.Dom0NS) - { - htrAddStylesheetItem_va(s,"\t#ms%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,h,z); - } - else if(s->Capabilities.CSS1) + if (htrAddStylesheetItem_va(s, + "\t\t#ms%POSmain { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "%STR " + "}\n", + id, x, y, w, h, z, + main_bg + ) != 0) { - htrAddStylesheetItem_va(s,"\t#ms%POSmain { POSITION:absolute; VISIBILITY:inherit; overflow:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,h,z); - htrAddStylesheetItem_va(s,"\t#ms%POSmain { %STR}\n",id,main_bg); - } - else - { - mssError(1,"HTMS","Cannot render - environment unsupported"); + mssError(0, "HTMS", "Failed to write main MultiScroll CSS."); + goto err; } - /** DOM linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "ms%POSmain",id); + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "ms%POSmain", id)) goto err; - /** Script include call **/ - htrAddScriptInclude(s, "/sys/js/htdrv_multiscroll.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); + /** Include scripts. **/ + if (htrAddScriptInclude(s, "/sys/js/htdrv_multiscroll.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0) != 0) goto err; - /** Event Handlers **/ - htrAddEventHandlerFunction(s, "document","MOUSEUP", "ms", "ms_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "ms", "ms_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER", "ms", "ms_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEOUT", "ms", "ms_mouseout"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "ms", "ms_mousemove"); + /** Add event handlers. **/ + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "ms", "ms_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "ms", "ms_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "ms", "ms_mouseout") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "ms", "ms_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "ms", "ms_mouseup") != 0) goto err; /** Script init **/ - htrAddScriptInit_va(s, " ms_init(wgtrGetNodeRef(ns,'%STR&SYM'), {});\n", - name); + if (htrAddScriptInit_va(s, + "\tms_init(wgtrGetNodeRef(ns, '%STR&SYM'), {});\n", + name + ) != 0) + { + mssError(0, "HTMS", "Failed to write JS init call."); + goto err; + } /** div for the main body of this widget **/ - htrAddBodyItem_va(s,"
\n",id); + if (htrAddBodyItem_va(s, "
\n", id) != 0) + { + mssError(0, "HTMS", "Failed to write HTML opening tag."); + goto err; + } - /** Check for objects within the widget. **/ - cnt = wgtrGetMatchingChildList(tree, "widget/multiscrollpart", childlist, sizeof(childlist)/sizeof(pWgtrNode)); - top_h = total_h = 0; - for(i=0;i\n",id,i); - always_visible = htrGetBoolean(child, "always_visible", 0); + pWgtrNode child = childlist[i]; + + /** Height computations. **/ + int ch; if (wgtrGetPropertyValue(child,"height",DATA_T_INTEGER,POD(&ch)) != 0 || ch < 0) ch = 0; - if (top_h >= total_h_accum + ch) - cy = total_h_accum; - else - cy = h - total_h + total_h_accum; + const int cy = (top_h >= total_h_accum + ch) ? total_h_accum : h - total_h + total_h_accum; total_h_accum += ch; - htrAddStylesheetItem_va(s,"\t#ms%POSpart%POS { POSITION:absolute; VISIBILITY:inherit; overflow:hidden; LEFT:0px; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%INTpx,%INTpx,0px);}\n",id,i,cy,w,ch,z+1,w,ch); - if (wgtrGetPropertyValue(child,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + + /** Property handling. **/ + if (wgtrGetPropertyValue(child, "name", DATA_T_STRING,POD(&ptr)) != 0) + { + mssError(1, "HTMS", "Failed to get part name."); + goto err_part; + } + always_visible = htrGetBoolean(child, "always_visible", 0); if (always_visible && ch == 0) { - mssError(1, "HTMS", "MultiScroll part '%s' of '%s' set to always_visible=true must have a height", ptr, name); - return -1; + mssError(1, "HTMS", "Fail: MultiScroll parts with always_visible=true must have a height."); + goto err_part; + } + + /** Write CSS styles. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#ms%POSpart%POS { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:0px; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "clip:rect(0px, %INTpx, %INTpx, 0px); " + "}\n", + id, i, cy, w, ch, z + 1, w, ch + ) != 0) + { + mssError(0, "HTMS", "Failed to write CSS."); + goto err_part; + } + + /** Write JS initialization call. **/ + if (htrAddScriptInit_va(s, + "\twgtrGetNodeRef(ns, '%STR&SYM')" + ".addPart(wgtrGetNodeRef('%STR&SYM','%STR&SYM'), {av:%INT, h:%INT, y:%INT});\n", + name, + wgtrGetNamespace(child), ptr, always_visible, ch, cy + ) != 0) + { + mssError(0, "HTMS", "Failed to write JS init call."); + goto err_part; } - htrAddScriptInit_va(s, " wgtrGetNodeRef(ns,'%STR&SYM').addPart(wgtrGetNodeRef('%STR&SYM','%STR&SYM'), {av:%INT, h:%INT, y:%INT});\n", - name, wgtrGetNamespace(child), ptr, always_visible, ch, cy); - htrRenderSubwidgets(s, child, z+2); - htrAddBodyItem(s, "
\n"); + + /** Write linked HTML opening tag. **/ + if (htrAddWgtrObjLinkage_va(s, child, "ms%POSpart%POS", id, i) != 0) goto err_part; + if (htrAddBodyItem_va(s, "
\n", id, i) != 0) + { + mssError(0, "HTMS", "Failed to write HTML opening tag."); + goto err_part; + } + + /** Write subwidgets. **/ + if (htrRenderSubwidgets(s, child, z + 2) != 0) + { + mssError(0, "HTMS", "Failed to render child widgets of MultiScroll part."); + goto err_part; + } + + /** Write HTML closing tag. **/ + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTMS", "Failed to write HTML closing tag."); + goto err_part; + } + + /** Success, go to next. **/ + continue; + + err_part: /* Error handling. */ + mssError(0, "HTMS", + "Failed to render MultiScroll part #%u, aka. \"%s\":\"%s\".", + i + 1u, child->Name, child->Type + ); + goto err; } - htrRenderSubwidgets(s, tree, z+1); + + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto err; /** End the containing layer. **/ - htrAddBodyItem(s, "
\n"); + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTMS", "Failed to write HTML closing tag."); + goto err; + } - return 0; + /** Success. **/ + return 0; + + err: + mssError(0, "HTMS", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } -/*** htmsRender_part - renderer for the multiscroll part subwidget. +/*** htmsRender_part - renderer for the MultiScroll part subwidget. ***/ int htmsRender_part(pHtSession s, pWgtrNode tree, int z) @@ -202,13 +301,6 @@ htmsInitialize() strcpy(drv->WidgetName,"multiscroll"); drv->Render = htmsRender; - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_objcanvas.c b/centrallix/htmlgen/htdrv_objcanvas.c index 6ff6078e8..bd093dde0 100644 --- a/centrallix/htmlgen/htdrv_objcanvas.c +++ b/centrallix/htmlgen/htdrv_objcanvas.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 2004 LightSys Technology Services, Inc. */ +/* Copyright (C) 2004-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,14 @@ /* Description: HTML Widget driver for the 'object canvas' */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -59,30 +55,30 @@ htocRender(pHtSession s, pWgtrNode oc_node, int z) char main_bg[128]; char osrc[64]; int x=-1,y=-1,w,h; - int id; int allow_select, show_select; - if(!s->Capabilities.Dom0NS && !(s->Capabilities.Dom1HTML && s->Capabilities.CSS1)) + /** Get an id for this. **/ + const int id = (HTOC.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTOC","Netscape DOM or W3C DOM1 HTML and CSS support required"); - return -1; + mssError(1, "HTOC", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTOC.idcnt++); - /** Get x,y,w,h of this object **/ if (wgtrGetPropertyValue(oc_node,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; if (wgtrGetPropertyValue(oc_node,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; if (wgtrGetPropertyValue(oc_node,"width",DATA_T_INTEGER,POD(&w)) != 0) { mssError(1,"HTOC","Pane widget must have a 'width' property"); - return -1; + goto err; } if (wgtrGetPropertyValue(oc_node,"height",DATA_T_INTEGER,POD(&h)) != 0) { mssError(1,"HTOC","Pane widget must have a 'height' property"); - return -1; + goto err; } /** Background color/image? **/ @@ -96,50 +92,99 @@ htocRender(pHtSession s, pWgtrNode oc_node, int z) /** allow selection of objects? **/ allow_select = htrGetBoolean(oc_node, "allow_selection", 0); + if (allow_select < 0) goto err; /** show current selection? **/ show_select = htrGetBoolean(oc_node, "show_selection", 0); + if (show_select < 0) goto err; /** Get name **/ - if (wgtrGetPropertyValue(oc_node,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(oc_node,"name",DATA_T_STRING,POD(&ptr)) != 0) goto err; strtcpy(name,ptr,sizeof(name)); /** Add css item for the layer **/ - if (s->Capabilities.CSS2) - htrAddStylesheetItem_va(s,"\t#oc%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; overflow: hidden; %STR}\n",id,x,y,w,h,z,main_bg); - else - htrAddStylesheetItem_va(s,"\t#oc%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; HEIGHT:%POS; Z-INDEX:%POS; }\n",id,x,y,w,h,z); + if (htrAddStylesheetItem_va(s, + "\t\t#oc%POSbase { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "%STR " + "}\n", + id, + ht_flex_x(x, oc_node), + ht_flex_y(y, oc_node), + ht_flex_w(w, oc_node), + ht_flex_h(h, oc_node), + z, + main_bg + ) != 0) + { + mssError(0, "HTOC", "Failed to write base CSS."); + goto err; + } + + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, oc_node, "oc%POSbase", id)) goto err; - htrAddWgtrObjLinkage_va(s, oc_node, "oc%POSbase",id); + /** Include scripts. **/ + if (htrAddScriptInclude(s, "/sys/js/htdrv_objcanvas.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0) != 0) goto err; - /** Include our necessary supporting js files **/ - htrAddScriptInclude(s, "/sys/js/htdrv_objcanvas.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); + /** Add event handlers. **/ + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "oc", "oc_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "oc", "oc_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "oc", "oc_mouseout") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "oc", "oc_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "oc", "oc_mouseup") != 0) goto err; - /** Event Handlers **/ - htrAddEventHandlerFunction(s, "document", "MOUSEUP", "oc", "oc_mouseup"); - htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "oc", "oc_mousedown"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "oc", "oc_mouseover"); - htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "oc", "oc_mousemove"); - htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "oc", "oc_mouseout"); - /** Script initialization call. **/ - htrAddScriptInit_va(s, " oc_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), osrc:%[wgtrGetNodeRef(ns,\"%STR&SYM\")%]%[null%], allow_select:%INT, show_select:%INT, name:\"%STR&SYM\"});\n", - name, *osrc, osrc, !*osrc, allow_select, show_select, name); + if (htrAddScriptInit_va(s, + "\toc_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "osrc:%[wgtrGetNodeRef(ns, '%STR&SYM')%]%[null%], " + "name:'%STR&SYM', " + "allow_select:%INT, " + "show_select:%INT, " + "});\n", + name, (osrc[0] != '\0'), osrc, (osrc[0] == '\0'), name, + allow_select, show_select + ) != 0) + { + mssError(0, "HTOC", "Failed to write JS init call."); + goto err; + } /** HTML body
element for the base layer. **/ - htrAddBodyItem_va(s,"
\n",id); - if (!s->Capabilities.CSS2) - htrAddBodyItem_va(s,"
 
\n",main_bg,w); + if (htrAddBodyItem_va(s,"
\n", id) != 0) + { + mssError(0, "HTOC", "Failed to write HTML opening tag."); + goto err; + } - /** Check for objects within the pane. **/ - htrRenderSubwidgets(s, oc_node, z+2); + /** Render children. **/ + if (htrRenderSubwidgets(s, oc_node, z + 2) != 0) goto err; /** End the containing layer. **/ - if (!s->Capabilities.CSS2) htrAddBodyItem(s, ""); - htrAddBodyItem(s, "
\n"); + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTOC", "Failed to write HTML closing tag."); + goto err; + } - return 0; + /** Success. **/ + return 0; + + err: + mssError(0, "HTOC", + "Failed to render \"%s\":\"%s\" (id: %d).", + oc_node->Name, oc_node->Type, id + ); + return -1; } @@ -159,13 +204,6 @@ htocInitialize() strcpy(drv->WidgetName,"objcanvas"); drv->Render = htocRender; - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_osrc.c b/centrallix/htmlgen/htdrv_osrc.c index 9ba4bbfd9..3b3a0e97f 100644 --- a/centrallix/htmlgen/htdrv_osrc.c +++ b/centrallix/htmlgen/htdrv_osrc.c @@ -1,22 +1,10 @@ /* vim: set sw=3: */ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 2000-2007 LightSys Technology Services, Inc. */ +/* Copyright (C) 2000-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -42,6 +30,17 @@ /* Description: HTML Widget driver for an object system */ /************************************************************************/ +#include +#include + +#include "cxlib/check.h" +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/newmalloc.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct { @@ -50,69 +49,6 @@ static struct { enum htosrc_autoquery_types { Unset=-1, Never=0, OnLoad=1, OnFirstReveal=2, OnEachReveal=3 }; -#if 00 -/*** AddRule: add a declarative osrc rule to the generated document. - ***/ -int -htosrc_internal_AddRule(pHtSession s, pWgtrNode tree, char* treename, pWgtrNode sub_tree) - { - char ruletype[32]; - int query_delay; - int min_chars; - int trailing_wildcard; - int leading_wildcard; - char fieldname[64]; - char* ptr; - pExpression code; - - /** Get type of rule **/ - if (wgtrGetPropertyValue(sub_tree, "ruletype", DATA_T_STRING, POD(&ptr)) != 0) - { - mssError(1, "HTOSRC", "ruletype is required for widget/osrc-rule"); - return -1; - } - strtcpy(ruletype, ptr, sizeof(ruletype)); - - /** Handle the rule based on the type **/ - if (!strcmp(ruletype, "relationship")) - { - } - else if (!strcmp(ruletype, "filter")) - { - trailing_wildcard = htrGetBoolean(sub_tree, "trailing_wildcard", 1); - leading_wildcard = htrGetBoolean(sub_tree, "leading_wildcard", 0); - if (wgtrGetPropertyValue(sub_tree, "min_chars", DATA_T_INTEGER, POD(&min_chars)) != 0) - min_chars = 3; - if (wgtrGetPropertyValue(sub_tree, "query_delay", DATA_T_INTEGER, POD(&query_delay)) != 0) - query_delay = 500; - if (wgtrGetPropertyValue(sub_tree, "fieldname", DATA_T_STRING, POD(&ptr)) == 0) - strtcpy(fieldname, ptr, sizeof(fieldname)); - else - { - mssError(1, "HTOSRC", "fieldname is required for widget/osrc-rule of type 'filter'"); - return -1; - } - - /** Get target **/ - if (wgtrGetPropertyType(sub_tree,"value") == DATA_T_CODE) - { - wgtrGetPropertyValue(sub_tree,"value",DATA_T_CODE,POD(&code)); - htrAddExpression(s, treename, wgtrGetDName(sub_tree), code); - } - - /** Write the init line **/ - htrAddScriptInit_va(s, " nodes[\"%STR&SYM\"].AddRule('%STR&SYM', {dname:'%STR&SYM', field:'%STR&ESCQ', qd:%INT, mc:%INT, tw:%INT, lw:%INT});\n", - treename, ruletype, wgtrGetDName(sub_tree), fieldname, - query_delay, min_chars, trailing_wildcard, leading_wildcard); - } - else if (!strcmp(ruletype, "keying")) - { - } - - return 0; - } -#endif - /* htosrcRender - generate the HTML code for the page. @@ -121,35 +57,34 @@ htosrc_internal_AddRule(pHtSession s, pWgtrNode tree, char* treename, pWgtrNode int htosrcRender(pHtSession s, pWgtrNode tree, int z) { - int id; char name[40]; char *ptr; int readahead; int scrollahead; int replicasize; - char *sql; - char *filter; - char *baseobj; - pWgtrNode sub_tree; -// pObjQuery qy; + char* sql = NULL; + char* filter = NULL; + char* baseobj = NULL; enum htosrc_autoquery_types aq; int receive_updates; int send_updates; int refresh_interval; - int count, i; + int rval = -1; int ind_activity; int use_having; int qy_reveal_only; char key_objname[32]; + char* no_string = ""; - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML) - { - mssError(1,"HTOSRC","Netscape DOM or W3C DOM1 HTML support required"); - return -1; - } + /** Get an id for this. **/ + const int id = (HTOSRC.idcnt++); - /** Get an id for this. **/ - id = (HTOSRC.idcnt++); + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) + { + mssError(1, "HTOSRC", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto end; + } /** Get name **/ if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; @@ -213,85 +148,136 @@ htosrcRender(pHtSession s, pWgtrNode tree, int z) /** Get replication updates from server? **/ receive_updates = htrGetBoolean(tree, "receive_updates", 0); + if (receive_updates < 0) goto end; /** Send updates to the server? **/ send_updates = htrGetBoolean(tree, "send_updates", 1); + if (send_updates < 0) goto end; - if (wgtrGetPropertyValue(tree,"sql",DATA_T_STRING,POD(&ptr)) == 0) - { - sql = nmSysStrdup(ptr); - } - else - { - if (wgtrGetPropertyType(tree,"sql") != DATA_T_CODE) - { - mssError(1,"HTOSRC","'sql' parameter required for objectsource '%s'", name); - return -1; - } - else - sql = nmSysStrdup(""); - } - - if (wgtrGetPropertyValue(tree,"baseobj",DATA_T_STRING,POD(&ptr)) == 0) - baseobj = nmSysStrdup(ptr); - else - baseobj = NULL; - - if (wgtrGetPropertyValue(tree,"filter",DATA_T_STRING,POD(&ptr)) == 0) - filter = nmSysStrdup(ptr); - else - filter = nmSysStrdup(""); - - /** create our instance variable **/ - htrAddWgtrObjLinkage_va(s, tree, "osrc%POSloader",id); - htrAddWgtrCtrLinkage(s, tree, "_parentctr"); + /** Get osrc strings. **/ + const int sql_datatype = wgtrGetPropertyType(tree, "sql"); + switch (sql_datatype) + { + case DATA_T_CODE: sql = no_string; break; + case DATA_T_STRING: + { + if (wgtrGetPropertyValue(tree, "sql", DATA_T_STRING, POD(&ptr)) == 0) + { + sql = checkPtr(nmSysStrdup(ptr)); + break; + } + /** Fallthrough **/ + } + default: + mssError(1, "HTOSRC", + "'sql' parameter of type string or code required for object source: \"%s\", but type code was %d", + name, sql_datatype + ); + return -1; + } - htrAddScriptGlobal(s, "osrc_syncid", "0", 0); - htrAddScriptGlobal(s, "osrc_relationships", "[]", 0); + baseobj = (wgtrGetPropertyValue(tree, "baseobj", DATA_T_STRING, POD(&ptr)) == 0) ? checkPtr(nmSysStrdup(ptr)) : no_string; + filter = (wgtrGetPropertyValue(tree, "filter", DATA_T_STRING, POD(&ptr)) == 0) ? checkPtr(nmSysStrdup(ptr)) : no_string; + if (sql == NULL || baseobj == NULL || filter == NULL) goto end; + + /** Link the widget and container to their DOM nodes. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "osrc%POSloader", id) != 0) goto end; + if (htrAddWgtrCtrLinkage(s, tree, "_parentctr") != 0) goto end; + + /** Write JS globals & includes. **/ + if (htrAddScriptGlobal(s, "osrc_relationships", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "osrc_syncid", "0", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/htdrv_osrc.js", 0) != 0) goto end; + + /** Write CSS. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#osrc%POSloader { " + "position:absolute; " + "visibility:hidden; " + "overflow:hidden; " + "left:0px; " + "top:1px; " + "width:1px; " + "height:1px; " + "z-index:0; " + "}\n", + id + ) != 0) + { + mssError(0, "HTOSRC", "Failed to write loader CSS."); + goto end; + } - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s," #osrc%POSloader { overflow:hidden; POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:1px; WIDTH:1px; HEIGHT:1px; Z-INDEX:0; }\n",id); + /** Script initialization call. **/ + if (htrAddScriptInit_va(s, + "\tosrc_init({ " + "loader:wgtrGetNodeRef(ns, '%STR&SYM'), " + "name:'%STR&SYM', " + "readahead:%INT, " + "scrollahead:%INT, " + "replicasize:%INT, " + "sql:'%STR&JSSTR', " + "filter:'%STR&JSSTR', " + "baseobj:'%STR&JSSTR', " + "autoquery:%INT, " + "requestupdates:%INT, " + "ind_act:%INT, " + "use_having:%INT, " + "qy_reveal_only:%INT, " + "send_updates:%INT, " + "key_objname:'%STR&JSSTR', " + "refresh:%INT, " + "});\n", + name, name, readahead, scrollahead, replicasize, sql, filter, baseobj, + aq, receive_updates, ind_activity, use_having, qy_reveal_only, + send_updates, key_objname, refresh_interval + ) != 0) + { + mssError(0, "HTOSRC", "Failed to write JS init call."); + goto end; + } - /** Script initialization call. **/ - htrAddScriptInit_va(s," osrc_init({loader:wgtrGetNodeRef(ns,\"%STR&SYM\"), readahead:%INT, scrollahead:%INT, replicasize:%INT, sql:\"%STR&JSSTR\", filter:\"%STR&JSSTR\", baseobj:\"%STR&JSSTR\", name:\"%STR&SYM\", autoquery:%INT, requestupdates:%INT, ind_act:%INT, use_having:%INT, qy_reveal_only:%INT, send_updates:%INT, key_objname:\"%STR&JSSTR\", refresh:%INT});\n", - name,readahead,scrollahead,replicasize,sql,filter, - baseobj?baseobj:"",name,aq,receive_updates, ind_activity, - use_having, qy_reveal_only, send_updates, key_objname, refresh_interval); - //htrAddScriptCleanup_va(s," %s.layers.osrc%dloader.cleanup();\n", parentname, id); + /** HTML body element for the frame **/ + if (htrAddBodyItemLayerStart(s, HTR_LAYER_F_DYNAMIC, "osrc%POSloader", id, NULL) != 0) + { + mssError(0, "HTOSRC", "Failed to write HTML opening tag for osrc loader."); + goto end; + } + if (htrAddBodyItemLayerEnd(s, HTR_LAYER_F_DYNAMIC) != 0) + { + mssError(0, "HTOSRC", "Failed to write HTML closing tag for osrc loader."); + goto end; + } + if (htrAddBodyItem(s, "\n") != 0) + { + mssError(0, "HTOSRC", "Failed to write HTML newline."); + goto end; + } - htrAddScriptInclude(s, "/sys/js/htdrv_osrc.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z) != 0) goto end; - /** HTML body element for the frame **/ - htrAddBodyItemLayerStart(s,HTR_LAYER_F_DYNAMIC,"osrc%POSloader",id, NULL); - htrAddBodyItemLayerEnd(s,HTR_LAYER_F_DYNAMIC); - htrAddBodyItem(s, "\n"); + /** Success. **/ + rval = 0; - count = xaCount(&(tree->Children)); - for (i=0;iChildren), i); -#if 00 - if (wgtrGetPropertyValue(sub_tree, "outer_type", DATA_T_STRING, POD(&ptr)) == 0 && !strcmp(ptr, "widget/osrc-rule")) - { - htosrc_internal_AddRule(s, tree, name, sub_tree); - } - else - { -#endif - htrRenderWidget(s, sub_tree, z); -#if 00 - } -#endif + mssError(0, "HTOSRC", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); } - nmSysFree(filter); - if (baseobj) nmSysFree(baseobj); - nmSysFree(sql); + /** Clean up. **/ + if (filter != NULL && filter != no_string) nmSysFree(filter); + if (baseobj != NULL && baseobj != no_string) nmSysFree(baseobj); + if (sql != NULL && sql != no_string) nmSysFree(sql); - return 0; -} + return rval; + } /* @@ -309,16 +295,6 @@ int htosrcInitialize() { strcpy(drv->WidgetName,"osrc"); drv->Render = htosrcRender; - /** Add actions **/ - htrAddAction(drv,"Clear"); - htrAddAction(drv,"Query"); - htrAddAction(drv,"Delete"); - htrAddAction(drv,"Create"); - htrAddAction(drv,"Modify"); - - htrAddAction(drv,"Sync"); - htrAddAction(drv,"ReverseSync"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_page.c b/centrallix/htmlgen/htdrv_page.c index e79f7b47e..62e7486c2 100755 --- a/centrallix/htmlgen/htdrv_page.c +++ b/centrallix/htmlgen/htdrv_page.c @@ -1,25 +1,8 @@ -#include -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "centrallix.h" -#include "wgtr.h" -#include "iface.h" -#include "stparse.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2004 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -45,6 +28,24 @@ /* Description: HTML Widget driver for the overall HTML page. */ /************************************************************************/ +#include +#include +#include +#include +#include + +#include "centrallix.h" +#include "cxlib/check.h" +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/newmalloc.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "cxss/cxss.h" +#include "ht_render.h" +#include "stparse.h" +#include "stparse_ne.h" +#include "wgtr.h" int @@ -53,9 +54,9 @@ htpageRender(pHtSession s, pWgtrNode tree, int z) char *ptr; char name[64]; int attract = 0; - int watchdogtimer; + int watchdogtimer = 180; char bgstr[128]; - int show, i, count; + int show, i; char kbfocus1[64]; /* kb focus = 3d raised */ char kbfocus2[64]; char msfocus1[64]; /* ms focus = black rectangle */ @@ -68,20 +69,22 @@ htpageRender(pHtSession s, pWgtrNode tree, int z) int dpi_scaling = 0; int w,h; char* path; - pStruct c_param; time_t t; struct tm* timeptr; char timestr[80]; - XArray endorsements; - XArray contexts; + pXArray endorsements = NULL; + pXArray contexts = NULL; int max_requests = 1; + int rval = -1; - if(!((s->Capabilities.Dom0NS || s->Capabilities.Dom0IE || (s->Capabilities.Dom1HTML && s->Capabilities.Dom2Events)) && s->Capabilities.CSS1) ) + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTPAGE","CSS Level 1 Support and (Netscape DOM support or (W3C Level 1 DOM support and W3C Level 2 Events support required))"); - return -1; + mssError(1, "HTPAGE", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto end; } + /** Set default focus colors. **/ strcpy(msfocus1,"#ffffff"); /* ms focus = 3d raised */ strcpy(msfocus2,"#7a7a7a"); strcpy(kbfocus1,"#000000"); /* kb focus = black rectangle */ @@ -91,7 +94,11 @@ htpageRender(pHtSession s, pWgtrNode tree, int z) /** If not at top-level, don't render the page. **/ /** Z is set to 10 for the top-level page. **/ - if (z != 10) return 0; + if (z != 10) + { + rval = 0; + goto end; + } /** These are always set for a page widget **/ wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)); @@ -100,20 +107,29 @@ htpageRender(pHtSession s, pWgtrNode tree, int z) /** Max active server requests at one time **/ wgtrGetPropertyValue(tree,"max_requests",DATA_T_INTEGER,POD(&max_requests)); - /** Page icon? **/ + /** Handle page icon. **/ if (wgtrGetPropertyValue(tree, "icon", DATA_T_STRING, POD(&ptr)) == 0) { - htrAddHeaderItem_va(s, " \n",ptr); + if (htrAddHeaderItem_va(s, "\t\n",ptr) != 0) + { + mssError(0, "HTPAGE", "Failed to write HTML for page icon."); + goto end; + } } - /** Check for a title. **/ - if (wgtrGetPropertyValue(tree,"title",DATA_T_STRING,POD(&ptr)) == 0) + /** Handle page title. **/ + if (wgtrGetPropertyValue(tree, "title", DATA_T_STRING, POD(&ptr)) == 0) { - htrAddHeaderItem_va(s, " %STR&HTE\n",ptr); + if (htrAddHeaderItem_va(s, "\t%STR&HTE\n", ptr) != 0) + { + mssError(0, "HTPAGE", "Failed to write HTML for page title."); + goto end; + } } /** Check for page load status **/ show = htrGetBoolean(tree, "loadstatus", 0); + if (show < 0) goto end; /** Initialize the html-related interface stuff **/ if (ifcHtmlInit(s, tree) < 0) @@ -122,25 +138,50 @@ htpageRender(pHtSession s, pWgtrNode tree, int z) } /** Auto-call startup and cleanup **/ - htrAddBodyParam_va(s, " onLoad=\"startup_%STR&SYM();\" onUnload=\"cleanup();\"", - s->Namespace->DName); + if (htrAddBodyParam_va(s, " onLoad='startup_%STR&SYM();'", s->Namespace->DName) != 0) + { + mssError(0, "HTPAGE", "Failed to register startup function."); + goto end; + } + if (htrAddBodyParam(s, " onUnload='cleanup();'") != 0) + { + mssError(0, "HTPAGE", "Failed to register cleanup function."); + goto end; + } + if (htrAddScriptCleanup(s, "\tpg_cleanup();\n") != 0) + { + mssError(0, "HTPAGE", "Failed to write page cleanup JS."); + goto end; + } /** Check for bgcolor. **/ - if (htrGetBackground(tree, NULL, 0, bgstr, sizeof(bgstr)) == 0) + if (htrGetBackground(tree, NULL, 1, bgstr, sizeof(bgstr)) == 0) { - htrAddBodyParam_va(s, " %STR",bgstr); + if (htrAddStylesheetItem_va(s, "\t\tbody { %STR }\n", bgstr) != 0) + { + mssError(0, "HTPAGE", "Failed to write page background color."); + goto end; + } } /** Check for text color **/ if (wgtrGetPropertyValue(tree,"textcolor",DATA_T_STRING,POD(&ptr)) == 0) { - htrAddBodyParam_va(s, " TEXT=\"%STR&HTE\"",ptr); + if (htrAddBodyParam_va(s, " text=\"%STR&HTE\"", ptr) != 0) + { + mssError(0, "HTPAGE", "Failed to write page text color."); + goto end; + } } /** Check for link color **/ if (wgtrGetPropertyValue(tree,"linkcolor",DATA_T_STRING,POD(&ptr)) == 0) { - htrAddBodyParam_va(s, " LINK=\"%STR&HTE\"",ptr); + if (htrAddStylesheetItem_va(s, "\t\ta:link { color:%STR&HTE; }\n", ptr) != 0) + { + mssError(0, "HTPAGE", "Failed to write page link color."); + goto end; + } } /** Keyboard Focus Indicator colors 1 and 2 **/ @@ -177,95 +218,101 @@ htpageRender(pHtSession s, pWgtrNode tree, int z) if (wgtrGetPropertyValue(tree,"attract",DATA_T_INTEGER,POD(&ptr)) == 0) attract = (intptr_t)ptr; + /** Get booleans. **/ show_diag = htrGetBoolean(tree, "show_diagnostics", 0); dpi_scaling = htrGetBoolean(tree, "dpi_scaling", 0); + /** Get font data. **/ wgtrGetPropertyValue(tree, "font_size", DATA_T_INTEGER, POD(&font_size)); if (font_size < 5 || font_size > 100) font_size = 12; - if (wgtrGetPropertyValue(tree, "font_name", DATA_T_STRING, POD(&ptr)) == 0 && ptr) strtcpy(font_name, ptr, sizeof(font_name)); else strcpy(font_name, ""); /** Add global for page metadata **/ - htrAddScriptGlobal(s, "page", "{}", 0); + if (htrAddScriptGlobal(s, "page", "{}", 0) != 0) goto end; /** Add a list of highlightable areas **/ /** These are javascript global variables**/ - htrAddScriptGlobal(s, "pg_arealist", "[]", 0); - htrAddScriptGlobal(s, "pg_keylist", "[]", 0); - htrAddScriptGlobal(s, "pg_curarea", "null", 0); - htrAddScriptGlobal(s, "pg_curlayer", "null", 0); - htrAddScriptGlobal(s, "pg_curkbdlayer", "null", 0); - htrAddScriptGlobal(s, "pg_curkbdarea", "null", 0); - htrAddScriptGlobal(s, "pg_lastkey", "-1", 0); - htrAddScriptGlobal(s, "pg_lastmodifiers", "null", 0); - htrAddScriptGlobal(s, "pg_keytimeoutid", "null", 0); - htrAddScriptGlobal(s, "pg_keyschedid", "0", 0); - htrAddScriptGlobal(s, "pg_modallayer", "null", 0); - htrAddScriptGlobal(s, "pg_key_ie_shifted", "false", 0); - htrAddScriptGlobal(s, "pg_attract", "null", 0); - htrAddScriptGlobal(s, "pg_gshade", "null", 0); - htrAddScriptGlobal(s, "pg_closetype", "null", 0); - htrAddScriptGlobal(s, "pg_explist", "[]", 0); - htrAddScriptGlobal(s, "pg_schedtimeout", "null", 0); - htrAddScriptGlobal(s, "pg_schedtimeoutlist", "[]", 0); - htrAddScriptGlobal(s, "pg_schedtimeoutid", "0", 0); - htrAddScriptGlobal(s, "pg_schedtimeoutstamp", "0", 0); - htrAddScriptGlobal(s, "pg_insame", "false", 0); - htrAddScriptGlobal(s, "cn_browser", "null", 0); - htrAddScriptGlobal(s, "ibeam_current", "null", 0); - htrAddScriptGlobal(s, "util_cur_mainlayer", "null", 0); - htrAddScriptGlobal(s, "pg_loadqueue", "[]", 0); - htrAddScriptGlobal(s, "pg_loadqueue_busy", "999999", 0); - htrAddScriptGlobal(s, "pg_debug_log", "null", 0); - htrAddScriptGlobal(s, "pg_isloaded", "false", 0); - htrAddScriptGlobal(s, "pg_username", "null", 0); - htrAddScriptGlobal(s, "pg_msg_handlers", "[]", 0); - htrAddScriptGlobal(s, "pg_msg_layer", "null", 0); - htrAddScriptGlobal(s, "pg_msg_timeout", "null", 0); - htrAddScriptGlobal(s, "pg_diag", show_diag?"true":"false", 0); /* causes pop-up boxes for certain non-fatel warnings */ - htrAddScriptGlobal(s, "pg_dpi_scaling", (dpi_scaling) ? "true" : "false", 0); /* Whether to scale launched windows based on display dots-per-inch */ - htrAddScriptGlobal(s, "pg_width", "0", 0); - htrAddScriptGlobal(s, "pg_height", "0", 0); - htrAddScriptGlobal(s, "pg_charw", "0", 0); - htrAddScriptGlobal(s, "pg_charh", "0", 0); - htrAddScriptGlobal(s, "pg_parah", "0", 0); - htrAddScriptGlobal(s, "pg_namespaces", "{}", 0); - htrAddScriptGlobal(s, "pg_handlertimeout", "null", 0); /* this is used by htr_mousemovehandler */ - htrAddScriptGlobal(s, "pg_mousemoveevents", "[]", 0); - htrAddScriptGlobal(s, "pg_handlers", "[]", 0); /* keeps track of handlers for basic events tied to document (click, mousemove, keypress, etc) */ - htrAddScriptGlobal(s, "pg_capturedevents", "0", 0); /* is the binary OR of all event flags that the document currently has registered */ - htrAddScriptGlobal(s, "pg_tiplayer", "null", 0); - htrAddScriptGlobal(s, "pg_tipindex", "0", 0); - htrAddScriptGlobal(s, "pg_tiptmout", "null", 0); - htrAddScriptGlobal(s, "pg_waitlyr", "null", 0); - htrAddScriptGlobal(s, "pg_appglobals", "[]", 0); - htrAddScriptGlobal(s, "pg_sessglobals", "[]", 0); - htrAddScriptGlobal(s, "pg_scripts", "[]", 0); - htrAddScriptGlobal(s, "pg_endorsements", "[]", 0); - htrAddScriptGlobal(s, "pg_max_requests", "1", 0); - - /** Add script include to get function declarations **/ - if(s->Capabilities.JS15 && s->Capabilities.Dom1HTML) - { - /*htrAddScriptInclude(s, "/sys/js/htdrv_page_js15.js", 0);*/ - } - htrAddScriptInclude(s, "/sys/js/htdrv_page.js", 0); - htrAddScriptInclude(s, "/sys/js/htdrv_connector.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - htrAddScriptInclude(s, "/sys/js/jquery/jquery-1.11.1.js", 0); + if (htrAddScriptGlobal(s, "pg_arealist", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_keylist", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_curarea", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_curboxarea", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_curlayer", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_curkbdlayer", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_curkbdarea", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_lastkey", "-1", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_lastmodifiers", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_keytimeoutid", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_keyschedid", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_modallayer", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_key_ie_shifted", "false", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_attract", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_gshade", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_closetype", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_explist", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_schedtimeout", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_schedtimeoutlist", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_schedtimeoutid", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_schedtimeoutstamp", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_insame", "false", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "cn_browser", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "ibeam_current", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "util_cur_mainlayer", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_loadqueue", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_loadqueue_busy", "999999", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_debug_log", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_isloaded", "false", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_username", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_msg_handlers", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_msg_layer", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_msg_timeout", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_diag", (show_diag) ? "true" : "false", 0) != 0) goto end; /* Use pop-ups for certain non-fatal warnings */ + if (htrAddScriptGlobal(s, "pg_dpi_scaling", (dpi_scaling) ? "true" : "false", 0) != 0) goto end; /* Scale launched windows using display dots-per-inch */ + if (htrAddScriptGlobal(s, "pg_width", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_height", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_charw", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_charh", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_parah", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_namespaces", "{}", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_handlertimeout", "null", 0) != 0) goto end; /* Used by htr_mousemovehandler() in ht_render.js. */ + if (htrAddScriptGlobal(s, "pg_mousemoveevents", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_handlers", "[]", 0) != 0) goto end; /* List of handlers for basic document events (click, mousemove, keypress, etc.). */ + if (htrAddScriptGlobal(s, "pg_capturedevents", "0", 0) != 0) goto end; /* Binary OR of all event flags currently registered on the document. */ + if (htrAddScriptGlobal(s, "pg_tiplayer", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_tipindex", "0", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_tiptmout", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_waitlyr", "null", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_appglobals", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_sessglobals", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_scripts", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_endorsements", "[]", 0) != 0) goto end; + if (htrAddScriptGlobal(s, "pg_max_requests", "1", 0) != 0) goto end; + + /** Include necessary scripts. **/ + if (htrAddScriptInclude(s, "/sys/js/htdrv_page.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/htdrv_connector.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/jquery/jquery-1.11.1.js", 0) != 0) goto end; /** Write named global **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&ptr)) != 0) goto end; strtcpy(name,ptr,sizeof(name)); - htrAddWgtrObjLinkage(s, tree, "window"); - htrAddWgtrCtrLinkage(s, tree, "document"); + /** Link the window and document to widgets. **/ + if (htrAddWgtrObjLinkage(s, tree, "window") != 0) goto end; + if (htrAddWgtrCtrLinkage(s, tree, "document") != 0) goto end; /** Set the application key **/ - htrAddScriptInit_va(s, " if (typeof window.akey == 'undefined') window.akey = '%STR&JSSTR';\n", s->ClientInfo->AKey); + /*** TODO: Greg - This should be a call to htrAddScriptGlobal(), but I + *** do not know how to do that with a the dynamic, formatted value. + ***/ + if (htrAddScriptInit_va(s, "\tif (typeof window.akey == 'undefined') window.akey = '%STR&JSSTR';\n", s->ClientInfo->AKey) != 0) + { + mssError(0, "HTPAGE", "Failed to write JS to set the application key."); + goto end; + } /** Send server's time to the client. **/ t = time(NULL); @@ -275,183 +322,340 @@ htpageRender(pHtSession s, pWgtrNode tree, int z) /** This isn't 100% ideal -- it causes a couple seconds of clock drift **/ if (strftime(timestr, sizeof(timestr), "%Y %m %d %T %Z", timeptr) > 0) { - htrAddScriptInit_va(s, " pg_servertime = new Date(%STR&DQUOT);\n", timestr); + if (htrAddScriptInit_va(s, "\tpg_servertime = new Date(%STR&DQUOT);\n", timestr)) + { + mssError(0, "HTPAGE", "Failed to write server time in JS."); + goto end; + } if (strftime(timestr, sizeof(timestr), "%Y %m %d %T", timeptr) > 0) - htrAddScriptInit_va(s, " pg_servertime_notz = new Date(%STR&DQUOT);\n", timestr); - htrAddScriptInit_va(s, " pg_clienttime = new Date();\n"); + { + if (htrAddScriptInit_va(s, "\tpg_servertime_notz = new Date(%STR&DQUOT);\n", timestr) != 0) + { + mssError(0, "HTPAGE", "Failed to write server time notz in JS."); + goto end; + } + } + if (htrAddScriptInit(s, "\tpg_clienttime = new Date();\n") != 0) + { + mssError(0, "HTPAGE", "Failed to write client time in JS."); + goto end; + } } } - /** Page init **/ - htrAddScriptInit(s, " if(typeof(pg_status_init)=='function')pg_status_init();\n"); - htrAddScriptInit_va(s, " pg_init(wgtrGetNodeRef(ns,'%STR&SYM'),%INT);\n", name, attract); - htrAddScriptInit_va(s, " pg_username = '%STR&JSSTR';\n", mssUserName()); - htrAddScriptInit_va(s, " pg_width = %INT;\n", w); - htrAddScriptInit_va(s, " pg_height = %INT;\n", h); - htrAddScriptInit_va(s, " pg_max_requests = %INT;\n", max_requests); - htrAddScriptInit_va(s, " pg_charw = %INT;\n", s->ClientInfo->CharWidth); - htrAddScriptInit_va(s, " pg_charh = %INT;\n", s->ClientInfo->CharHeight); - htrAddScriptInit_va(s, " pg_parah = %INT;\n", s->ClientInfo->ParagraphHeight); - - c_param = stLookup_ne(s->Params, "cx__obscure"); - if (c_param && !strcasecmp(c_param->StrVal,"yes")) - htrAddScriptInit(s, " obscure_data = true;\n"); - else - htrAddScriptInit(s, " obscure_data = false;\n"); + /** Write JS to initialize page variables. **/ + if (htrAddScriptInit_va(s, + "\tif (typeof(pg_status_init) === 'function') pg_status_init();\n" + "\tpg_init(wgtrGetNodeRef(ns,'%STR&SYM'),%INT);\n" + "\tpg_username = '%STR&JSSTR';\n" + "\tpg_width = %INT;\n" + "\tpg_height = %INT;\n" + "\tpg_max_requests = %INT;\n" + "\tpg_charw = %INT;\n" + "\tpg_charh = %INT;\n" + "\tpg_parah = %INT;\n", + name, attract, + mssUserName(), + w, h, + max_requests, + s->ClientInfo->CharWidth, + s->ClientInfo->CharHeight, + s->ClientInfo->ParagraphHeight + ) != 0) + { + mssError(0, "HTPAGE", "Failed to write JS to initialize page variables."); + goto end; + } + + /** Write obscore_data. **/ + pStruct c_param = stLookup_ne(s->Params, "cx__obscure"); + if (htrAddScriptInit_va(s, + "\tobscure_data = %BOOL;\n", + c_param != NULL && strcasecmp(c_param->StrVal, "yes") == 0 + ) != 0) + { + mssError(0, "HTPAGE", "Failed to write obscure_data variable in JS."); + goto end; + } /** Add template paths **/ for(i=0;iCapabilities.HTML40) - { - /** Add focus box **/ - htrAddStylesheetItem(s,"\ttd img { display: block; }\n"); - htrAddStylesheetItem(s,"\t#pgtop { POSITION:absolute; VISIBILITY:hidden; LEFT:-1000px;TOP:0px;WIDTH:1152px;HEIGHT:1px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgbtm { POSITION:absolute; VISIBILITY:hidden; LEFT:-1000px;TOP:0px;WIDTH:1152px;HEIGHT:1px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgrgt { POSITION:absolute; VISIBILITY:hidden; LEFT:0px;TOP:-1000px;WIDTH:1px;HEIGHT:864px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pglft { POSITION:absolute; VISIBILITY:hidden; LEFT:0px;TOP:-1000px;WIDTH:1px;HEIGHT:864px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgtvl { POSITION:absolute; VISIBILITY:hidden; LEFT:0px;TOP:0px;WIDTH:1px;HEIGHT:1px; Z-INDEX:0; }\n"); - htrAddStylesheetItem(s,"\t#pgktop { POSITION:absolute; VISIBILITY:hidden; LEFT:-1000px;TOP:0px;WIDTH:1152px;HEIGHT:1px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgkbtm { POSITION:absolute; VISIBILITY:hidden; LEFT:-1000px;TOP:0px;WIDTH:1152px;HEIGHT:1px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgkrgt { POSITION:absolute; VISIBILITY:hidden; LEFT:0px;TOP:-1000px;WIDTH:1px;HEIGHT:864px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pgklft { POSITION:absolute; VISIBILITY:hidden; LEFT:0px;TOP:-1000px;WIDTH:1px;HEIGHT:864px; clip:rect(0px,0px,0px,0px); Z-INDEX:1000; overflow:hidden;}\n"); - htrAddStylesheetItem(s,"\t#pginpt { POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:20px; Z-INDEX:20; }\n"); - htrAddStylesheetItem(s,"\t#pgping { POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:0px; WIDTH:0px; HEIGHT:0px; Z-INDEX:0;}\n"); - htrAddStylesheetItem(s,"\t#pgmsg { POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:0px; WIDTH:0px; HEIGHT:0px; Z-INDEX:0;}\n"); + /** Ignore null templates. **/ + path = wgtrGetTemplatePath(tree, i); + if (path == NULL) continue; + + /** Write code for the template. **/ + if (htrAddScriptInit_va(s, + "\twgtrGetNodeRef(ns,'%STR&SYM').templates.push('%STR&JSSTR');\n", + name, path + ) != 0) + { + mssError(0, "HTPAGE", + "Failed to write JS for template path #%d: \"%s\"", + i + 1, path + ); + goto end; + } } - else + + /** Allocate and init endorsement xarrays. **/ + XArray endorsements_buf; + if (check(xaInit(&endorsements_buf, 16)) != 0) goto end; + endorsements = &endorsements_buf; + XArray contexts_buf; + if (check(xaInit(&contexts_buf, 16)) != 0) goto end; + contexts = &contexts_buf; + if (checkNeg(cxssGetEndorsementList(endorsements, contexts)) < 0) goto end; + + /** Write endorsements. **/ + bool error = false; + for (unsigned int i = 0; i < endorsements->nItems; i++) + { + char* endorsement = endorsements->Items[i]; + char* context = contexts->Items[i]; + + /** Write endorsement. **/ + if (htrAddScriptInit_va(s, + "\tpg_endorsements.push({ e:'%STR&JSSTR', ctx:'%STR&JSSTR' });\n", + endorsement, context + ) != 0) + { + mssError(0, "HTPAGE", + "Failed to write endorsement #%u: \"%s\" of \"%s\" (continuing...)", + i + 1, endorsement, context + ); + error = true; + } + } + if (error) { - /** Add focus box **/ - htrAddStylesheetItem(s, - "\t#pgtop { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1152;HEIGHT:1; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgbtm { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1152;HEIGHT:1; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgrgt { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1;HEIGHT:864; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pglft { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1;HEIGHT:864; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgtvl { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1;HEIGHT:1; Z-INDEX:0; }\n" - "\t#pgktop { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1152;HEIGHT:1; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgkbtm { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1152;HEIGHT:1; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgkrgt { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1;HEIGHT:864; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pgklft { POSITION:absolute; VISIBILITY:hidden; LEFT:0;TOP:0;WIDTH:1;HEIGHT:864; clip:rect(1,1); Z-INDEX:1000;}\n" - "\t#pginpt { POSITION:absolute; VISIBILITY:hidden; LEFT:0; TOP:20; Z-INDEX:20; }\n" - "\t#pgping { POSITION:absolute; VISIBILITY:hidden; LEFT:0; TOP:0; Z-INDEX:0; }\n" - "\t#pgmsg { POSITION:absolute; VISIBILITY:hidden; LEFT:0; TOP:0; Z-INDEX:0; }\n"); + mssError(0, "HTPAGE", "Failed to write one or more endorsement(s) (failing)."); + goto end; } + /** Add focus box. **/ + if (htrAddStylesheetItem(s, + "\t\ttd img { display: block; }\n" + "\t\t.pg { position:absolute; visibility:hidden; }\n" + "\t\t.pgclip { overflow:hidden; z-index:1000; }\n" + "\t\t#pgtop { left:-1000px; top:0px; width:1152px; height:1px; }\n" + "\t\t#pgbtm { left:-1000px; top:0px; width:1152px; height:1px; }\n" + "\t\t#pgrgt { left:0px; top:-1000px; width:1px; height:864px; }\n" + "\t\t#pglft { left:0px; top:-1000px; width:1px; height:864px; }\n" + "\t\t#pgktop { left:-1000px; top:0px; width:1152px; height:1px; }\n" + "\t\t#pgkbtm { left:-1000px; top:0px; width:1152px; height:1px; }\n" + "\t\t#pgkrgt { left:0px; top:-1000px; width:1px; height:864px; }\n" + "\t\t#pgklft { left:0px; top:-1000px; width:1px; height:864px; }\n" + "\t\t#pgtvl { left:0px; top:0px; width:1px; height:1px; z-index:0; }\n" + "\t\t#pgping { left:0px; top:0px; width:0px; height:0px; z-index:0; }\n" + "\t\t#pgmsg { left:0px; top:0px; width:0px; height:0px; z-index:0; }\n" + ) != 0) + { + mssError(0, "HTPAGE", "Failed to write focus box CSS."); + goto end; + } + + /** Write pgstat. **/ if (show == 1) { - htrAddStylesheetItem(s, "\t#pgstat { POSITION:absolute; VISIBILITY:visible; LEFT:0;TOP:0;WIDTH:100%;HEIGHT:99%; Z-INDEX:100000;}\n"); - htrAddBodyItemLayerStart(s,0,"pgstat",0, NULL); - htrAddBodyItem_va(s, "", bgstr); - htrAddBodyItem (s, "
\n"); - htrAddBodyItemLayerEnd(s,0); + if (htrAddStylesheetItem(s, + "\t\t#pgstat { " + "position:absolute; " + "visibility:visible; " + "left:0; " + "top:0; " + "width:100%; " + "height:99%; " + "z-index:100000; " + "}\n" + ) != 0) + { + mssError(0, "HTPAGE", "Failed to write CSS for pgstat."); + goto end; + + } + + /** Write the pgstat DOM nodes. **/ + if (htrAddBodyItemLayerStart(s, 0, "pgstat", 0, NULL) != 0 + || htrAddBodyItem_va(s, "", bgstr) != 0 + || htrAddBodyItem(s, + "" + "" + "
loading...
\n" + ) != 0 + || htrAddBodyItemLayerEnd(s, 0) != 0) + { + mssError(0, "HTPAGE", "Failed to write HTML for pgstat DOM node."); + goto end; + } } - htrAddStylesheetItem_va(s, "\thtml { overflow:hidden; }\n"); - htrAddStylesheetItem_va(s, "\tbody { overflow:hidden; %[font-size:%POSpx; %]%[font-family:%STR&CSSVAL; %]}\n", - font_size > 0, font_size, *font_name, font_name); - htrAddStylesheetItem(s, "\tpre { font-size:90%; }\n"); + if (htrAddStylesheetItem(s, + "\t\thtml { " + "overflow:hidden; " + "}\n" + ) != 0) + { + mssError(0, "HTPAGE", "Failed to write CSS for main tag."); + goto end; + } + if (htrAddStylesheetItem_va(s, + "\t\tbody { " + "overflow:hidden; " + "%[font-size:%POSpx; %]" + "%[font-family:%STR&CSSVAL; %]" + "}\n", + (font_size > 0), font_size, + (*font_name), font_name + ) != 0) + { + mssError(0, "HTPAGE", "Failed to write CSS for main tag."); + goto end; + } + if (htrAddStylesheetItem(s, + "\t\tpre {" + "font-size:90%; " + "}\n" + ) != 0) + { + mssError(0, "HTPAGE", "Failed to write CSS for all
 tags.");
+	    goto end;
+	    }
 
-	if (s->Capabilities.Dom0NS)
+	/** Write trans divs. **/
+	bool trans_success = false;
+	#define HT_IMG_TAGS "src='/sys/images/trans_1.gif' alt=''"
+	if (htrAddBodyItem(s, "
\n") != 0) goto err_trans; + if (htrAddBodyItem(s, "
\n") != 0) goto err_trans; + if (htrAddBodyItem(s, "
\n") != 0) goto err_trans; + if (htrAddBodyItem(s, "
\n") != 0) goto err_trans; + if (htrAddBodyItem(s, "
\n") != 0) goto err_trans; + if (htrAddBodyItem(s, "
\n") != 0) goto err_trans; + if (htrAddBodyItem(s, "
\n") != 0) goto err_trans; + if (htrAddBodyItem(s, "
\n") != 0) goto err_trans; + if (htrAddBodyItem(s, "
\n") != 0) goto err_trans; + trans_success = true; + #undef HT_IMG_TAGS + + err_trans: + /** Handle errors. **/ + if (!trans_success) { - htrAddStylesheetItem_va(s, "\ttd { %[font-size:%POSpx; %]%[font-family:%STR&CSSVAL; %]}\n", - font_size > 0, font_size, *font_name, font_name); - htrAddStylesheetItem_va(s, "\tfont { %[font-size:%POSpx; %]%[font-family:%STR&CSSVAL; %]}\n", - font_size > 0, font_size, *font_name, font_name); + mssError(0, "HTPAGE", "Failed to write page trans div."); + goto end; } - htrAddBodyItem(s, "
\n"); - htrAddBodyItem(s, "
\n"); - htrAddBodyItem(s, "
\n"); - htrAddBodyItem(s, "
\n"); - htrAddBodyItem(s, "
\n"); - htrAddBodyItem(s, "
\n"); - htrAddBodyItem(s, "
\n"); - htrAddBodyItem(s, "
\n"); - htrAddBodyItem(s, "
\n"); + /** Write ping HTML. **/ + if (htrAddBodyItemLayerStart(s, HTR_LAYER_F_DYNAMIC, "pgping", 0, "pg") != 0 + || htrAddBodyItemLayerEnd(s, HTR_LAYER_F_DYNAMIC) != 0 + || htrAddBodyItem(s, "\n") != 0) + { + mssError(0, "HTPAGE", "Failed to write HTML for page ping DOM node."); + goto end; + } + + /** Write message HTML. **/ + if (htrAddBodyItemLayerStart(s, HTR_LAYER_F_DYNAMIC, "pgmsg", 0, "pg") != 0 + || htrAddBodyItemLayerEnd(s, HTR_LAYER_F_DYNAMIC) != 0 + || htrAddBodyItem(s, "\n") != 0) + { + mssError(0, "HTPAGE", "Failed to write HTML for page message DOM node."); + goto end; + } - htrAddBodyItemLayerStart(s,HTR_LAYER_F_DYNAMIC,"pgping",0, NULL); - htrAddBodyItemLayerEnd(s,HTR_LAYER_F_DYNAMIC); - htrAddBodyItemLayerStart(s,HTR_LAYER_F_DYNAMIC,"pgmsg",0, NULL); - htrAddBodyItemLayerEnd(s,HTR_LAYER_F_DYNAMIC); - htrAddBodyItem(s, "\n"); + /** Write JS call to initialize the pinging system. **/ + pStructInf net_http_config = stLookup(CxGlobals.ParsedConfig, "net_http"); + pStructInf watchdog_timer_attr = stLookup(net_http_config, "session_watchdog_timer"); + stAttrValue(watchdog_timer_attr, &watchdogtimer, NULL, 0); + if (htrAddScriptInit_va(s, + "\tpg_ping_init(htr_subel(wgtrGetNodeRef(ns, '%STR&SYM'), 'pgping'), %INT);\n", + name, watchdogtimer * 500 + ) != 0) + { + mssError(0, "HTPAGE", "Failed to write JS ping init call."); + goto end; + } - stAttrValue(stLookup(stLookup(CxGlobals.ParsedConfig, "net_http"),"session_watchdog_timer"),&watchdogtimer,NULL,0); - htrAddScriptInit_va(s," pg_ping_init(htr_subel(wgtrGetNodeRef(ns,\"%STR&SYM\"),\"pgping\"),%INT);\n",name,watchdogtimer/2*1000); - /** Add event code to handle mouse in/out of the area.... **/ - htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "pg", "pg_mousemove"); - htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "pg", "pg_mouseout"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "pg", "pg_mouseover"); - htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "pg", "pg_mousedown"); - htrAddEventHandlerFunction(s, "document", "MOUSEUP", "pg", "pg_mouseup"); - htrAddEventHandlerFunction(s, "document", "SCROLL", "pg", "pg_scroll"); - if (s->Capabilities.Dom1HTML) - htrAddEventHandlerFunction(s, "document", "CONTEXTMENU", "pg", "pg_contextmenu"); + /** Write event handling functions. **/ + if (htrAddEventHandlerFunction(s, "document", "CONTEXTMENU", "pg", "pg_contextmenu") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "KEYDOWN", "pg", "pg_keydown") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "KEYPRESS", "pg", "pg_keypress") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "KEYUP", "pg", "pg_keyup") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "pg", "pg_mousedown") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "pg", "pg_mousemove") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "pg", "pg_mouseout") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "pg", "pg_mouseover") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "pg", "pg_mouseup") != 0) goto end; + if (htrAddEventHandlerFunction(s, "document", "SCROLL", "pg", "pg_scroll") != 0) goto end; - /** W3C DOM Level 2 Event model doesn't require a textbox to get keystrokes **/ - if(s->Capabilities.Dom0NS) + /** Set colors for the focus layers **/ + if (htrAddScriptInit_va(s, + "\tpage.kbcolor1 = '%STR&JSSTR';\n" + "\tpage.kbcolor2 = '%STR&JSSTR';\n" + "\tpage.mscolor1 = '%STR&JSSTR';\n" + "\tpage.mscolor2 = '%STR&JSSTR';\n" + "\tpage.dtcolor1 = '%STR&JSSTR';\n" + "\tpage.dtcolor2 = '%STR&JSSTR';\n", + kbfocus1, kbfocus2, + msfocus1, msfocus2, + dtfocus1, dtfocus2 + ) != 0) { - htrAddBodyItem(s, "
\n"); - htrAddEventHandlerFunction(s, "document", "MOUSEUP", "pg2", "pg_mouseup_ns4"); + mssError(0, "HTPAGE", "Failed to write JS to set focus box colors."); + goto end; } - /** Set colors for the focus layers **/ - htrAddScriptInit_va(s, " page.kbcolor1 = '%STR&JSSTR';\n page.kbcolor2 = '%STR&JSSTR';\n",kbfocus1,kbfocus2); - htrAddScriptInit_va(s, " page.mscolor1 = '%STR&JSSTR';\n page.mscolor2 = '%STR&JSSTR';\n",msfocus1,msfocus2); - htrAddScriptInit_va(s, " page.dtcolor1 = '%STR&JSSTR';\n page.dtcolor2 = '%STR&JSSTR';\n",dtfocus1,dtfocus2); - /*htrAddScriptInit(s, " document.LSParent = null;\n");*/ - - htrAddScriptInit(s, " pg_togglecursor();\n"); + /** Write code to ensure cursor updating starts. **/ + if (htrAddScriptInit(s, "\tpg_togglecursor();\n") != 0) + { + mssError(0, "HTPAGE", "Failed to write JS call to start cursor updating."); + goto end; + } + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto end; - htrAddEventHandlerFunction(s, "document", "KEYDOWN", "pg", "pg_keydown"); - htrAddEventHandlerFunction(s, "document", "KEYUP", "pg", "pg_keyup"); - htrAddEventHandlerFunction(s, "document", "KEYPRESS", "pg", "pg_keypress"); + /** Write JS to signal that the page is done loading. **/ + if (htrAddScriptInit(s, + "\tif(typeof(pg_status_close) === 'function') pg_status_close();\n" + "\tpg_loadqueue_busy = false;\n" + "\tpg_serialized_load_doone();\n" + ) != 0) + { + mssError(0, "HTPAGE", "Failed to write JS to signal that loading is done."); + goto end; + } + + /** Success. **/ + rval = 0; - /** create the root node of the wgtr **/ - count = xaCount(&(tree->Children)); - for (i=0;iChildren),i), z+1); + mssError(0, "HTPAGE", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); } - /** keyboard input for NS4 **/ - if(s->Capabilities.Dom0NS) + /** Free the endorsement list. **/ + if (endorsements != NULL) { - htrAddScriptInit(s, - " setTimeout(pg_mvpginpt, 1, document.layers.pginpt);\n" - " document.layers.pginpt.moveTo(window.innerWidth-2, 20);\n" - " document.layers.pginpt.visibility = 'inherit';\n"); - htrAddScriptInit(s," document.layers.pginpt.document.tmpform.x.focus();\n"); + for (unsigned int i = 0; i < endorsements->nItems; i++) + nmSysFree(endorsements->Items[i]); + xaDeInit(endorsements); + } + if (contexts != NULL) + { + for (unsigned int i = 0; i < contexts->nItems; i++) + nmSysFree(contexts->Items[i]); + xaDeInit(contexts); } - htrAddScriptInit(s, " if(typeof(pg_status_close)=='function')pg_status_close();\n"); - htrAddScriptInit(s, " pg_loadqueue_busy = false;\n"); - htrAddScriptInit(s, " pg_serialized_load_doone();\n"); - - return 0; + return rval; } /*** htpageInitialize - register with the ht_render module. @@ -469,14 +673,6 @@ htpageInitialize() strcpy(drv->Name,"HTML Page Driver"); strcpy(drv->WidgetName,"page"); drv->Render = htpageRender; - /** Actions **/ - htrAddAction(drv, "LoadPage"); - htrAddParam(drv, "LoadPage", "Source", DATA_T_STRING); - htrAddAction(drv, "Launch"); - htrAddParam(drv, "Launch", "Source", DATA_T_STRING); - htrAddParam(drv, "Launch", "Width", DATA_T_INTEGER); - htrAddParam(drv, "Launch", "Height", DATA_T_INTEGER); - htrAddParam(drv, "Launch", "Name", DATA_T_STRING); /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_pane.c b/centrallix/htmlgen/htdrv_pane.c index 0683cc6e6..3edc835ad 100644 --- a/centrallix/htmlgen/htdrv_pane.c +++ b/centrallix/htmlgen/htdrv_pane.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,14 @@ /* Description: HTML Widget driver for a tab control. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -59,36 +55,35 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) char main_bg[128]; char bdr[64]; int x=-1,y=-1,w,h; - int id; int style = 1; /* 0 = lowered, 1 = raised, 2 = none, 3 = bordered */ char* c1; char* c2; - int box_offset; int border_radius; int shadow_offset, shadow_radius; char shadow_color[128]; - if(!s->Capabilities.Dom0NS && !(s->Capabilities.Dom1HTML && s->Capabilities.CSS1)) + /** Get an id for this. **/ + const int id = (HTPN.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTPN","Netscape DOM or W3C DOM1 HTML and CSS support required"); - return -1; + mssError(1, "HTPN", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTPN.idcnt++); - /** Get x,y,w,h of this object **/ if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { mssError(1,"HTPN","Pane widget must have a 'width' property"); - return -1; + goto err; } if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) { mssError(1,"HTPN","Pane widget must have a 'height' property"); - return -1; + goto err; } /** Border radius, for raised/lowered/bordered panes **/ @@ -98,12 +93,6 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) /** Background color/image? **/ htrGetBackground(tree,NULL,!s->Capabilities.Dom0NS,main_bg,sizeof(main_bg)); - /** figure out box offset fudge factor... stupid box model... **/ - if (s->Capabilities.CSSBox) - box_offset = 1; - else - box_offset = 0; - /** Drop shadow **/ shadow_offset=0; if (wgtrGetPropertyValue(tree, "shadow_offset", DATA_T_INTEGER, POD(&shadow_offset)) == 0 && shadow_offset > 0) @@ -120,12 +109,12 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) } /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto err; strtcpy(name,ptr,sizeof(name)); - htrCheckAddExpression(s, tree, name, "enabled"); - htrCheckAddExpression(s, tree, name, "background"); - htrCheckAddExpression(s, tree, name, "bgcolor"); + if (htrCheckAddExpression(s, tree, name, "enabled") < 0) goto err; + if (htrCheckAddExpression(s, tree, name, "background") < 0) goto err; + if (htrCheckAddExpression(s, tree, name, "bgcolor") < 0) goto err; /** Style of pane - raised/lowered **/ if (wgtrGetPropertyValue(tree,"style",DATA_T_STRING,POD(&ptr)) == 0) @@ -135,6 +124,8 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) if (!strcmp(ptr,"flat")) style = 2; if (!strcmp(ptr,"bordered")) style = 3; } + + /** Computes styling colors. **/ if (style == 1) /* raised */ { c1 = "white"; @@ -153,56 +144,146 @@ htpnRender(pHtSession s, pWgtrNode tree, int z) strtcpy(bdr,ptr,sizeof(bdr)); } - /** Ok, write the style header items. **/ - if (style == 2) /* flat */ - { - htrAddStylesheetItem_va(s,"\t#pn%POSmain { POSITION:absolute; VISIBILITY:inherit; overflow:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w,h,z); - htrAddStylesheetItem_va(s,"\t#pn%POSmain { border-radius: %INTpx; %STR}\n",id,border_radius,main_bg); - } + /** Write the CSS for borders on the pane dom node. **/ + if (style == 2) { /* flat, the default style, nothing to do */ } else if (style == 0 || style == 1) /* lowered or raised */ { - htrAddStylesheetItem_va(s,"\t#pn%POSmain { POSITION:absolute; VISIBILITY:inherit; overflow: hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w-2*box_offset,h-2*box_offset,z); - htrAddStylesheetItem_va(s,"\t#pn%POSmain { border-style: solid; border-width: 1px; border-color: %STR %STR %STR %STR; border-radius: %INTpx; %STR}\n",id,c1,c2,c2,c1,border_radius,main_bg); + if (htrAddStylesheetItem_va(s, + "\t\t#pn%POSmain {" + "border-style: solid; " + "border-width: 1px; " + "border-color: %STR %STR %STR %STR; " + "}\n", + id, + c1, c2, c2, c1 + ) != 0) + { + mssError(0, "HTPN", + "Failed to write %s pane CSS.", + (style == 0) ? "lowered" : "raised" + ); + goto err; + } } else if (style == 3) /* bordered */ { - htrAddStylesheetItem_va(s,"\t#pn%POSmain { POSITION:absolute; VISIBILITY:inherit; overflow: hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",id,x,y,w-2*box_offset,h-2*box_offset,z); - htrAddStylesheetItem_va(s,"\t#pn%POSmain { border-style: solid; border-width: 1px; border-color:%STR&CSSVAL; border-radius: %INTpx; %STR}\n",id,bdr,border_radius,main_bg); + if (htrAddStylesheetItem_va(s, + "\t\t#pn%POSmain {" + "border-style: solid;" + "border-width: 1px; " + "border-color:%STR&CSSVAL; " + "}\n", + id, + bdr + ) != 0) + { + mssError(0, "HTPN", "Failed to write bordered pane CSS."); + goto err; + } + } + + /*** Write the main CSS for the pane DOM node. + *** + *** border-box keeps the border inside the width and height, which is + *** what wgtdrv_pane.c promises the layout when it declares the border + *** as an inset. + ***/ + if (htrAddStylesheetItem_va(s, + "\t\t#pn%POSmain {" + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "box-sizing:border-box; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "border-radius:%INTpx; " + "%STR " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z, + border_radius, + main_bg + ) != 0) + { + mssError(0, "HTPN", "Failed to write main CSS."); + goto err; } + if (shadow_radius > 0) { - htrAddStylesheetItem_va(s,"\t#pn%POSmain { box-shadow: %POSpx %POSpx %POSpx %STR&CSSVAL; }\n", id, shadow_offset, shadow_offset, shadow_radius, shadow_color); + if (htrAddStylesheetItem_va(s, + "\t\t#pn%POSmain { " + "box-shadow: %POSpx %POSpx %POSpx %STR&CSSVAL; " + "}\n", + id, + shadow_offset, shadow_offset, shadow_radius, shadow_color + ) != 0) + { + mssError(0, "HTPN", "Failed to write shadow CSS."); + goto err; + } } /** DOM linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "pn%POSmain",id); + if (htrAddWgtrObjLinkage_va(s, tree, "pn%POSmain", id) != 0) goto err; /** Script include call **/ - htrAddScriptInclude(s, "/sys/js/htdrv_pane.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); + if (htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_pane.js", 0) != 0) goto err; /** Event Handlers **/ - htrAddEventHandlerFunction(s, "document","MOUSEUP", "pn", "pn_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "pn", "pn_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER", "pn", "pn_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEOUT", "pn", "pn_mouseout"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "pn", "pn_mousemove"); + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "pn", "pn_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "pn", "pn_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "pn", "pn_mouseout") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "pn", "pn_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "pn", "pn_mouseup") != 0) goto err; /** Script initialization call. **/ - htrAddScriptInit_va(s, " pn_init({mainlayer:wgtrGetNodeRef(ns,\"%STR&SYM\"), layer:wgtrGetNodeRef(ns,\"%STR&SYM\")});\n", - name, name); + if (htrAddScriptInit_va(s, + "\tpn_init({" + "mainlayer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "});\n", + name, name + ) != 0) + { + mssError(0, "HTPN", "Failed to write JS init call."); + goto err; + } /** HTML body
element for the base layer. **/ - //htrAddBodyItem_va(s,"
\n",id, w-2, h-2); - htrAddBodyItem_va(s,"
\n",id, w-2, h-2); + if (htrAddBodyItem_va(s,"
\n", id) != 0) + { + mssError(0, "HTPN", "Failed to write HTML opening tag."); + goto err; + } - /** Check for objects within the pane. **/ - htrRenderSubwidgets(s, tree, z+2); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto err; - /** End the containing layer. **/ - htrAddBodyItem(s, "
\n"); + /** Write the end of the container. **/ + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTPN", "Failed to write HTML closing tag."); + goto err; + } - return 0; + /** Success. **/ + return 0; + + err: + mssError(0, "HTPN", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } @@ -222,13 +303,6 @@ htpnInitialize() strcpy(drv->WidgetName,"pane"); drv->Render = htpnRender; - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_parameter.c b/centrallix/htmlgen/htdrv_parameter.c index 7b71d5aa7..b765e3c10 100644 --- a/centrallix/htmlgen/htdrv_parameter.c +++ b/centrallix/htmlgen/htdrv_parameter.c @@ -1,23 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/xstring.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "wgtr.h" -#include "stparse_ne.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2007 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -44,6 +29,20 @@ /* pass information to/from an application or component. */ /************************************************************************/ +#include +#include + +#include "cxlib/check.h" +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xstring.h" +#include "hints.h" +#include "ht_render.h" +#include "obj.h" +#include "stparse_ne.h" +#include "wgtr.h" + /*** htparamRender - generate the HTML code for the page. ***/ @@ -55,29 +54,31 @@ htparamRender(pHtSession s, pWgtrNode tree, int z) char paramname[64]; char type[32]; char findcontainer[64]; - int i; - XString xs; - pObjPresentationHints hints; pStruct find_inf; int deploy_to_client; - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML ) + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTPARAM","Netscape DOM or W3C DOM1HTML support required"); - return -1; + mssError(1, "HTPARAM", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } /** Get name and type **/ if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) - return -1; + { + mssError(1, "HTPARAM", "Failed to get name."); + goto err; + } strtcpy(name,ptr,sizeof(name)); if (wgtrGetPropertyValue(tree,"type",DATA_T_STRING,POD(&ptr)) != 0) ptr = "string"; strtcpy(type,ptr,sizeof(type)); - if (objTypeID(type) < 0 && strcmp(type,"object")) + const int datatype = objTypeID(type); + if (datatype < 0 && strcmp(type, "object") != 0) { - mssError(1,"HTPARAM","Invalid parameter data type for '%s'", name); - return -1; + mssError(1, "HTPARAM", "Invalid datatype: \"%s\".", type); + goto err; } /** Specify name of param (in case param widget name isn't the desired param name) **/ @@ -96,8 +97,8 @@ htparamRender(pHtSession s, pWgtrNode tree, int z) findcontainer[0] = '\0'; /** JavaScript include file **/ - htrAddScriptInclude(s, "/sys/js/htdrv_parameter.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0); + if (htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_parameter.js", 0) != 0) goto err; /** Value supplied? **/ if (deploy_to_client) @@ -106,39 +107,94 @@ htparamRender(pHtSession s, pWgtrNode tree, int z) find_inf = NULL; /** Parameter has presentation-hints components to it. Set those. **/ + const int has_find_inf = (find_inf != NULL && find_inf->StrVal != NULL); if (deploy_to_client) { + bool successful = false; + XString xs = { AllocLen: 0 }; + pObjPresentationHints hints = NULL; + /** Script init **/ - htrAddScriptInit_va(s, " pa_init(wgtrGetNodeRef(ns,\"%STR&SYM\"), {name:'%STR&JSSTR', type:'%STR&JSSTR', findc:'%STR&JSSTR', val:%[null%]%[\"%STR&HEX\"%]});\n", - name, paramname, type, findcontainer, !find_inf || !find_inf->StrVal, find_inf && find_inf->StrVal, find_inf?find_inf->StrVal:""); + if (htrAddScriptInit_va(s, + "\tpa_init(wgtrGetNodeRef(ns, '%STR&SYM'), { " + "name:'%STR&JSSTR', " + "type:'%STR&JSSTR', " + "findc:'%STR&JSSTR', " + "val:%['%STR&HEX'%]%[null%], " + "});\n", + name, paramname, type, findcontainer, + (has_find_inf), (has_find_inf) ? find_inf->StrVal : "null", (!has_find_inf) + ) != 0) + { + mssError(0, "HTPARAM", "Failed to write JS init call."); + goto err_deploy; + } + /** Get presentation hints. **/ hints = wgtrWgtToHints(tree); - if (!hints) + if (hints == NULL) + { + mssError(0, "HTPARAM", "Failed to get presentation hints."); + goto err_deploy; + } + + /** Write presentation hints. **/ + if (check(xsInit(&xs)) != 0) goto err_deploy; + if (hntEncodeHints(hints, &xs) < 0) { - mssError(0, "HTPARAM", "Error in parameter data"); - return -1; + mssError(0, "HTPARAM", "Failed to encode presentation hints."); + goto err_deploy; } - xsInit(&xs); - hntEncodeHints(hints, &xs); - htrAddScriptInit_va(s, " cx_set_hints(wgtrGetNodeRef(ns,\"%STR&SYM\"), '%STR&JSSTR', 'app');\n", - name, xs.String); - xsDeInit(&xs); - objFreeHints(hints); - - htrAddScriptInit_va(s, " wgtrGetNodeRef(ns,\"%STR&SYM\").Verify();\n", name); + if (htrAddScriptInit_va(s, + "\tcx_set_hints(wgtrGetNodeRef(ns, '%STR&SYM'), '%STR&JSSTR', 'app');\n", + name, xs.String + ) != 0) + { + mssError(0, "HTPARAM", "Failed to write JS for presentation hints."); + goto err_deploy; + } + + if (htrAddScriptInit_va(s, "\twgtrGetNodeRef(ns, '%STR&SYM').Verify();\n", name) != 0) + { + mssError(0, "HTPARAM", "Failed to write JS to verify presentation hints."); + goto err_deploy; + } + + /** Success. **/ + successful = true; + + err_deploy: + /** Clean up. **/ + if (check(xsDeInit(&xs)) != 0) goto err_deploy; + if (hints != NULL && objFreeHints(hints) != 0) + { + mssError(0, "HTPARAM", "Failed to free presentation hints."); + goto err_deploy; + } + + /** Handle errors. **/ + if (!successful) goto err; } else { tree->RenderFlags |= HT_WGTF_NORENDER; } + /** Nonvisual widget. **/ tree->RenderFlags |= HT_WGTF_NOOBJECT; - /** Check for more sub-widgets within the param **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+1); - - return 0; + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto err; + + /** Success. **/ + return 0; + + err: + mssError(0, "HTPARAM", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); + return -1; } @@ -148,8 +204,6 @@ int htparamInitialize() { pHtDriver drv; - /*pHtEventAction action; - pHtParam param;*/ /** Allocate the driver **/ drv = htrAllocDriver(); diff --git a/centrallix/htmlgen/htdrv_radiobutton.c b/centrallix/htmlgen/htdrv_radiobutton.c index c1d395c06..01569ad41 100644 --- a/centrallix/htmlgen/htdrv_radiobutton.c +++ b/centrallix/htmlgen/htdrv_radiobutton.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 2000-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 2000-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,275 +29,469 @@ /* radio button. */ /************************************************************************/ +#include +#include -/** globals **/ -static struct { - int idcnt; -} HTRB; - - -/** htrbRender - generate the HTML code for the page. **/ -int htrbRender(pHtSession s, pWgtrNode tree, int z) { - char* ptr; - char name[64]; - char title[64]; - char sbuf2[200]; - //char bigbuf[4096]; - char textcolor[32]; - char main_background[128]; - char outline_background[128]; - char form[64]; - pWgtrNode radiobutton_obj, sub_tree; - int x=-1,y=-1,w,h; - int top_offset; - int cover_height, cover_width; - int item_spacing; - int id, i, j; - int is_selected; - int rb_cnt; - int cover_margin; - char fieldname[32]; - char value[64]; - char label[64]; - - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML) - { - mssError(1,"HTRB","Netscape 4.x or W3C DOM support required"); - return -1; - } - - /** Get an id for this. **/ - id = (HTRB.idcnt++); - - /** Get x,y,w,h of this object **/ - if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; - if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; - if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { - mssError(1,"HTRB","RadioButtonPanel widget must have a 'width' property"); - return -1; - } - if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) { - mssError(1,"HTRB","RadioButtonPanel widget must have a 'height' property"); - return -1; - } - - /** Background color/image? **/ - htrGetBackground(tree,NULL,!s->Capabilities.Dom0NS,main_background,sizeof(main_background)); - - /** Text color? **/ - if (wgtrGetPropertyValue(tree,"textcolor",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(textcolor,ptr,sizeof(textcolor)); - else - strcpy(textcolor,"black"); - - /** Outline color? **/ - htrGetBackground(tree,"outline",!s->Capabilities.Dom0NS,outline_background,sizeof(outline_background)); - - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(name,ptr,sizeof(name)); - - /** Get title **/ - if (wgtrGetPropertyValue(tree,"title",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(title,ptr,sizeof(title)); - - /** User requesting expression for selected tab? **/ - htrCheckAddExpression(s, tree, name, "value"); - - /** User requesting expression for selected tab using integer index value? **/ - htrCheckAddExpression(s, tree, name, "value_index"); - - /** Get fieldname **/ - if (wgtrGetPropertyValue(tree,"fieldname",DATA_T_STRING,POD(&ptr)) == 0) - { - strtcpy(fieldname,ptr,sizeof(fieldname)); - } - else - { - fieldname[0]='\0'; - } - - if (wgtrGetPropertyValue(tree,"form",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(form,ptr,sizeof(form)); - else - form[0]='\0'; +#include "cxlib/check.h" +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "ht_render.h" +#include "wgtr.h" - htrAddScriptInclude(s, "/sys/js/htdrv_radiobutton.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - /** Ok, write the style header items. **/ - top_offset = s->ClientInfo->ParagraphHeight*3/4+1; - cover_height = h-(top_offset+3+2); - cover_width = w-(2*3 +2); - htrAddStylesheetItem_va(s,"\t#rb%POSparent { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); }\n", - id,x,y,w,h,z,w,h); - htrAddStylesheetItem_va(s,"\t#rb%POSborder { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); }\n", - id,3,top_offset,w-(2*3),h-(top_offset+3),z+1,w-(2*3),h-(top_offset+3)); - htrAddStylesheetItem_va(s,"\t#rb%POScover { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); }\n", - id,1,1,cover_width,cover_height,z+2,cover_width,cover_height); - htrAddStylesheetItem_va(s,"\t#rb%POStitle { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; }\n", - id,10,1,w/2,s->ClientInfo->ParagraphHeight,z+3); - - htrAddScriptGlobal(s, "radiobutton", "null", 0); +/** globals **/ +static struct + { + int idcnt; + } + HTRB; - /** DOM linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "rb%POSparent",id); - htrAddWgtrCtrLinkage_va(s, tree, "htr_subel(htr_subel(_obj,\"rb%POSborder\"),\"rb%POScover\")",id,id); - /** Loop through each radiobutton and flag it NOOBJECT **/ - rb_cnt = 0; - for (j=0;jChildren));j++) +/** htrbRender - generate the HTML code for the page. **/ +int htrbRender(pHtSession s, pWgtrNode tree, int z) + { + int rval = -1; + char* ptr; + XArray radio_buttons = { .nAlloc = 0 }; + + /** Get an id for this widget. **/ + const int id = (HTRB.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - radiobutton_obj = xaGetItem(&(tree->Children), j); - radiobutton_obj->RenderFlags |= HT_WGTF_NOOBJECT; - wgtrGetPropertyValue(radiobutton_obj,"outer_type",DATA_T_STRING,POD(&ptr)); - if (!strcmp(ptr,"widget/radiobutton")) - { - rb_cnt++; - } + mssError(1, "HTRB", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto end_free; } - /* - Now lets loop through and create a style sheet for each optionpane on the - radiobuttonpanel - */ - item_spacing = 12 + s->ClientInfo->ParagraphHeight; - cover_margin = 10; - if (item_spacing*rb_cnt+2*cover_margin > cover_height) - item_spacing = (cover_height-2*cover_margin)/rb_cnt; - if (item_spacing*rb_cnt+2*cover_margin > cover_height) - cover_margin = (cover_height-(item_spacing*rb_cnt))/2; - if (cover_margin < 2) cover_margin = 2; - i = 1; - for (j=0;jChildren));j++) + + /** Get x,y,w,h of this object. **/ + int x, y, w, h, spacing; + if (wgtrGetPropertyValue(tree, "x", DATA_T_INTEGER, POD(&x)) != 0) x = 0; + if (wgtrGetPropertyValue(tree, "y", DATA_T_INTEGER, POD(&y)) != 0) y = 0; + if (wgtrGetPropertyValue(tree, "width", DATA_T_INTEGER, POD(&w)) != 0) { - radiobutton_obj = xaGetItem(&(tree->Children), j); - wgtrGetPropertyValue(radiobutton_obj,"outer_type",DATA_T_STRING,POD(&ptr)); - if (!strcmp(ptr,"widget/radiobutton")) + mssError(1,"HTRB","RadioButtonPanel widget must have a 'width' property"); + goto end_free; + } + if (wgtrGetPropertyValue(tree, "height", DATA_T_INTEGER, POD(&h)) != 0) + { + mssError(1,"HTRB","RadioButtonPanel widget must have a 'height' property"); + goto end_free; + } + if (wgtrGetPropertyValue(tree, "spacing", DATA_T_INTEGER, POD(&spacing)) != 0) + spacing = 10; + if (spacing < 0) + { + mssError(1, "HTRB", "RadioButtonPanel widget's 'spacing' property cannot be negative."); + goto end_free; + } + + /** Get the name and title attributes. **/ + char name[64] = "", title[64] = ""; + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&ptr)) != 0) + { + mssError(1, "HTRB", "RadioButtonPanel widget must have a 'name' property"); + goto end_free; + } + strtcpy(name, ptr, sizeof(name)); + if (wgtrGetPropertyValue(tree, "title", DATA_T_STRING, POD(&ptr)) != 0) + { + mssError(1, "HTRB", "RadioButtonPanel widget must have a 'title' property"); + goto end_free; + } + strtcpy(title, ptr, sizeof(title)); + + /** Get text color attribute. **/ + char textcolor[32]; + if (wgtrGetPropertyValue(tree, "textcolor", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(textcolor, ptr, sizeof(textcolor)); + else + strcpy(textcolor, "black"); + + /** Get background attributes. **/ + char main_background[128] = ""; + char outline_background[128] = ""; + if (htrGetBackground(tree, NULL, true, main_background, sizeof(main_background)) < 0) goto end_free; + if (htrGetBackground(tree, "outline", true, outline_background, sizeof(outline_background)) < 0) goto end_free; + + /** User requesting expression for selected tab? **/ + if (htrCheckAddExpression(s, tree, name, "value") < 0) goto end_free; + + /** User requesting expression for selected tab using integer index value? **/ + if (htrCheckAddExpression(s, tree, name, "value_index") < 0) goto end_free; + + /** Get fieldname and form attributes. **/ + char fieldname[32] = "", form[64] = ""; + if (wgtrGetPropertyValue(tree, "fieldname", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(fieldname, ptr, sizeof(fieldname)); + if (wgtrGetPropertyValue(tree, "form", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(form, ptr, sizeof(form)); + + + /** Include scripts. **/ + if (htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0) != 0) goto end_free; + if (htrAddScriptInclude(s, "/sys/js/htdrv_radiobutton.js", 0) != 0) goto end_free; + + /** Link DOM node to widget data. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "rb%POSparent", id) != 0) goto end_free; + if (htrAddWgtrCtrLinkage_va(s, tree, "htr_subel(htr_subel(_obj, 'rb%POSborder'), 'rb%POScover')", id, id) != 0) goto end_free; + + /** Script initialization call. **/ + if (htrAddScriptInit_va(s, "\t{ " + "const parentPane = wgtrGetNodeRef(ns, '%STR&SYM'); " + "const borderPane = htr_subel(parentPane, 'rb%POSborder'); " + "const coverPane = htr_subel(borderPane, 'rb%POScover'); " + "const titlePane = htr_subel(parentPane, 'rb%POStitle'); " + "const easterEgg2 = 'Easter Egg #2';" + "radiobuttonpanel_init({ " + "parentPane, borderPane, coverPane, titlePane, " + "fieldname:'%STR&JSSTR', " + "mainBackground:'%STR&JSSTR', " + "outlineBackground:'%STR&JSSTR', " + "form:'%STR&JSSTR', " + "}); }\n", + name, id, id, id, + fieldname, + main_background, + outline_background, + form + ) != 0) + { + mssError(0, "HTRB", "Failed to write JS init code."); + goto end_free; + } + + /** Add event listenners. **/ + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "radiobutton", "radiobutton_mousedown") != 0) goto end_free; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "radiobutton", "radiobutton_mousemove") != 0) goto end_free; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "radiobutton", "radiobutton_mouseover") != 0) goto end_free; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "radiobutton", "radiobutton_mouseup") != 0) goto end_free; + + + /** Write style headers for container DOM nodes. **/ + const int para_height = s->ClientInfo->ParagraphHeight; + const int top_offset = (para_height * 3) / 4 + 1; + if (htrAddStylesheetItem_va(s, + "\t\t.rb%POSall { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "}\n", + id + ) != 0) + { + mssError(0, "HTRB", "Failed to write shared CSS."); + goto end_free; + } + if (htrAddStylesheetItem_va(s, + "\t\t#rb%POSparent { " + "cursor:default; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + id, + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z + ) != 0) + { + mssError(0, "HTRB", "Failed to write parent (container) CSS."); + goto end_free; + } + if (htrAddStylesheetItem_va(s, + "\t\t#rb%POSborder { " + "left:3px; " + "top:%POSpx; " + "width:calc(100%% - 6px); " + "height:calc(100%% - %POSpx); " + "z-index:%POS; " + "}\n", + id, + top_offset, + top_offset + 3, + z + 1 + ) != 0) + { + mssError(0, "HTRB", "Failed to write border CSS."); + goto end_free; + } + if (htrAddStylesheetItem_va(s, + "\t\t#rb%POScover { " + "left:1px; " + "top:1px; " + "width:calc(100%% - 2px); " + "height:calc(100%% - 2px); " + "z-index:%POS; " + "}\n", + id, + z + 2 + ) != 0) + { + mssError(0, "HTRB", "Failed to write cover CSS."); + goto end_free; + } + if (htrAddStylesheetItem_va(s, + "\t\t#rb%POStitle { " + "position:absolute; " + "visibility:inherit; " + "left:10px; " + "top:1px; " + "width:50%%; " + "height:%POSpx; " + "z-index:%POS; " + "}\n", + id, + para_height, + z + 3 + ) != 0) + { + mssError(0, "HTRB", "Failed to write title CSS."); + goto end_free; + } + + + /** Write HTML to contain the radio buttons. **/ + if (htrAddBodyItem_va(s, + "
" + "
" + "
\n", + id, id, id, id, id, id + ) != 0) + { + mssError(0, "HTRB", "Failed to write HTML for containers."); + goto end_free; + } + + /** Search child array for radio buttons. **/ + if (check(xaInit(&radio_buttons, tree->Children.nItems)) != 0) goto end_free; + for (int i = 0; i < tree->Children.nItems; i++) + { + pWgtrNode child = checkPtr(tree->Children.Items[i]); + if (child == NULL) { - htrAddStylesheetItem_va(s,"\t#rb%POSoption%POS { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px, %POSpx, %POSpx, 0px); }\n", - id,i,7,cover_margin+((i-1)*item_spacing)+3,cover_width-7,item_spacing,z+2,cover_width-7,item_spacing); - i++; + mssError(1, "HTRB", "Child widget #%d/%d is NULL.", i + 1, tree->Children.nItems); + goto end_free; } + + /** Mark child as no-object. **/ + child->RenderFlags |= HT_WGTF_NOOBJECT; + + /** Add radio buttons to the array, render other widgets immediately (so we can forget about them). **/ + wgtrGetPropertyValue(child, "outer_type", DATA_T_STRING, POD(&ptr)); + if (strcmp(ptr, "widget/radiobutton") == 0) + { + if (checkNeg(xaAddItem(&radio_buttons, child)) < 0) goto end_free; + } + else if (htrRenderWidget(s, child, z + 1) != 0) goto end_free; } - - /** Script initialization call. **/ - if (strlen(main_background) > 0) { - htrAddScriptInit_va(s, - " var rb = wgtrGetNodeRef(ns, \"%STR&SYM\");\n" - " radiobuttonpanel_init({\n" - " parentPane:rb, fieldname:\"%STR&JSSTR\",\n" - " borderPane:htr_subel(rb,\"rb%POSborder\"),\n" - " coverPane:htr_subel(htr_subel(rb,\"rb%POSborder\"),\"rb%POScover\"),\n" - " titlePane:htr_subel(rb,\"rb%POStitle\"),\n" - " mainBackground:\"%STR&JSSTR\", outlineBackground:\"%STR&JSSTR\", form:\"%STR&JSSTR\"});\n", - name, fieldname, id, id,id, id, main_background, outline_background, form); - } else { - htrAddScriptInit_va(s," radiobuttonpanel_init({parentPane:wgtrGetNodeRef(ns,\"%STR&SYM\"), fieldname:\"%STR&JSSTR\", borderPane:0, coverPane:0, titlePane:0, mainBackground:0, outlineBackground:0, form:\"%STR&JSSTR\"});\n", name, fieldname, form); - } - - htrAddEventHandlerFunction(s, "document", "MOUSEUP", "radiobutton", "radiobutton_mouseup"); - htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "radiobutton", "radiobutton_mousedown"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "radiobutton", "radiobutton_mouseover"); - htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "radiobutton", "radiobutton_mousemove"); - - /* - Now lets loop through and add each radiobutton - */ - i = 1; - for (j=0;jChildren));j++) + + /** Write style for radio buttons. **/ + const int top_padding = 12; + const int button_height = para_height + 2; + const int content_height = top_padding + (button_height * radio_buttons.nItems) + 8; + for (int i = 0; i < radio_buttons.nItems; i++) { - sub_tree = xaGetItem(&(tree->Children), j); - wgtrGetPropertyValue(sub_tree,"outer_type",DATA_T_STRING,POD(&ptr)); - if (!strcmp(ptr,"widget/radiobutton")) + pWgtrNode radio_button = radio_buttons.Items[i]; + + /** Store data for the individual radio button. **/ + char value_buf[64] = "", label_buf[64] = ""; + if (wgtrGetPropertyValue(radio_button, "value", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(value_buf, ptr, sizeof(value_buf)); + if (wgtrGetPropertyValue(radio_button, "label", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(label_buf, ptr, sizeof(label_buf)); + const int is_selected = (htrGetBoolean(radio_button, "selected", 0) > 0); + wgtrGetPropertyValue(radio_button, "name", DATA_T_STRING, POD(&ptr)); + + /** Create str pointers. value & label each default to the other. **/ + char* radio_button_name = ptr; /* Name temporarily stored in the wgtrGetPropertyValue() buffer. */ + char* radio_button_value = (value_buf[0] == '\0') ? label_buf : value_buf; + char* radio_button_label = (label_buf[0] == '\0') ? value_buf : label_buf; + + /** Link the radio button DOM node to widget data. **/ + if (htrAddWgtrObjLinkage_va(s, radio_button, "rb%POSoption%POS", id, i) != 0) goto err_option; + + /** Write the initialization call. **/ + if (htrAddScriptInit_va(s, "\t{ " + "const rbitem = wgtrGetNodeRef('%STR&SYM', '%STR&SYM');" + "add_radiobutton(rbitem, { " + "selected:%POS, " + "buttonset:htr_subel(rbitem, 'rb%POSbuttonset%POS'), " + "buttonunset:htr_subel(rbitem, 'rb%POSbuttonunset%POS'), " + "value:htr_subel(rbitem, 'rb%POSvalue%POS'), " + "label:htr_subel(rbitem, 'rb%POSlabel%POS'), " + "valuestr:'%STR&JSSTR', " + "labelstr:'%STR&JSSTR', " + "}); }\n", + wgtrGetNamespace(radio_button), radio_button_name, + is_selected, + id, i, id, i, + id, i, id, i, + radio_button_value, radio_button_label + ) != 0) + { + mssError(0, "HTRB", "Failed to write JS add_radiobutton() call."); + goto err_option; + } + + /*** Write CSS for the radio button container. The 0px lower bound keeps + *** buttons from overlapping when the panel is shorter than its content. + ***/ + const int base_top = top_padding + (button_height * i); + const double percent_space_above = (100.0 / radio_buttons.nItems) * i; + const double content_above = ((double)content_height / radio_buttons.nItems) * i; + if (htrAddStylesheetItem_va(s, + "\t\t#rb%POSoption%POS { " + "left:7px; " + "top:calc(%POSpx " + "+ clamp(0px, %DBL%% - %DBLpx, %POSpx)" + "); " + "width:calc(100%% - 14px); " + "height:%POSpx; " + "z-index:%POS; " + "}\n", + id, i, + base_top, + percent_space_above, content_above, spacing * i, + button_height, + z + 2 + ) != 0) + { + mssError(0, "HTRB", "Failed to write option CSS."); + goto err_option; + } + + /** Write CSS for the radio button elements. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#rb%POSbuttonset%POS, " + "#rb%POSbuttonunset%POS { " + "position:absolute; " + "overflow:hidden; " + "left:5px; " + "top:%INTpx; " + "width:12px; " + "height:12px; " + "z-index:%POS; " + "cursor:pointer; " + "}\n", + id, i, + id, i, + (para_height / 2) - 4, + z + 2 + ) != 0) + { + mssError(0, "HTRB", "Failed to write option set CSS."); + goto err_option; + } + if (htrAddStylesheetItem_va(s, + "\t\t#rb%POSvalue%POS { " + "position:absolute; " + "visibility:hidden; " + "overflow:hidden; " + "left:5px; " + "top:6px; " + "width:12px; " + "height:12px; " + "z-index:%POS; " + "}\n", + id, i, + z + 2 + ) != 0) { - if (wgtrGetPropertyValue(sub_tree,"value",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(value, ptr, sizeof(value)); - else - value[0] = '\0'; - if (wgtrGetPropertyValue(sub_tree,"label",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(label, ptr, sizeof(label)); - else - label[0] = '\0'; - is_selected = htrGetBoolean(sub_tree, "selected", 0); - if (is_selected < 0) is_selected = 0; - htrAddWgtrObjLinkage_va(s,sub_tree,"rb%POSoption%POS",id,i); - wgtrGetPropertyValue(sub_tree,"name",DATA_T_STRING,POD(&ptr)); - htrAddScriptInit_va(s, - " var rbitem = wgtrGetNodeRef('%STR&SYM', '%STR&SYM');\n" - " add_radiobutton(rbitem, {selected:%INT, buttonset:htr_subel(rbitem, \"rb%POSbuttonset%POS\"), buttonunset:htr_subel(rbitem, \"rb%POSbuttonunset%POS\"), value:htr_subel(rbitem, \"rb%POSvalue%POS\"), label:htr_subel(rbitem, \"rb%POSlabel%POS\"), valuestr:\"%STR&JSSTR\", labelstr:\"%STR&JSSTR\"});\n", - wgtrGetNamespace(sub_tree), ptr, - is_selected, - id, i, id, i, - id, i, id, i, - value, label); - i++; + mssError(0, "HTRB", "Failed to write option value CSS."); + goto err_option; } - else + if (htrAddStylesheetItem_va(s, + "\t\t#rb%POSlabel%POS { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "left:27px; " + "top:2px; " + "width:calc(100%% - 27px); " + "height:calc(100%% - 2px); " + "z-index:%POS; " + "cursor:pointer; " + "}\n", + id, i, + z + 2 + ) != 0) { - htrRenderWidget(s, sub_tree, z+1); + mssError(0, "HTRB", "Failed to write option label CSS."); + goto err_option; } + + /** Write radio button HTML. **/ + if (htrAddBodyItem_va(s, + "
\n" + " \n" + "
\n" + "
%STR&HTE
\n" + " \n" + "
\n", + id, i, id, + id, i, + id, i, + id, i, textcolor, radio_button_label, + id, i, radio_button_value + ) != 0) + { + mssError(0, "HTRB", "Failed to write option HTML."); + goto err_option; + } + + /** Success. **/ + continue; + + err_option: + mssError(0, "HTRB", "Failed to write option #%d/%d.", i + 1, radio_buttons.nItems); + goto end_free; } - - /* - Do the HTML layers - */ - htrAddBodyItem_va(s,"
\n", id); - htrAddBodyItem_va(s,"
\n", id); - htrAddBodyItem_va(s,"
\n", id); - - /* Loop through each radio button and do the option pane and sub layers */ - i = 1; - for (j=0;jChildren));j++) + + /** Close divs and write title. **/ + if (htrAddBodyItem_va(s, + "
\n" + "
%STR&HTE
\n" + "
\n", + id, textcolor, title + ) != 0) { - radiobutton_obj = xaGetItem(&(tree->Children), j); - wgtrGetPropertyValue(radiobutton_obj,"outer_type",DATA_T_STRING,POD(&ptr)); - if (!strcmp(ptr,"widget/radiobutton")) - { - /** CSS layers **/ - htrAddStylesheetItem_va(s,"\t#rb%POSbuttonset%POS { POSITION:absolute; VISIBILITY:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); CURSOR:pointer; }\n", - id,i,5,2+(s->ClientInfo->ParagraphHeight-12)/2,12,12,z+2,12,12); - htrAddStylesheetItem_va(s,"\t#rb%POSbuttonunset%POS { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); CURSOR:pointer; }\n", - id,i,5,2+(s->ClientInfo->ParagraphHeight-12)/2,12,12,z+2,12,12); - htrAddStylesheetItem_va(s,"\t#rb%POSvalue%POS { POSITION:absolute; VISIBILITY:hidden; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); }\n", - id,i,5,5,12,12,z+2,12,12); - htrAddStylesheetItem_va(s,"\t#rb%POSlabel%POS { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; CLIP:rect(0px,%POSpx,%POSpx,0px); CURSOR:pointer; }\n", - id,i,27,2,cover_width-(27+1),item_spacing-1,z+2,cover_width-(27+1),item_spacing-1); - - /** Body layers **/ - htrAddBodyItem_va(s,"
\n", id, i); - htrAddBodyItem_va(s,"
\n", id, i); - htrAddBodyItem_va(s,"
\n", id, i); - - wgtrGetPropertyValue(radiobutton_obj,"label",DATA_T_STRING,POD(&ptr)); - strtcpy(sbuf2,ptr,sizeof(sbuf2)); - htrAddBodyItem_va(s,"
%STR&HTE
\n", - id, i, textcolor, sbuf2); + mssError(0, "HTRB", "Failed to write title and closing HTML tags."); + goto end_free; + } + + /** Success. **/ + rval = 0; - /* use label (from above) as default value if no value given */ - if(wgtrGetPropertyValue(radiobutton_obj,"value",DATA_T_STRING,POD(&ptr))==0) - { - strtcpy(sbuf2,ptr,sizeof(sbuf2)); - } + end_free: + /** Clean up. **/ + if (radio_buttons.nAlloc != 0) xaDeInit(&radio_buttons); - htrAddBodyItem_va(s," \n", - id, i, sbuf2); - htrAddBodyItem(s, "
\n"); - i++; - } + if (rval != 0) + { + mssError(0, "HTRB", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } - - htrAddBodyItem(s, "
\n"); - htrAddBodyItem(s, "
\n"); - htrAddBodyItem_va(s,"
%STR&HTE
\n", id, textcolor, title); - htrAddBodyItem(s, "
\n"); - return 0; -} + return rval; + } /** htrbInitialize - register with the ht_render module. **/ int htrbInitialize() { pHtDriver drv; + + /** Initialize globals. */ + HTRB.idcnt = 0; /** Allocate the driver **/ drv = htrAllocDriver(); @@ -320,21 +502,9 @@ int htrbInitialize() { strcpy(drv->WidgetName,"radiobuttonpanel"); drv->Render = htrbRender; - /** Events **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - htrAddEvent(drv,"DataChange"); - - /** Register. **/ + /** Register with dhtml support. **/ htrRegisterDriver(drv); - htrAddSupport(drv, "dhtml"); - HTRB.idcnt = 0; - return 0; } diff --git a/centrallix/htmlgen/htdrv_repeat.c b/centrallix/htmlgen/htdrv_repeat.c index b4943abc3..349c33c5b 100644 --- a/centrallix/htmlgen/htdrv_repeat.c +++ b/centrallix/htmlgen/htdrv_repeat.c @@ -1,21 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "cxlib/qprintf.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -42,22 +29,29 @@ /* render its subwidgets. */ /************************************************************************/ +#include -/** Globals **/ +#include "cxlib/mtsession.h" +#include "ht_render.h" +#include "wgtr.h" -static struct - { - int idcnt; - } HTRPT; int htrptRender(pHtSession s, pWgtrNode tree, int z) { /** Render Subwidgets **/ - htrAddWgtrCtrLinkage(s, tree, "_parentctr"); - htrRenderSubwidgets(s,tree,z); + if (htrAddWgtrCtrLinkage(s, tree, "_parentctr") != 0) goto err; + if (htrRenderSubwidgets(s, tree, z) != 0) goto err; - return 0; + /** Success. **/ + return 0; + + err: + mssError(0, "HTRPT", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); + return -1; } int @@ -68,8 +62,6 @@ htrptInitialize() /** Allocate the driver **/ drv = htrAllocDriver(); if (!drv) return -1; - - memset(&HTRPT, 0, sizeof(HTRPT)); /** Fill in structure **/ strcpy(drv->Name,"Repeat Object Driver"); diff --git a/centrallix/htmlgen/htdrv_rule.c b/centrallix/htmlgen/htdrv_rule.c index ec1ee0b4a..b03a2d2d4 100644 --- a/centrallix/htmlgen/htdrv_rule.c +++ b/centrallix/htmlgen/htdrv_rule.c @@ -1,23 +1,8 @@ -#include -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/xstring.h" -#include "cxlib/mtsession.h" -#include "cxlib/qprintf.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2007 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -46,6 +31,19 @@ /* rules are specific to the affected widget. */ /************************************************************************/ +#include +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/newmalloc.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xstring.h" +#include "expression.h" +#include "ht_render.h" +#include "obj.h" +#include "wgtr.h" /*** Rule definition - for other drivers registering rule types @@ -191,8 +189,8 @@ htruleRender(pHtSession s, pWgtrNode tree, int z) htrAddWgtrCtrLinkage(s, tree, "_parentctr"); /** Script Init **/ - htrAddScriptInit_va(s, " rl_init(wgtrGetNodeRef(ns,\"%STR&SYM\"), \"%STR&JSSTR\", %STR);\n", nptr, ruletype, xs->String); - htrAddScriptInit_va(s, " wgtrGetParent(wgtrGetNodeRef(ns,\"%STR&SYM\")).addRule(wgtrGetNodeRef(ns,\"%STR&SYM\"));\n", nptr, nptr); + htrAddScriptInit_va(s, "\trl_init(wgtrGetNodeRef(ns, '%STR&SYM'), '%STR&JSSTR', %STR);\n", nptr, ruletype, xs->String); + htrAddScriptInit_va(s, "\twgtrGetParent(wgtrGetNodeRef(ns, '%STR&SYM')).addRule(wgtrGetNodeRef(ns,'%STR&SYM'));\n", nptr, nptr); /** mark this node as not being associated with a DHTML object **/ tree->RenderFlags |= HT_WGTF_NOOBJECT; diff --git a/centrallix/htmlgen/htdrv_scrollbar.c b/centrallix/htmlgen/htdrv_scrollbar.c index e9b5b65e4..70d500c37 100644 --- a/centrallix/htmlgen/htdrv_scrollbar.c +++ b/centrallix/htmlgen/htdrv_scrollbar.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2003 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,6 +29,16 @@ /* or vertical. */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "expression.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -58,22 +56,22 @@ htsbRender(pHtSession s, pWgtrNode tree, int z) char* ptr; char name[64]; int x,y,w,h,r; - int id, i, t; int visible = 1; char bcolor[64] = ""; char bimage[64] = ""; int is_horizontal = 0; pExpression code; - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom0IE && !s->Capabilities.Dom1HTML) + /** Get an id for this. **/ + const int id = (HTSB.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTSB","Netscape 4.x, IE, or W3C DOM support required"); - return -1; + mssError(1, "HTSB", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTSB.idcnt++); - /** Which direction? **/ if (wgtrGetPropertyValue(tree,"direction",DATA_T_STRING,POD(&ptr)) == 0) { @@ -85,19 +83,19 @@ htsbRender(pHtSession s, pWgtrNode tree, int z) if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) { mssError(1,"HTSB","Scrollbar widget must have an 'x' property"); - return -1; + goto err; } if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) { mssError(1,"HTSB","Scrollbar widget must have a 'y' property"); - return -1; + goto err; } if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { if (is_horizontal) { mssError(1,"HTSB","Horizontal scrollbar widgets must have a 'width' property"); - return -1; + goto err; } else { @@ -107,14 +105,14 @@ htsbRender(pHtSession s, pWgtrNode tree, int z) if (is_horizontal && w <= 18*3) { mssError(1,"HTSB","Horizontal scrollbar width must be greater than %d", 18*3); - return -1; + goto err; } if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) { if (!is_horizontal) { mssError(1,"HTSB","Vertical scrollbar widgets must have a 'height' property"); - return -1; + goto err; } else { @@ -124,11 +122,15 @@ htsbRender(pHtSession s, pWgtrNode tree, int z) if (!is_horizontal && h <= 18*3) { mssError(1,"HTSB","Vertical scrollbar height must be greater than %d", 18*3); - return -1; + goto err; } /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&ptr)) != 0) + { + mssError(1, "HTSB", "Failed to get widget name."); + goto err; + } strtcpy(name,ptr,sizeof(name)); /** Range of scrollbar (static or dynamic property) **/ @@ -136,6 +138,7 @@ htsbRender(pHtSession s, pWgtrNode tree, int z) r = w; else r = h; + int t; if ((t = wgtrGetPropertyType(tree,"range")) == DATA_T_INTEGER) { wgtrGetPropertyValue(tree,"range",DATA_T_INTEGER,POD(&r)); @@ -157,36 +160,75 @@ htsbRender(pHtSession s, pWgtrNode tree, int z) } /** Marked not visible? **/ - if (wgtrGetPropertyValue(tree,"visible",DATA_T_STRING,POD(&ptr)) == 0) - { - if (!strcmp(ptr,"false")) visible = 0; - } + visible = htrGetBoolean(tree, "visible", 1); /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#sb%POSpane { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; clip:rect(0px,%POSpx,%POSpx,0px); Z-INDEX:%POS; }\n",id,visible?"inherit":"hidden",x,y,w,h,w,h, z); - if (is_horizontal) - htrAddStylesheetItem_va(s,"\t#sb%POSthum { POSITION:absolute; VISIBILITY:inherit; LEFT:18px; TOP:0px; WIDTH:18px; Z-INDEX:%POS; }\n",id,z+1); - else - htrAddStylesheetItem_va(s,"\t#sb%POSthum { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:18px; WIDTH:18px; Z-INDEX:%POS; }\n",id,z+1); - - /** Write globals for internal use **/ - htrAddScriptGlobal(s, "sb_target_img", "null", 0); - htrAddScriptGlobal(s, "sb_click_x","0",0); - htrAddScriptGlobal(s, "sb_click_y","0",0); - htrAddScriptGlobal(s, "sb_thum_x","0",0); - htrAddScriptGlobal(s, "sb_thum_y","0",0); - htrAddScriptGlobal(s, "sb_mv_timeout","null",0); - htrAddScriptGlobal(s, "sb_mv_incr","0",0); - htrAddScriptGlobal(s, "sb_cur_mainlayer","null",0); - - /** DOM Linkage **/ - htrAddWgtrObjLinkage_va(s, tree, "sb%POSpane",id); + if (htrAddStylesheetItem_va(s, + "\t\t#sb%POSpane { " + "position:absolute; " + "visibility:%STR; " + "overflow:hidden; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "}\n", + id, + (visible) ? "inherit" : "hidden", + x, y, w, h, + z + ) != 0) + { + mssError(0, "HTSB", "Failed to write scrollbar pane CSS."); + goto err; + } + if (htrAddStylesheetItem_va(s, + "\t\t#sb%POSthum { " + "position:absolute; " + "visibility:inherit; " + "left:%POSpx; " + "top:%POSpx; " + "width:18px; " + "z-index:%POS; " + "}\n", + id, + (is_horizontal) ? 18 : 0, + (is_horizontal) ? 0 : 18, + z + 1 + ) != 0) + { + mssError(0, "HTSB", "Failed to write scrollbar thumb CSS."); + goto err; + } - htrAddScriptInclude(s, "/sys/js/htdrv_scrollbar.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); + /** Write JS globals, linking, includes, etc. **/ + if (htrAddScriptGlobal(s, "sb_click_x","0", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "sb_click_y","0", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "sb_cur_mainlayer","null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "sb_mv_incr","0", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "sb_mv_timeout","null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "sb_target_img", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "sb_thum_x","0", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "sb_thum_y","0", 0) != 0) goto err; + if (htrAddWgtrObjLinkage_va(s, tree, "sb%POSpane", id) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_scrollbar.js", 0) != 0) goto err; /** Script initialization call. **/ - htrAddScriptInit_va(s," sb_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), tname:\"sb%POSthum\", isHorizontal:%INT, range:%INT});\n", name, id, is_horizontal, r); + if (htrAddScriptInit_va(s, + "\tsb_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "tname:'sb%POSthum', " + "isHorizontal:%INT, " + "range:%INT, " + "});\n", + name, id, is_horizontal, r + ) != 0) + { + mssError(0, "HTSB", "Failed to write JS init call."); + goto err; + } /** HTML body
elements for the layers. **/ htrAddBodyItem_va(s,"
", id, *bcolor, bcolor, *bimage, bimage, w); @@ -206,19 +248,30 @@ htsbRender(pHtSession s, pWgtrNode tree, int z) /** Add the event handling scripts **/ - htrAddEventHandlerFunction(s, "document","MOUSEDOWN","sb","sb_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE","sb","sb_mousemove"); - htrAddEventHandlerFunction(s, "document","MOUSEUP","sb","sb_mouseup"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "sb","sb_mouseover"); + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "sb", "sb_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "sb", "sb_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "sb", "sb_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "sb", "sb_mouseup") != 0) goto err; - /** Check for more sub-widgets within the scrollbar (visual ones not allowed). **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+2); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 2) != 0) goto err; + + /** Write the closing HTML
tag. **/ + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTSB", "Failed to write HTML closing tag."); + goto err; + } - /** Finish off the last
**/ - htrAddBodyItem(s,"
\n"); + /** Success. **/ + return 0; - return 0; + err: + mssError(0, "HTSB", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } @@ -238,17 +291,6 @@ htsbInitialize() strcpy(drv->WidgetName,"scrollbar"); drv->Render = htsbRender; - /** Events **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - - htrAddAction(drv,"MoveTo"); - htrAddParam(drv,"MoveTo","Value",DATA_T_INTEGER); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_scrollpane.c b/centrallix/htmlgen/htdrv_scrollpane.c index db1a6ec40..4e3a24477 100644 --- a/centrallix/htmlgen/htdrv_scrollpane.c +++ b/centrallix/htmlgen/htdrv_scrollpane.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -42,6 +30,14 @@ /* layer. Can contain most objects, except for framesets. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -56,145 +52,275 @@ static struct int htspaneRender(pHtSession s, pWgtrNode tree, int z) { - char* ptr; - char name[64]; - int x,y,w,h; - int id, i; - int visible = 1; - char bcolor[64] = ""; - char bimage[64] = ""; - - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom0IE &&!(s->Capabilities.Dom1HTML && s->Capabilities.Dom2CSS)) + char* str; + + /** Get an id for this scrollpane. **/ + const int id = (HTSPANE.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTSPANE","Netscape DOM or W3C DOM1 HTML and DOM2 CSS support required"); - return -1; + mssError(1, "HTSPANE", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - - /** Get an id for this. **/ - id = (HTSPANE.idcnt++); - - /** Get x,y,w,h of this object **/ - if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) + + /** Get name. **/ + char name[64]; + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&str)) != 0) { - mssError(1,"HTSPANE","ScrollPane widget must have an 'x' property"); - return -1; + mssError(1, "HTSPANE", "widget/scrollpane must have a 'name' property."); + goto err; } - if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) + strtcpy(name, str, sizeof(name)); + + /** Get layout data (required). **/ + int x, y, w, h; + if (wgtrGetPropertyValue(tree, "x", DATA_T_INTEGER, POD(&x)) != 0) { - mssError(1,"HTSPANE","ScrollPane widget must have a 'y' property"); - return -1; + mssError(1, "HTSPANE", "widget/scrollpane must have an 'x' property."); + goto err; } - if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) + if (wgtrGetPropertyValue(tree, "y", DATA_T_INTEGER, POD(&y)) != 0) { - mssError(1,"HTSPANE","ScrollPane widget must have a 'width' property"); - return -1; + mssError(1, "HTSPANE", "widget/scrollpane must have a 'y' property."); + goto err; } - if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) + if (wgtrGetPropertyValue(tree, "width", DATA_T_INTEGER, POD(&w)) != 0) { - mssError(1,"HTSPANE","ScrollPane widget must have a 'height' property"); - return -1; + mssError(1, "HTSPANE", "widget/scrollpane must have a 'width' property."); + goto err; } - - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(name,ptr,sizeof(name)); - - /** Check background color **/ - if (wgtrGetPropertyValue(tree,"bgcolor",DATA_T_STRING,POD(&ptr)) == 0) + if (wgtrGetPropertyValue(tree, "height", DATA_T_INTEGER, POD(&h)) != 0) { - strtcpy(bcolor,ptr,sizeof(bcolor)); + mssError(1, "HTSPANE", "widget/scrollpane must have a 'height' property."); + goto err; } - if (wgtrGetPropertyValue(tree,"background",DATA_T_STRING,POD(&ptr)) == 0) + + /** Get the background color or image. **/ + char background_color[64] = ""; + if (wgtrGetPropertyValue(tree, "bgcolor", DATA_T_STRING, POD(&str)) == 0) { - strtcpy(bimage,ptr,sizeof(bimage)); + strtcpy(background_color, str, sizeof(background_color)); } - - /** Marked not visible? **/ - if (wgtrGetPropertyValue(tree,"visible",DATA_T_STRING,POD(&ptr)) == 0) + char background_image[64] = ""; + if (wgtrGetPropertyValue(tree, "background", DATA_T_STRING, POD(&str)) == 0) { - if (!strcmp(ptr,"false")) visible = 0; + strtcpy(background_image, str, sizeof(background_image)); } - - /** Ok, write the style header items. **/ - if (s->Capabilities.Dom0NS) + + /** Get visibility. **/ + const int visible = htrGetBoolean(tree, "visible", 1); + if (visible == -1) goto err; + + /** Link the widget and container to DOM nodes. **/ + if (htrAddWgtrCtrLinkage_va(s, tree, "htr_subel(_obj, 'sp%POSarea')", id) != 0) { - htrAddStylesheetItem_va(s,"\t#sp%POSpane { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; clip:rect(0px,%POSpx,%POSpx,0px); Z-INDEX:%POS; }\n",id,visible?"inherit":"hidden",x,y,w,h,w,h, z); - htrAddStylesheetItem_va(s,"\t#sp%POSarea { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:0px; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,w-18,z+1); - htrAddStylesheetItem_va(s,"\t#sp%POSthum { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:18px; WIDTH:18px; Z-INDEX:%POS; }\n",id,w-18,z+1); + mssError(0, "HTSPANE", "Failed to add container linkage."); + goto err; } - - /** Write globals for internal use **/ - htrAddScriptGlobal(s, "sp_target_img", "null", 0); - htrAddScriptGlobal(s, "sp_click_x","0",0); - htrAddScriptGlobal(s, "sp_click_y","0",0); - htrAddScriptGlobal(s, "sp_thum_y","0",0); - htrAddScriptGlobal(s, "sp_mv_timeout","null",0); - htrAddScriptGlobal(s, "sp_mv_incr","0",0); - htrAddScriptGlobal(s, "sp_cur_mainlayer","null",0); - - /** DOM Linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "sp%POSpane",id); - htrAddWgtrCtrLinkage_va(s, tree, "htr_subel(_obj, \"sp%POSarea\")",id); - - htrAddScriptInclude(s, "/sys/js/htdrv_scrollpane.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - - htrAddScriptInit_va(s," sp_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), aname:\"sp%POSarea\", tname:\"sp%POSthum\"});\n", name,id,id); - - /** HTML body
elements for the layers. **/ - if(s->Capabilities.Dom0NS) - { - htrAddBodyItem_va(s,"
", id, *bcolor, bcolor, *bimage, bimage, w); - htrAddBodyItem(s, "
"); - htrAddBodyItem_va(s,"",h-36); - htrAddBodyItem(s, "
\n"); - htrAddBodyItem_va(s,"
\n
",id,id,w-2,h-2); - } - else if(s->Capabilities.Dom1HTML) - { - //htrAddStylesheetItem_va(s,"\t#sp%dpane { POSITION:absolute; VISIBILITY:%s; LEFT:%dpx; TOP:%dpx; WIDTH:%dpx; HEIGHT:%dpx; clip:rect(0px,%dpx,%dpx,0px); Z-INDEX:%d; }\n",id,visible?"inherit":"hidden",x,y,w,h,w,h, z); - //htrAddStylesheetItem_va(s,"\t#sp%darea { HEIGHT: %dpx; WIDTH:%dpx; }\n",id, h, w-18); - //htrAddStylesheetItem_va(s,"\t#sp%dthum { POSITION:absolute; VISIBILITY:inherit; LEFT:%dpx; TOP:18px; WIDTH:18px; Z-INDEX:%dpx; }\n",id,w-18,z+1); - htrAddBodyItem_va(s,"
\n",id,visible?"inherit":"hidden",x,y,w,h,w,h,z); - htrAddBodyItem_va(s,"", id); - htrAddBodyItem_va(s,"", id); - htrAddBodyItem_va(s,"", id); - htrAddStylesheetItem_va(s,"\t#sp%POSup { POSITION: absolute; LEFT: %INTpx; TOP: 0px; }\n",id, w-18); - htrAddStylesheetItem_va(s,"\t#sp%POSbar { POSITION: absolute; LEFT: %INTpx; TOP: 18px; WIDTH: 18px; HEIGHT: %POSpx;}\n",id, w-18, h-36); - htrAddStylesheetItem_va(s,"\t#sp%POSdown { POSITION: absolute; LEFT: %INTpx; TOP: %INTpx; }\n",id, w-18, h-18); - htrAddBodyItem_va(s,"
\n", id,w-18,z+1); - htrAddBodyItem_va(s,"
",id,h,w-18,z+1); - } - else - { - mssError(1,"HTSPNE","Browser not supported"); + if (htrAddWgtrObjLinkage_va(s, tree, "sp%POSpane", id) != 0) + { + mssError(0, "HTSPANE", "Failed to add object linkage."); + goto err; } - + + /** Include scrollpane script and its dependencies. **/ + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_scrollpane.js", 0) != 0) goto err; + /** Add the event handling scripts **/ - htrAddEventHandlerFunction(s, "document","MOUSEDOWN","sp","sp_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE","sp","sp_mousemove"); - htrAddEventHandlerFunction(s, "document","MOUSEUP","sp","sp_mouseup"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "sp","sp_mouseover"); - - /** Do subwidgets **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+2); - - /** Finish off the last
**/ - if(s->Capabilities.Dom0NS) + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "sp", "sp_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "sp", "sp_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "sp", "sp_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "sp", "sp_mouseup") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "WHEEL", "sp", "sp_wheel") != 0) goto err; + + /** Init the JS scripts. **/ + if (htrAddScriptInit_va(s, + "\tsp_init({" + "layer: wgtrGetNodeRef(ns, '%STR&SYM'), " + "area_name: 'sp%POSarea', " + "thumb_name: 'sp%POSthumb', " + "});\n", + name, id, id + ) != 0) { - htrAddBodyItem(s,"
\n"); + mssError(0, "HTSPANE", "Failed to write JS init call."); + goto err; } - else if(s->Capabilities.Dom1HTML) + + /** Write the scrollpane. **/ + if (htrAddBodyItem_va(s, + "
\n", + id, + (visible) ? "inherit" : "hidden", + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + ht_flex_h(h, tree), + z + ) != 0) { - htrAddBodyItem(s,"
\n"); + mssError(0, "HTSPANE", "Failed to write HTML for main scrollpane div."); + goto err; } - else + + /** Write shared CSS for the following UI elements. **/ + if (htrAddStylesheetItem_va(s, + "\t\t.sp%POS_scroll { " + "position:absolute; " + "left:"ht_flex_format"; " + "}\n", + id, + ht_flex(w - 18, tree->width, 1.0) + ) != 0) { - mssError(1,"HTSPNE","browser not supported"); + mssError(0, "HTSPANE", "Failed to write shared CSS ui elements."); + goto err; } - - return 0; + + /** Write the up button. **/ + if (htrAddBodyItem_va(s, + "", + id, + id + ) != 0) + { + mssError(0, "HTSPANE", "Failed to write HTML for the up button."); + goto err; + } + + /** Write the scroll bar. **/ + if (htrAddBodyItem_va(s, + "", + id, + id, + ht_flex(h - 36, tree->height, 1.0) + ) != 0) + { + mssError(0, "HTSPANE", "Failed to write HTML for the scroll bar."); + goto err; + } + + /** Write the down button. **/ + if (htrAddBodyItem_va(s, + "", + id, + id, + ht_flex(h - 18, tree->height, 1.0) + ) != 0) + { + mssError(0, "HTSPANE", "Failed to write HTML for the down button."); + goto err; + } + + /** Write the scroll thumb. **/ + if (htrAddBodyItem_va(s, + "
" + "scroll_thumb" + "
\n", + id, + id, + z + 1 + ) != 0) + { + mssError(0, "HTSPANE", "Failed to write HTML for the scroll thumb."); + goto err; + } + + /** Write the scroll area. **/ + if (htrAddBodyItem_va(s, + "
", + id, + ht_flex(w - 18, tree->width, 1.0), + ht_flex(h, tree->height, 1.0), + z + 1 + ) != 0) + { + mssError(0, "HTSPANE", "Failed to write HTML for the scroll area."); + goto err; + } + + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 2) != 0) goto err; + + /** Close the final
s. **/ + if (htrAddBodyItem(s,"
\n") != 0) + { + mssError(0, "HTSPANE", "Failed to write closing HTML tags."); + goto err; + } + + return 0; + + err: + mssError(0, "HTSPANE", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } @@ -204,29 +330,21 @@ int htspaneInitialize() { pHtDriver drv; - - /** Allocate the driver **/ + + /** Allocate the driver **/ drv = htrAllocDriver(); if (!drv) return -1; - + /** Fill in the structure. **/ - strcpy(drv->Name,"HTML ScrollPane Widget Driver"); + strcpy(drv->Name,"HTML Widget/scrollpane Driver"); strcpy(drv->WidgetName,"scrollpane"); drv->Render = htspaneRender; - - /** Events **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - + /** Register. **/ htrRegisterDriver(drv); - + htrAddSupport(drv, "dhtml"); HTSPANE.idcnt = 0; - + return 0; } diff --git a/centrallix/htmlgen/htdrv_spinner.c b/centrallix/htmlgen/htdrv_spinner.c index 7252b86a5..583eaafc4 100644 --- a/centrallix/htmlgen/htdrv_spinner.c +++ b/centrallix/htmlgen/htdrv_spinner.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,15 @@ /* Description: HTML Widget driver for a single-line editbox. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + + /*This file was based on the edit box file revision 1.2*/ @@ -65,7 +62,6 @@ htspnrRender(pHtSession s, pWgtrNode tree, int z) int is_raised = 1; char* c1; char* c2; - int maxchars; if(!s->Capabilities.Dom0NS) { @@ -89,9 +85,6 @@ htspnrRender(pHtSession s, pWgtrNode tree, int z) mssError(1,"HTSPNR","Spinner widget must have a 'height' property"); return -1; } - - /** Maximum characters to accept from the user **/ - if (wgtrGetPropertyValue(tree,"maxchars",DATA_T_INTEGER,POD(&maxchars)) != 0) maxchars=255; /** Background color/image? **/ htrGetBackground(tree,NULL,!s->Capabilities.Dom0NS, main_bg, sizeof(main_bg)); @@ -114,12 +107,12 @@ htspnrRender(pHtSession s, pWgtrNode tree, int z) } /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#spnr%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",id,x,y,w,z); - htrAddStylesheetItem_va(s,"\t#spnr%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",id,1,1,w-12,z); - htrAddStylesheetItem_va(s,"\t#spnr%POScon1 { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",id,1,1,w-2-12,z+1); - htrAddStylesheetItem_va(s,"\t#spnr%POScon2 { POSITION:absolute; VISIBILITY:hidden; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",id,1,1,w-2-12,z+1); - htrAddStylesheetItem_va(s,"\t#spnr_button_up { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",1+w-12,1,w,z); - htrAddStylesheetItem_va(s,"\t#spnr_button_down { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; Z-INDEX:%POS; }\n",1+w-12,1+9,w,z); + htrAddStylesheetItem_va(s, "\t\t#spnr%POSmain { position:absolute; visibility:inherit; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", id, x, y, w, z); + htrAddStylesheetItem_va(s, "\t\t#spnr%POSbase { position:absolute; visibility:inherit; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", id, 1, 1, w-12, z); + htrAddStylesheetItem_va(s, "\t\t#spnr%POScon1 { position:absolute; visibility:inherit; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", id, 1, 1, w-2-12, z+1); + htrAddStylesheetItem_va(s, "\t\t#spnr%POScon2 { position:absolute; visibility:hidden; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", id, 1, 1, w-2-12, z+1); + htrAddStylesheetItem_va(s, "\t\t#spnr_button_up { position:absolute; visibility:inherit; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", 1+w-12, 1, w, z); + htrAddStylesheetItem_va(s, "\t\t#spnr_button_down { position:absolute; visibility:inherit; left:%INT; top:%INT; width:%POS; z-index:%POS; }\n", 1+w-12, 1+9,w, z); /** DOM Linkage **/ htrAddWgtrObjLinkage_va(s, tree, "spnr%POSmain",id); @@ -134,13 +127,16 @@ htspnrRender(pHtSession s, pWgtrNode tree, int z) htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "spnr", "spnr_mousedown"); /** Script initialization call. **/ - htrAddScriptInit_va(s, - " var spnr = wgtrGetNodeRef(ns, \"%STR&SYM\");\n" - " spnr_init({main:spnr, layer:htr_subel(spnr,\"spnr%POSbase\"), c1:htr_subel(htr_subel(spnr,\"spnr%POSbase\"),\"spnr%POScon1\"), c2:htr_subel(htr_subel(spnr,\"spnr%POSbase\"),\"spnr%POScon2\")});\n", - name, - id, - id, id, - id, id); + htrAddScriptInit_va(s, "\t{ " + "const main = wgtrGetNodeRef(ns, '%STR&SYM'); " + "const layer = htr_subel(main, 'spnr%POSbase'); " + "spnr_init({ " + "main, layer, " + "c1:htr_subel(layer,'spnr%POScon1'), " + "c2:htr_subel(layer,'spnr%POScon2'), " + "}); }\n", + name, id, id, id + ); /** HTML body
element for the base layer. **/ htrAddBodyItem_va(s, "
\n",id); @@ -182,17 +178,6 @@ htspnrInitialize() strcpy(drv->Name,"DHTML Spinner Box Driver"); strcpy(drv->WidgetName,"spinner"); drv->Render = htspnrRender; - - - /** Add a 'set value' action **/ - htrAddAction(drv,"SetValue"); - htrAddParam(drv,"SetValue","Value",DATA_T_STRING); /* value to set it to */ - htrAddParam(drv,"SetValue","Trigger",DATA_T_INTEGER); /* whether to trigger the Modified event */ - - /** Value-modified event **/ - htrAddEvent(drv,"Modified"); - htrAddParam(drv,"Modified","NewValue",DATA_T_STRING); - htrAddParam(drv,"Modified","OldValue",DATA_T_STRING); /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_tab.c b/centrallix/htmlgen/htdrv_tab.c index 9b029bf6e..6153de9b5 100644 --- a/centrallix/htmlgen/htdrv_tab.c +++ b/centrallix/htmlgen/htdrv_tab.c @@ -1,21 +1,8 @@ -#include -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,6 +28,17 @@ /* Description: HTML Widget driver for a tab control. */ /************************************************************************/ +#include +#include +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -58,315 +56,750 @@ enum httab_locations { Top=0, Bottom=1, Left=2, Right=3, None=4 }; int httabRender(pHtSession s, pWgtrNode tree, int z) { - char* ptr; - char* type,*field; + char* ptr; int tmp = 0; + char* page_type = NULL; + char* field = NULL; char name[64]; - char tab_txt[128]; + char text_color[128]; char main_bg[128]; char inactive_bg[128]; - char page_type[32]; char fieldname[128]; - char sel[128]; - int sel_idx= -1; - pWgtrNode tabpage_obj; - int x=-1,y=-1,w,h; - int id,tabcnt, i, j; - char* subnptr; - enum httab_locations tloc; - int tab_width = 0; - int xoffset,yoffset,xtoffset, ytoffset; - int is_selected; - char* bg; + int sel_idx = -1; + int x = -1, y = -1; + int w, h; /* width & height of the tab control. */ + int tab_w = 0, tab_h = 0; + int is_auto_tab_w = 0; /* 1 if tab_w should be computed client-side. */ + int xoffset, yoffset, xtoffset, ytoffset; char* tabname; pWgtrNode children[32]; int border_radius; char border_style[32]; char border_color[64]; int border_width; - int shadow_offset, shadow_radius, shadow_angle; - char shadow_color[128]; - - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom0IE &&(!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS)) + + /** Reserve the next tab widget ID. **/ + const int id = (HTTAB.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTTAB","NS4 or W3C DOM Support required"); - return -1; + mssError(1, "HTTAB", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - - /** Get an id for this. **/ - id = (HTTAB.idcnt++); - - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(name,ptr,sizeof(name)); - - /** Get x,y,w,h of this object **/ - if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; - if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; - if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) + + /** Get the tab widget name. **/ + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&ptr)) != 0) { - mssError(0,"HTTAB","Tab widget must have a 'width' property"); - return -1; + mssError(1, "HTTAB", "Tab widget has no name?!"); + goto err; } - if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) + strtcpy(name, ptr, sizeof(name)); + + /** Get x, y, w, & h of this object. **/ + if (wgtrGetPropertyValue(tree, "x", DATA_T_INTEGER, POD(&x)) != 0) x = 0; + if (wgtrGetPropertyValue(tree, "y", DATA_T_INTEGER, POD(&y)) != 0) y = 0; + if (wgtrGetPropertyValue(tree, "width", DATA_T_INTEGER, POD(&w)) != 0) { - mssError(0,"HTTAB","Tab widget must have a 'height' property"); - return -1; + mssError(1, "HTTAB", "Tab widget must have a 'width' property"); + goto err; } - - /** Drop shadow **/ - shadow_offset=0; + if (wgtrGetPropertyValue(tree, "height", DATA_T_INTEGER, POD(&h)) != 0) + { + mssError(1, "HTTAB", "Tab widget must have a 'height' property"); + goto err; + } + + /** Get drop shadow data. **/ + int shadow_offset = 0, shadow_radius = 0, shadow_angle = 135; + char shadow_color[128]; if (wgtrGetPropertyValue(tree, "shadow_offset", DATA_T_INTEGER, POD(&shadow_offset)) == 0 && shadow_offset > 0) shadow_radius = shadow_offset+1; - else - shadow_radius = 0; wgtrGetPropertyValue(tree, "shadow_radius", DATA_T_INTEGER, POD(&shadow_radius)); + wgtrGetPropertyValue(tree, "shadow_angle", DATA_T_INTEGER, POD(&shadow_angle)); strcpy(shadow_color, "black"); if (shadow_radius > 0) { if (wgtrGetPropertyValue(tree, "shadow_color", DATA_T_STRING, POD(&ptr)) == 0) strtcpy(shadow_color, ptr, sizeof(shadow_color)); } - if (wgtrGetPropertyValue(tree, "shadow_angle", DATA_T_INTEGER, POD(&shadow_angle)) != 0) - shadow_angle = 135; - - /** Border radius, color, and style. **/ - if (wgtrGetPropertyValue(tree,"border_radius",DATA_T_INTEGER,POD(&border_radius)) != 0) - border_radius=0; - if (wgtrGetPropertyValue(tree,"border_color",DATA_T_STRING,POD(&ptr)) != 0) + const double shadow_x = sin(shadow_angle * M_PI/180) * shadow_offset; + const double shadow_y = cos(shadow_angle * M_PI/180) * -shadow_offset; + + /** Get border info (radius, color, and style). **/ + if (wgtrGetPropertyValue(tree, "border_radius", DATA_T_INTEGER, POD(&border_radius)) != 0) + border_radius = 0; + if (wgtrGetPropertyValue(tree, "border_color", DATA_T_STRING, POD(&ptr)) != 0) strcpy(border_color, "#ffffff"); else strtcpy(border_color, ptr, sizeof(border_color)); - if (wgtrGetPropertyValue(tree,"border_style",DATA_T_STRING,POD(&ptr)) != 0) + if (wgtrGetPropertyValue(tree, "border_style", DATA_T_STRING,POD(&ptr)) != 0) strcpy(border_style, "outset"); else strtcpy(border_style, ptr, sizeof(border_style)); if (!strcmp(border_style, "none") || !strcmp(border_style, "hidden")) - border_width=0; + border_width = 0; else - border_width=1; - - /** Which side are the tabs on? **/ - if (wgtrGetPropertyValue(tree,"tab_location",DATA_T_STRING,POD(&ptr)) == 0) + border_width = 1; + + /** Get tab_location. **/ + enum httab_locations tloc = Top; + if (wgtrGetPropertyValue(tree, "tab_location", DATA_T_STRING, POD(&ptr)) == 0) { - if (!strcasecmp(ptr,"top")) tloc = Top; - else if (!strcasecmp(ptr,"bottom")) tloc = Bottom; - else if (!strcasecmp(ptr,"left")) tloc = Left; - else if (!strcasecmp(ptr,"right")) tloc = Right; - else if (!strcasecmp(ptr,"none")) tloc = None; + if (strcasecmp(ptr, "none") == 0) tloc = None; + else if (strcasecmp(ptr, "top") == 0) tloc = Top; + else if (strcasecmp(ptr, "bottom") == 0) tloc = Bottom; + else if (strcasecmp(ptr, "left") == 0) tloc = Left; + else if (strcasecmp(ptr, "right") == 0) tloc = Right; else { mssError(1,"HTTAB","%s: '%s' is not a valid tab_location",name,ptr); - return -1; + goto err; + } + } + + /** Count the number of tabs. **/ + const int tab_count = wgtrGetMatchingChildList(tree, "widget/tabpage", children, sizeof(children) / sizeof(pWgtrNode)); + + /** Get the selected tab. **/ + if (wgtrGetPropertyType(tree, "selected") == DATA_T_STRING && + wgtrGetPropertyType(tree, "selected_index") == DATA_T_INTEGER) + { + mssError(1,"HTTAB","%s: cannot specify both 'selected' and 'selected_index'", name); + goto err; + } + if (wgtrGetPropertyValue(tree, "selected", DATA_T_STRING, POD(&ptr)) == 0) + { + /** Search for the tab with the indicated name. **/ + for (unsigned int i = 0; i < tab_count; i++) + { + char* tab_name; + wgtrGetPropertyValue(children[i], "name", DATA_T_STRING, POD(&tab_name)); + if (strcmp(ptr, tab_name) == 0) + { + /** sel_idx is 1 based, but i is 0 based. **/ + sel_idx = i + 1; + break; + } + } + if (sel_idx == -1) { + mssError(1, "HTTAB", "Failed to find tab named '%s'", ptr); + + /** Attempt to give hint. **/ + if (tab_count > 0) + { + char* example_tab_name; + wgtrGetPropertyValue(children[0], "name", DATA_T_STRING, POD(&example_tab_name)); + mssError(0, "HTTAB", "Hint: 'selected' should be a tab name, such as \"%s\".", example_tab_name); + } + + /** Fail. **/ + goto err; + } + } + else if (wgtrGetPropertyValue(tree, "selected_index", DATA_T_INTEGER, POD(&sel_idx)) == 0) + { + if (sel_idx <= 0) + { + mssError(1, "HTTAB", "Invalid value for 'selected_index': %d.", sel_idx); + if (sel_idx == 0) mssError(0, "HTTAB", "Hint: 'selected_index' is 1-based."); + goto err; + } + if (sel_idx > tab_count) + { + mssError(1, "HTTAB", + "Invalid value for 'selected_index': %d. Tab control only has %d tab%s.", + sel_idx, tab_count, (tab_count == 1) ? "" : "s" + ); + goto err; } } else { - tloc = Top; + /** No specified selected tab, default to the first one. **/ + sel_idx = 1; } - - /** How wide should left/right tabs be? **/ - if (wgtrGetPropertyValue(tree,"tab_width",DATA_T_INTEGER,POD(&tab_width)) != 0) + + /** Handle user expressions for the selected tab. **/ + if (htrCheckAddExpression(s, tree, name, "selected") < 0) goto err; + if (htrCheckAddExpression(s, tree, name, "selected_index") < 0) goto err; + + /** Get the background colors/images. **/ + htrGetBackground(tree, NULL, s->Capabilities.Dom2CSS, main_bg, sizeof(main_bg)); + if (htrGetBackground(tree, "inactive", s->Capabilities.Dom2CSS, inactive_bg, sizeof(inactive_bg)) != 0) + strcpy(inactive_bg, main_bg); + + /** Get the text color. **/ + if (wgtrGetPropertyValue(tree, "textcolor", DATA_T_STRING, POD(&ptr)) == 0 + && !strpbrk(ptr, "{};&<>\"\'")) + strtcpy(text_color, ptr, sizeof(text_color)); + else + strcpy(text_color,"black"); + + /** Get the tab spacing and tab height. **/ + /** tab_w and tab_h are left as 0 if unset to tell the front end to calculate them dynamically. **/ + int tab_spacing = 2; /* Default to a 2px gap between tabs. */ + if (wgtrGetPropertyValue(tree, "tab_spacing", DATA_T_INTEGER, POD(&tmp)) == 0) tab_spacing = tmp; + if (wgtrGetPropertyValue(tree, "tab_width", DATA_T_INTEGER, POD(&tmp)) == 0) { - if (tloc == Right || tloc == Left) + if (tmp <= 0) { - mssError(1,"HTTAB","%s: tab_width must be specified with tab_location of left or right", name); - return -1; + mssError(1, "HTTAB", "%s: 'tab_width' expected positive nonzero int, got %d.", name, tmp); + goto err; } + tab_w = tmp; + } + else if (tloc == Right || tloc == Left) + { + mssError(1, "HTTAB", "%s: 'tab_width' must be specified for 'tab_location' of left or right", name); + goto err; } else { - if (tab_width < 0) tab_width = 0; + /** Use a default value, updated client side. */ + tab_w = 80; + is_auto_tab_w = 1; } - - /** Which tab is selected? **/ - if (wgtrGetPropertyType(tree,"selected") == DATA_T_STRING && - wgtrGetPropertyValue(tree,"selected",DATA_T_STRING,POD(&ptr)) == 0) + if (wgtrGetPropertyValue(tree, "tab_height", DATA_T_INTEGER, POD(&tmp)) == 0) { - strtcpy(sel,ptr, sizeof(sel)); + if (tmp <= 0) + { + mssError(1, "HTTAB", "%s: 'tab_height' expected positive nonzero int, got %d.", name, tmp); + goto err; + } + tab_h = tmp; } else { - strcpy(sel,""); + /** Use default value, no client side calculation available. **/ + tab_h = 24; } - if (wgtrGetPropertyValue(tree,"selected_index", DATA_T_INTEGER, POD(&sel_idx)) != 0) + + /** Get macro selection translation values. **/ + /** CHANGE: Code previously used 1, but I think 0 is a better looking default. **/ + int along, out; + if (wgtrGetPropertyValue(tree, "select_translate_along", DATA_T_INTEGER, POD(&along)) != 0) along = 0; + if (wgtrGetPropertyValue(tree, "select_translate_out", DATA_T_INTEGER, POD(&out)) != 0) out = 2; + + /*** Tab strip thickness along whichever edge has it. The strip is + *** inside our box, using space given to the tab control by apos.c. + *** See inset declarations in wgtdrv_tab.c. + ***/ + const int strip_w = (tloc == Left || tloc == Right) ? tab_w : 0; + const int strip_h = (tloc == Top || tloc == Bottom) ? tab_h : 0; + + /** Determine offset to actual tab pages and offsets for selected tabs. **/ + int select_x_offset = 0, select_y_offset = 0; + switch (tloc) { - sel_idx = -1; + /*** Shift to cover boarder line: + *** Top: ytoffset +1 + *** Bottom: ytoffset -2 + *** Left: xtoffset +1 + *** Right: xtoffset -2 + ***/ + case Top: xoffset = 0; yoffset = strip_h; xtoffset = 0; ytoffset = 0; select_x_offset = -along; select_y_offset = -out; break; + case Bottom: xoffset = 0; yoffset = 0; xtoffset = 0; ytoffset = h-strip_h-1; select_x_offset = -along; select_y_offset = +out; break; + case Left: xoffset = strip_w; yoffset = 0; xtoffset = 0; ytoffset = 0; select_x_offset = -out; select_y_offset = -along; break; + case Right: xoffset = 0; yoffset = 0; xtoffset = w-strip_w-1; ytoffset = 0; select_x_offset = +out; select_y_offset = -along; break; + case None: xoffset = 0; yoffset = 0; xtoffset = 0; ytoffset = 0; select_x_offset = 0; select_y_offset = 0; break; + default: mssError(1, "HTTAB", "Unexpected tab location value: %d", tloc); goto err; } - if (sel_idx != -1 && *sel != '\0') + + /** Get coordinate-based selection translation values. **/ + if (wgtrGetPropertyValue(tree, "select_translate_x", DATA_T_INTEGER, POD(&tmp)) == 0) select_x_offset = tmp; + if (wgtrGetPropertyValue(tree, "select_translate_y", DATA_T_INTEGER, POD(&tmp)) == 0) select_y_offset = tmp; + + /*** Apply the opposite of the selection offset to all tabs. This + *** prevents the offset from causing the selected tab to appear + *** detached from the tab control. + ***/ + xtoffset -= select_x_offset; + ytoffset -= select_y_offset; + + /** Get the rendering type (debugging feature). **/ + /** Allows the developer to turn off JS client-side widget rendering for testing. **/ + int do_client_rendering = 1; + if (wgtrGetPropertyValue(tree, "rendering", DATA_T_STRING, POD(&ptr)) == 0) { - mssError(1,"HTTAB","%s: cannot specify both 'selected' and 'selected_index'", name); - return -1; + if (strcmp(ptr, "server-side") == 0) do_client_rendering = 0; + else if (strcmp(ptr, "client-side") == 0) do_client_rendering = 1; + else + { + mssError(1, "HTTAB", "Unknown value for 'rendering': %s", ptr); + mssError(0, "HTTAB", "HINT: Should be either 'server-side' or 'client-size'."); + goto err; + } } - - /** User requesting expression for selected tab? **/ - htrCheckAddExpression(s, tree, name, "selected"); - - /** User requesting expression for selected tab using integer index value? **/ - htrCheckAddExpression(s, tree, name, "selected_index"); - - /** Background color/image? **/ - htrGetBackground(tree, NULL, s->Capabilities.Dom2CSS, main_bg, sizeof(main_bg)); - - /** Inactive tab color/image? **/ - htrGetBackground(tree, "inactive", s->Capabilities.Dom2CSS, inactive_bg, sizeof(inactive_bg)); - - /** Text color? **/ - if (wgtrGetPropertyValue(tree,"textcolor",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(tab_txt, ptr, sizeof(tab_txt)); - else - strcpy(tab_txt,"black"); - if (strpbrk(tab_txt, "{};&<>\"\'")) - strcpy(tab_txt,"black"); - - /** Determine offset to actual tab pages **/ - switch(tloc) + if (!do_client_rendering && is_auto_tab_w && (tloc == Top || tloc == Bottom)) { - case Top: xoffset = 0; yoffset = 24; xtoffset = 0; ytoffset = 0; break; - case Bottom: xoffset = 0; yoffset = 0; xtoffset = 0; ytoffset = h; break; - case Right: xoffset = 0; yoffset = 0; xtoffset = w; ytoffset = 0; break; - case Left: xoffset = tab_width; yoffset = 0; xtoffset = 0; ytoffset = 0; break; - case None: xoffset = 0; yoffset = 0; xtoffset = 0; ytoffset = 0; + /*** The widget specifies server-side rendering for Top/Bottom tabs + *** with dynamic width. This will probably look broken. + ***/ + fprintf(stderr, "WARNING: " + "'rendering' value of \"server-side\" will break on tabs with " + "dynamic widths because they cannot be calculated server-side!\n" + ); + fprintf(stderr, "HINT: Specify the 'tab_width' attribute or use \"client-side\" rendering.\n"); } - - /** Ok, write the style header items. **/ - htrAddStylesheetItem_va(s,"\t#tc%POSbase { background-position: 0px -24px; %STR }\n", id, main_bg); - - /** DOM Linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "tc%POSbase",id); - - /** Script include **/ - htrAddScriptInclude(s, "/sys/js/htdrv_tab.js", 0); - - /** Add a global for the master tabs listing **/ - htrAddScriptGlobal(s, "tc_tabs", "null", 0); - htrAddScriptGlobal(s, "tc_cur_mainlayer", "null", 0); - - /** Event handler for click-on-tab **/ - htrAddEventHandlerFunction(s, "document","MOUSEDOWN","tc","tc_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEUP","tc","tc_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE","tc","tc_mousemove"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER","tc","tc_mouseover"); - + + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "tc%POSctrl", id) != 0) + { + mssError(0, "HTTAB", "Failed to add object linkage to tab."); + goto err; + } + + /** Include the htdrv_tab.js script. **/ + if (htrAddScriptInclude(s, "/sys/js/htdrv_tab.js", 0) != 0) goto err; + + /** Add globals for the master tabs listing. **/ + if (htrAddScriptGlobal(s, "tc_cur_mainlayer", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "tc_tabs", "null", 0) != 0) goto err; + + /** Add mouse event handlers. **/ + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "tc", "tc_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "tc", "tc_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "tc", "tc_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "tc", "tc_mouseup") != 0) goto err; + /** Script initialization call. **/ - htrAddScriptInit_va(s," tc_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), tloc:%INT, mainBackground:\"%STR&JSSTR\", inactiveBackground:\"%STR&JSSTR\"});\n", - name, tloc, main_bg, inactive_bg); - + char tloc_name[8]; + switch (tloc) + { + case Top: strcpy(tloc_name, "Top"); break; + case Bottom: strcpy(tloc_name, "Bottom"); break; + case Left: strcpy(tloc_name, "Left"); break; + case Right: strcpy(tloc_name, "Right"); break; + case None: strcpy(tloc_name, "None"); break; + } + if (htrAddScriptInit_va(s, + "\ttc_init({" + "layer:wgtrGetNodeRef(ns,'%STR&SYM'), " + "tloc:'%STR', " + "tab_class:'tc%POS_tab', " + "select_x_offset:%INT, " + "select_y_offset:%INT, " + "xtoffset:%INT, " + "ytoffset:%INT, " + "xoffset:%INT, " + "yoffset:%INT, " + "tab_spacing:%INT, " + "tab_w:%INT, " + "tab_h:%INT, " + "do_client_rendering:%BOOL, " + "});\n", + name, tloc_name, + id, + select_x_offset, select_y_offset, + xtoffset, ytoffset, + xoffset, yoffset, + tab_spacing, + (is_auto_tab_w) ? 0 : tab_w, /* 0 tells the front end that it should recalculate tab_w. */ + tab_h, + do_client_rendering + ) != 0) + { + mssError(0, "HTTAB", "Failed to write JS script call."); + goto err; + } + /** Check for tabpages within the tab control, to do the tabs at the top. **/ - tabcnt = wgtrGetMatchingChildList(tree, "widget/tabpage", children, sizeof(children)/sizeof(pWgtrNode)); if (tloc != None) { - for (i=0;i0, tab_width, - is_selected?(z+2):z, - (tloc==Bottom || tloc==Right)?0:border_radius, (tloc==Bottom || tloc==Left)?0:border_radius, (tloc==Top || tloc==Left)?0:border_radius, (tloc==Top || tloc==Right)?0:border_radius, - //(tloc==Top || tloc==Left)?0:border_radius, (tloc==Right)?0:border_radius, border_radius, (tloc==Bottom)?0:border_radius, - border_style, - (tloc!=Bottom)?1:0, (tloc!=Left)?1:0, (tloc!=Top)?1:0, (tloc!=Right)?1:0, - border_color, - sin(shadow_angle*M_PI/180)*shadow_offset, cos(shadow_angle*M_PI/180)*(-shadow_offset), shadow_radius, shadow_color, - (tloc != Right)?"left":"right", - tab_txt, bg - ); - - htrAddBodyItem_va(s, "

%[ %STR&HTE %]%[ %STR&HTE %]

\n", - id, i+1, - tloc == Right, tabname, - is_selected?2:3, - tloc != Right, tabname - ); + case Top: case Bottom: i_offset_x = full_tab_spacing + tab_w; break; + case Right: case Left: i_offset_y = full_tab_spacing + tab_h; break; + case None:; /* Unreachable, but the compiler doesn't believe me. */ + } + + /** Compute clip area, ensuring that it will not overlap the tab control. **/ + /*** I wasn't sure how to remove edges of any clip path, so I just set + *** -1000 and we'll hope that keeps them out of the way. + ***/ + const int clip_top = (tloc == Bottom) ? 0 : -1000; + const int clip_right = (tloc == Left) ? 0 : -1000; + const int clip_bottom = (tloc == Top) ? 0 : -1000; + const int clip_left = (tloc == Right) ? 0 : -1000; + + /** Compute border radius, only rounding corners that don't touch the tab control. **/ + const int border_radius_top_left = (tloc == Bottom || tloc == Right) ? 0 : border_radius; + const int border_radius_top_right = (tloc == Bottom || tloc == Left) ? 0 : border_radius; + const int border_radius_bottom_right = (tloc == Top || tloc == Left) ? 0 : border_radius; + const int border_radius_bottom_left = (tloc == Top || tloc == Right) ? 0 : border_radius; + + /** Compute border width, skipping borders that would overlap the tab control. **/ + const int border_top = (tloc != Bottom) ? 1 : 0; + const int border_right = (tloc != Left) ? 1 : 0; + const int border_bottom = (tloc != Top) ? 1 : 0; + const int border_left = (tloc != Right) ? 1 : 0; + + /** Compute tab flex information. **/ + /*** fl_x/y is enough flex to line up with the left/top of the tab + *** control. However, if the tab box changes size, tabs on the + *** right/bottom need to flex enough to handle that, too. + ***/ + double tab_fl_x = ht_get_fl_x(tree), tab_fl_y = ht_get_fl_y(tree); + if (tloc == Right) tab_fl_x += ht_get_fl_w(tree); + else if (tloc == Bottom) tab_fl_y += ht_get_fl_h(tree); + const int parent_w = ht_get_parent_w(tree); + const int parent_h = ht_get_parent_h(tree); + + /** Inject tab_fl values for client-side rendering. **/ + if (htrAddScriptInit_va(s, + "{ " + "const node = wgtrGetNodeRef(ns, '%STR&SYM'); " + "node.tab_fl_x = %DBL; " + "node.tab_fl_y = %DBL; " + "}\n", + name, tab_fl_x, tab_fl_y + ) != 0) + { + mssError(0, "HTTAB", "Failed to write JS to set tab flex values."); + goto err; + } + + /** Loop over each tab. **/ + for (unsigned int i = 0; i < tab_count; i++) + { + const pWgtrNode tab = children[i]; + + /** Get the tab name, preferring to use the title attribute (if specified). **/ + if (wgtrGetPropertyValue(tab, "title", DATA_T_STRING, POD(&tabname)) != 0) + wgtrGetPropertyValue(tab, "name", DATA_T_STRING, POD(&tabname)); + + /** Write tab CSS styles. **/ + const int is_selected = (i == sel_idx - 1); + const int tab_x = (x + xtoffset) + (i_offset_x * i); + const int tab_y = (y + ytoffset) + (i_offset_y * i); + + /*** Anchor the background tile grid to the widget box edge each + *** axis flexes with, so the pattern stays continuous with the + *** tab pages at any viewport size. Offsets are measured from + *** the padding box to that edge. tc_anchor_bg() in + *** htdrv_tab.js mirrors this math for tabs it repositions. + ***/ + const char* bg_edge_x = (tloc == Right) ? "right" : "left"; + const char* bg_edge_y = (tloc == Bottom) ? "bottom" : "top"; + const int bg_x = (tloc == Right) ? (tab_x + tab_w - border_right) - (x + w) : -(tab_x - x + border_left); + const int bg_y = (tloc == Bottom) ? (tab_y + tab_h - border_bottom) - (y + h) : -(tab_y - y + border_top); + if (htrAddStylesheetItem_va(s, + "\t\t#tc%POStab%POS { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "cursor:pointer; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "%[width:%POSpx; %]" /* Tab width has 0 flexibility. */ + "%[height:%POSpx; %]" /* Tab height has 0 flexibility. */ + "z-index:%POS; " + "clip-path:inset(%INTpx %INTpx %INTpx %INTpx); " + "border-radius:%POSpx %POSpx %POSpx %POSpx; " + "border-width:%POSpx %POSpx %POSpx %POSpx; " + "border-style:%STR&CSSVAL; " + "border-color:%STR&CSSVAL; " + "box-shadow:%DBLpx %DBLpx %POSpx %STR&CSSVAL; " + "text-align:%STR&CSSVAL; " + "color:%STR&CSSVAL; " + "font-weight:bold; /*" + "easter-egg-6:value;*/ " + "background-position:%STR %INTpx %STR %INTpx; " + "}\n", + id, i + 1, + ht_flex(tab_x, parent_w, tab_fl_x), + ht_flex(tab_y, parent_h, tab_fl_y), + (!is_auto_tab_w), tab_w, /* Tab width has 0 flexibility. */ + (tab_h > 0), tab_h, /* Tab height has 0 flexibility. */ + (is_selected) ? (z + 2) : z, + clip_top, clip_right, clip_bottom, clip_left, + border_radius_top_left, border_radius_top_right, border_radius_bottom_right, border_radius_bottom_left, + border_top, border_right, border_bottom, border_left, + border_style, + border_color, + shadow_x, shadow_y, shadow_radius, shadow_color, + (tloc != Right) ? "left" : "right", + text_color, + bg_edge_x, bg_x, bg_edge_y, bg_y + ) != 0) + { + mssError(0, "HTTAB", "Failed to write CSS for tab."); + goto err_tab; + } + + if (htrAddStylesheetItem_va(s, + "\t\t#tc%POStab%POS.tab_selected { " + "transform:translate(%INTpx, %INTpx); " + "background-position:%STR %INTpx %STR %INTpx; " + "}\n", + id, i + 1, + select_x_offset, select_y_offset, + bg_edge_x, bg_x + ((tloc == Right) ? select_x_offset : -select_x_offset), + bg_edge_y, bg_y + ((tloc == Bottom) ? select_y_offset : -select_y_offset) + ) != 0) + { + mssError(0, "HTTAB", "Failed to write CSS for selected tab."); + goto err_tab; + } + + /** Write tab HTML content. **/ + if (htrAddBodyItem_va(s, + "
" + "

" + "%[% %STR&HTE% %]" + "" + "%[% %STR&HTE% %]" + "

" + "
\n", + id, i + 1, id, (is_selected), + (tloc == Right), tabname, + (is_selected) ? 2 : 3, tab_h, + (tloc != Right), tabname + ) != 0) + { + mssError(0, "HTTAB", "Failed to write HTML for tab."); + goto err_tab; + } + + /** Tab written successfully. **/ + continue; + + err_tab: /** Handle errors. **/ + mssError(0, "HTTAB", "Failed to write tab #%d.", i + 1); + goto err; } } - - /** h-2 and w-2 because w3c dom borders add to actual width **/ - htrAddBodyItem_va(s,"
\n", - id, h-border_width*2, w-border_width*2, x+xoffset, y+yoffset, z+1, - border_style, border_color, - (tloc==Top || tloc==Left)?0:border_radius, (tloc==Right)?0:border_radius, border_radius, (tloc==Bottom)?0:border_radius, - sin(shadow_angle*M_PI/180)*shadow_offset, cos(shadow_angle*M_PI/180)*(-shadow_offset), shadow_radius, shadow_color - ); - - /** Check for tabpages within the tab control entity, this time to do the pages themselves **/ - for (i=0;i\n", id) != 0) + { + mssError(0, "HTTAB", "Failed to write HTML for tab control."); + goto err; + } + + /** Check for tab pages within the tab control entity, this time to do the pages themselves. **/ + const char* widget_namespace = wgtrGetNamespace(tree); + for (unsigned int i = 0; i < tab_count; i++) + { + const pWgtrNode tab_page_tree = children[i]; + + /** Handle namespace transition. **/ + if (htrCheckNSTransition(s, tree, tab_page_tree) != 0) { - if(wgtrGetPropertyValue(tabpage_obj,"fieldname",DATA_T_STRING,POD(&field)) == 0) - strtcpy(fieldname,field,sizeof(fieldname)); + mssError(0, "HTTAB", "Failed to transition to namespace while writing tab page."); + goto tab_page_err; } - - /** Add script initialization to add a new tabpage **/ - if (tloc == None) - htrAddScriptInit_va(s," wgtrGetNodeRef('%STR&SYM', '%STR&SYM').addTab(null,wgtrGetContainer(wgtrGetNodeRef(\"%STR&SYM\",\"%STR&SYM\")),wgtrGetNodeRef('%STR&SYM','%STR&SYM'),'%STR&JSSTR','%STR&JSSTR','%STR&JSSTR');\n", - wgtrGetNamespace(tree), name, - wgtrGetNamespace(tabpage_obj), ptr, wgtrGetNamespace(tree), name, ptr,page_type,fieldname); + + /** Check if the tab is selected. **/ + int is_selected = (i == sel_idx - 1); + + /** Get page type. **/ + if (wgtrGetPropertyValue(tab_page_tree, "type", DATA_T_STRING, POD(&ptr)) != 0) + page_type = "static"; /* Default: static page_type. */ + else if (ptr == NULL) + { + mssError(0, "HTTAB", "Failed to get attribute 'type'"); + goto tab_page_err; + } + else if (strcmp(ptr, "dynamic") == 0) page_type = "dynamic"; + else if (strcmp(ptr, "static") == 0) page_type = "static"; else - htrAddScriptInit_va(s," wgtrGetNodeRef('%STR&SYM','%STR&SYM').addTab(htr_subel(wgtrGetParentContainer(wgtrGetNodeRef('%STR&SYM','%STR&SYM')),\"tc%POStab%POS\"),wgtrGetContainer(wgtrGetNodeRef(\"%STR&SYM\",\"%STR&SYM\")),wgtrGetNodeRef('%STR&SYM','%STR&SYM'),'%STR&JSSTR','%STR&JSSTR','%STR&JSSTR');\n", - wgtrGetNamespace(tree), name, - wgtrGetNamespace(tree), name, - id, i+1, wgtrGetNamespace(tabpage_obj), ptr, wgtrGetNamespace(tree), name, ptr,page_type,fieldname); - - /** Add named global for the tabpage **/ - subnptr = nmSysStrdup(ptr); - htrAddWgtrObjLinkage_va(s, tabpage_obj, "tc%POSpane%POS", id, i+1); - htrAddWgtrCtrLinkage_va(s, tabpage_obj, "htr_subel(_parentobj, \"tc%POSpane%POS\")", id, i+1); - - /** Add DIV section for the tabpage. **/ - htrAddBodyItem_va(s,"
\n", - id,i+1,is_selected?"inherit":"hidden",w-2,z+2); - - /** Now look for sub-items within the tabpage. **/ - for (j=0;jChildren));j++) - htrRenderWidget(s, xaGetItem(&(tabpage_obj->Children), j), z+3); - - htrAddBodyItem(s, "
\n"); - - nmSysFree(subnptr); - - /** Add the visible property **/ - htrCheckAddExpression(s, tabpage_obj, ptr, "visible"); - - htrCheckNSTransitionReturn(s, tree, tabpage_obj); + { + mssError(1, "HTTAB", "Unknown value \"%s\" for attribute 'type'.", ptr); + goto tab_page_err; + } + + /** Get feildname. **/ + if (page_type[0] == 'd' + && wgtrGetPropertyValue(tab_page_tree, "fieldname", DATA_T_STRING, POD(&field)) == 0) + strtcpy(fieldname, field, sizeof(fieldname)); + else fieldname[0] = '\0'; + + /** Get name. **/ + if (wgtrGetPropertyValue(tab_page_tree, "name", DATA_T_STRING, POD(&ptr)) != 0) + { + mssError(1, "HTTAB", "Failed to get attribute 'name'"); + goto tab_page_err; + } + + /** Write the addTab() initialization call (in a new scope). **/ + const char* tab_page_namespace = wgtrGetNamespace(tab_page_tree); + if (htrAddScriptInit_va(s, "\t{ " + "const tabctrl = wgtrGetNodeRef('%STR&SYM', '%STR&SYM'); " + "tabctrl" + ".addTab({ " + "%[tab:htr_subel(wgtrGetParentContainer(tabctrl), 'tc%POStab%POS'), %]" + "page:wgtrGetContainer(wgtrGetNodeRef('%STR&SYM', '%STR&SYM')), " + "name:'%STR&JSSTR', type:'%STR&JSSTR', fieldname:'%STR&JSSTR', " + "}); " + "}\n", + widget_namespace, name, + (tloc != None), id, i + 1, + tab_page_namespace, ptr, + ptr, page_type, fieldname + ) != 0) + { + mssError(0, "HTTAB", "Failed to write JS to add tab page."); + goto tab_page_err; + } + + /** Add named global for the tabpage. **/ + if (htrAddWgtrObjLinkage_va(s, tab_page_tree, "tc%POSpane%POS", id, i+1) != 0) + { + mssError(0, "HTTAB", "Failed to add object linkage for tab page."); + goto tab_page_err; + } + if (htrAddWgtrCtrLinkage_va(s, tab_page_tree, "htr_subel(_parentobj, \"tc%POSpane%POS\")", id, i + 1) != 0) + { + mssError(0, "HTTAB", "Failed to add container linkage for tab page."); + goto tab_page_err; + } + + /** Add DIV section to contane the tabpage. **/ + if (htrAddBodyItem_va(s, + "
\n", + id, i + 1, + (is_selected) ? "inherit" : "hidden", + z + 2 + ) != 0) + { + mssError(0, "HTTAB", "Failed to write HTML to add tab page."); + goto tab_page_err; + } + + /** Handle sub-items within the tabpage. **/ + if (htrRenderSubwidgets(s, tab_page_tree, z+3) != 0) + { + mssError(0, "HTTAB", "Failed to render subwidgets of tab page."); + goto tab_page_err; + } + + /** Close the tab page container. */ + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTTAB", "Failed to write closing div tag of tab page."); + goto tab_page_err; + } + + /** Add the visible property. **/ + if (htrCheckAddExpression(s, tab_page_tree, ptr, "visible") < 0) goto tab_page_err; + + /** Handle namespace transition. **/ + if (htrCheckNSTransitionReturn(s, tree, tab_page_tree) != 0) + { + mssError(0, "HTTAB", "Failed to return from namespace."); + goto tab_page_err; + } + + continue; + + /** Error cases. **/ + tab_page_err: + mssError(0, "HTTAB", + "Failed to render tab page \"%s\":\"%s\", #%d.", + tree->Name, tree->Type, i + 1 + ); + goto err; } - - /** Need to do other subwidgets (connectors, etc.) now **/ - htrRenderSubwidgets(s, tree, z+1); - + + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) + { + mssError(0, "HTTAB", "Failed to render subwidgets."); + goto err; + } + /** End the containing layer. **/ - htrAddBodyItem(s, "
\n"); - - return 0; + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTTAB", "Failed to write closing div tag."); + goto err; + } + + return 0; + + err: + mssError(0, "HTTAB", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } int @@ -385,25 +818,20 @@ httabInitialize() { pHtDriver drv; - /** Allocate the driver **/ + /** Tab Control Driver. **/ drv = htrAllocDriver(); - if (!drv) return -1; - - /** Fill in the structure. **/ - strcpy(drv->Name,"DHTML Tab Control Driver"); - strcpy(drv->WidgetName,"tab"); + if (drv == NULL) return -1; + strcpy(drv->Name, "DHTML Tab Control Driver"); + strcpy(drv->WidgetName, "tab"); drv->Render = httabRender; - /*xaAddItem(&(drv->PseudoTypes), "tabpage");*/ - - /** Register. **/ htrRegisterDriver(drv); - htrAddSupport(drv, "dhtml"); + /** Tab Page Driver. **/ drv = htrAllocDriver(); - if (!drv) return -1; - strcpy(drv->Name,"DHTML Tab Page Driver"); - strcpy(drv->WidgetName,"tabpage"); + if (drv == NULL) return -1; + strcpy(drv->Name, "DHTML Tab Page Driver"); + strcpy(drv->WidgetName, "tabpage"); drv->Render = httabRender_page; htrRegisterDriver(drv); htrAddSupport(drv, "dhtml"); diff --git a/centrallix/htmlgen/htdrv_table.c b/centrallix/htmlgen/htdrv_table.c index ff4d2b759..3851fd6f4 100644 --- a/centrallix/htmlgen/htdrv_table.c +++ b/centrallix/htmlgen/htdrv_table.c @@ -1,16 +1,3 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "stparse.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ @@ -35,7 +22,7 @@ /* A copy of the GNU General Public License has been included in this */ /* distribution in the file "COPYING". */ /* */ -/* Module: htdrv_table.c */ +/* Module: htdrv_table.c */ /* Author: Greg Beeley (GRB) */ /* Creation: October 29, 1999 */ /* Description: HTML Widget driver for a data-driven table. Has three */ @@ -58,6 +45,22 @@ /* only. Dynamicrow tables use the most client resources. */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/newmalloc.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "ht_render.h" +#include "wgtr.h" + + +/*** This file uses the optional Comment Anchors VSCode extension, documented + *** with CommentAnchorsExtension.md in centrallix-sysdoc. + ***/ + #define HTTBL_MAX_COLS (64) @@ -99,12 +102,7 @@ typedef struct typedef struct { char name[64]; - char sbuf[160]; - char tbl_bgnd[128]; char hdr_bgnd[128]; - char row_bgnd1[128]; - char row_bgnd2[128]; - char row_bgndhigh[128]; char colsep_bgnd[128]; char textcolor[64]; char textcolorhighlight[64]; @@ -112,34 +110,25 @@ typedef struct char newrow_bgnd[128]; char newrow_textcolor[64]; char osrc[64]; - char row_border[64]; - char row_shadow_color[64]; - int row_shadow; - int row_shadow_radius; - int row_radius; - int x,y,w,h; + httbl_col* col_infs[HTTBL_MAX_COLS]; + int ncols; int id; + int x,y,w,h; int data_mode; /* 0="rows" or 1="properties" */ - int outer_border; - int inner_border; int inner_padding; - httbl_col* col_infs[HTTBL_MAX_COLS]; - int ncols; int windowsize; int min_rowheight; int max_rowheight; int cellhspacing; int cellvspacing; - int followcurrent; int dragcols; int colsep; int colsep_mode; - int gridinemptyrows; + int grid_in_empty_rows; int allow_selection; int show_selection; int initial_selection; int allow_deselection; - int reverse_order; int overlap_scrollbar; /* scrollbar overlaps with table */ int hide_scrollbar; /* don't show scrollbar at all */ int demand_scrollbar; /* only show scrollbar when needed */ @@ -148,150 +137,434 @@ typedef struct int allow_sorting; /* allow header-click column sort */ } httbl_struct; + int httblRenderDynamic(pHtSession s, pWgtrNode tree, int z, httbl_struct* t) { - int colid; - char *ptr; - int i; - pWgtrNode sub_tree; - int subcnt = 0; - char *nptr; - int h; - int first_offset = (t->has_header)?(t->min_rowheight + t->cellvspacing):0; - pWgtrNode children[32]; - int detailcnt; - httbl_col* col; + char* ptr; - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML) + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTTBL","Netscape 4 DOM or W3C DOM support required"); - return -1; + mssError(1, "HTTBL", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** STYLE for the layer **/ - htrAddStylesheetItem_va(s,"\t#tbld%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; } \n",t->id,t->x,t->y,(t->overlap_scrollbar)?(t->w):(t->w-18),z+0); - htrAddStylesheetItem_va(s,"\t#tbld%POSscroll { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:18px; HEIGHT:%POSpx; Z-INDEX:%POS; }\n",t->id,(t->hide_scrollbar || t->demand_scrollbar)?"hidden":"inherit",t->x+t->w-18,t->y+first_offset,t->h-first_offset,z+0); - htrAddStylesheetItem_va(s,"\t#tbld%POSbox { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:18px; WIDTH:16px; HEIGHT:16px; Z-INDEX:%POS; BORDER: solid 1px; BORDER-COLOR: white gray gray white; }\n",t->id,z+1); - - htrAddScriptGlobal(s,"tbld_current","null",0); - htrAddScriptGlobal(s,"tbldb_current","null",0); - htrAddScriptGlobal(s,"tbldx_current","null",0); - htrAddScriptGlobal(s,"tbldb_start","null",0); - htrAddScriptGlobal(s,"tbldbdbl_current","null",0); - - htrAddScriptInclude(s, "/sys/js/htdrv_table.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - - htrAddWgtrObjLinkage_va(s, tree, "tbld%POSpane",t->id); - - htrAddScriptInit_va(s," tbld_init({tablename:'%STR&SYM', table:wgtrGetNodeRef(ns,\"%STR&SYM\"), scroll:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")),\"tbld%POSscroll\"), boxname:\"tbld%POSbox\", name:\"%STR&SYM\", height:%INT, width:%INT, innerpadding:%INT, innerborder:%INT, windowsize:%INT, min_rowheight:%INT, max_rowheight:%INT, cellhspacing:%INT, cellvspacing:%INT, textcolor:\"%STR&JSSTR\", textcolorhighlight:\"%STR&JSSTR\", titlecolor:\"%STR&JSSTR\", rowbgnd1:\"%STR&JSSTR\", rowbgnd2:\"%STR&JSSTR\", rowbgndhigh:\"%STR&JSSTR\", hdrbgnd:\"%STR&JSSTR\", followcurrent:%INT, dragcols:%INT, colsep:%INT, colsep_mode:%INT, colsep_bgnd:\"%STR&JSSTR\", gridinemptyrows:%INT, reverse_order:%INT, allow_selection:%INT, show_selection:%INT, initial_selection:%INT, allow_deselection:%INT, overlap_sb:%INT, hide_sb:%INT, demand_sb:%INT, osrc:%['%STR&SYM'%]%[null%], dm:%INT, hdr:%INT, newrow_bgnd:\"%STR&JSSTR\", newrow_textcolor:\"%STR&JSSTR\", rcsize:%INT, cols:[", - t->name,t->name,t->name,t->id,t->id,t->name,t->h, - (t->overlap_scrollbar)?t->w:t->w-18, - t->inner_padding,t->inner_border,t->windowsize,t->min_rowheight, t->max_rowheight, - t->cellhspacing, t->cellvspacing,t->textcolor, - t->textcolorhighlight, t->titlecolor,t->row_bgnd1,t->row_bgnd2, - t->row_bgndhigh,t->hdr_bgnd,t->followcurrent,t->dragcols, - t->colsep, t->colsep_mode, t->colsep_bgnd,t->gridinemptyrows, t->reverse_order, - t->allow_selection, t->show_selection, t->initial_selection, t->allow_deselection, - t->overlap_scrollbar, t->hide_scrollbar, t->demand_scrollbar, - *(t->osrc) != '\0', t->osrc, *(t->osrc) == '\0', - t->data_mode, t->has_header, - t->newrow_bgnd, t->newrow_textcolor, - t->rowcache_size); + /** Write CSS for the table base element. **/ + const int content_width = (t->overlap_scrollbar) ? (t->w) : (t->w - 18); + if (htrAddStylesheetItem_va(s, + "\t\t#tbld%POSbase { " + "position:absolute; " + "visibility:inherit; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:"ht_flex_format"; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + t->id, + ht_flex_x(t->x, tree), + ht_flex_y(t->y, tree), + ht_flex_w(content_width, tree), + ht_flex_h(t->h, tree), + z + 0 + ) != 0) + { + mssError(0, "HTTBL", "Failed to write base table CSS."); + goto err; + } - for(colid=0;colidncols;colid++) + /** Write CSS for the table scrollbar. **/ + const int row_start_y = (t->has_header) ? (t->min_rowheight + t->cellvspacing) : 0; + if (htrAddStylesheetItem_va(s, + "\t\t#tbld%POSscroll { " + "position:absolute; " + "visibility:%STR; " + "left:"ht_flex_format"; " + "top:"ht_flex_format"; " + "width:18px; " + "height:"ht_flex_format"; " + "z-index:%POS; " + "}\n", + t->id, + (t->hide_scrollbar || t->demand_scrollbar) ? "hidden" : "inherit", /** TODO: Greg - This logic looks fishy. Why does `demand_scrollbar = true` hide the scrollbar?? **/ + ht_flex(t->x + t->w - 18, ht_get_parent_w(tree), ht_get_fl_x(tree) + ht_get_fl_w(tree)), + ht_flex_y(t->y + row_start_y, tree), + ht_flex_h(t->h - row_start_y, tree), + z + 0 + ) != 0) { - col = t->col_infs[colid]; - htrAddScriptInit_va(s,"{name:\"%STR&JSSTR\",ns:\"%STR&JSSTR\",fieldname:\"%STR&JSSTR\",sort_fieldname:\"%STR&JSSTR\",title:\"%STR&JSSTR\",width:%INT,type:\"%STR&JSSTR\",group:%POS,align:\"%STR&JSSTR\",wrap:\"%STR&JSSTR\",caption_fieldname:\"%STR&JSSTR\",caption_textcolor:\"%STR&JSSTR\",image_maxwidth:%POS,image_maxheight:%POS,item_separator:\"%STR&JSSTR\",item_type:\"%STR&JSSTR\",item_source:\"%STR&JSSTR\",item_width:%POS,item_height:%POS,item_spacing:%POS,allow_sorting:%INT},", - col->wname, - col->wnamespace, - col->fieldname, - col->sort_fieldname, - col->title, - col->width, - col->type, - col->group[0]?1:0, - col->align, - col->wrap, - col->caption_fieldname, - col->caption_textcolor, - col->image_maxwidth, - col->image_maxheight, - col->item_separator, - col->item_type, - col->item_source, - col->item_width, - col->item_height, - col->item_spacing, - col->allow_sorting - ); + mssError(0, "HTTBL", "Failed to write table scrollbar CSS."); + goto err; + } + + /** Write CSS for the table scroll thumb. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#tbld%POSthumb { " + "position:absolute; " + "visibility:inherit; " + "left:0px; " + "top:18px; " + "width:16px; " + "height:16px; " + "z-index:%POS; " + "border:solid 1px; " + "border-color:white gray gray white; " + "}\n", + t->id, + z + 1 + ) != 0) + { + mssError(0, "HTTBL", "Failed to write table scroll thumb CSS."); + goto err; + } + + /** Link to the table base object. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "tbld%POSbase", t->id) != 0) goto err; + + /** Add globals for scripts. **/ + if (htrAddScriptGlobal(s, "tbld_current", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "tbldb_current", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "tbldb_start", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "tbldbdbl_current", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "tbldx_current", "null", 0) != 0) goto err; + + /** Include scripts. **/ + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_table.js", 0) != 0) goto err; + + /** Begin writing the js initialization call. **/ + if (htrAddScriptInit_va(s, "\ttbld_init({") != 0) + { + mssError(0, "HTTBL", "Failed to write JS to start table init call."); + goto err; + } + + /** Write identification data. **/ + const int has_osrc = (t->osrc != NULL && t->osrc[0] != '\0'); + if (htrAddScriptInit_va(s, + "name:'%STR&SYM', " + "table:wgtrGetNodeRef(ns, '%STR&SYM'), " + "scroll:htr_subel(" + "wgtrGetParentContainer(wgtrGetNodeRef(ns, '%STR&SYM')), " + "'tbld%POSscroll'" + "), " + "osrc:%['%STR&SYM'%]%[null%], ", + t->name, t->name, t->name, t->id, + (has_osrc), t->osrc, (!has_osrc) + ) != 0) + { + mssError(0, "HTTBL", "Failed to write JS table identification data."); + goto err; + } + + /** Write layout data. **/ + if (htrAddScriptInit_va(s, + "height:%INT, " + "width:%INT, " + "innerpadding:%INT, " + "min_rowheight:%INT, " + "max_rowheight:%INT, " + "cellhspacing:%INT, " + "cellvspacing:%INT, ", + t->h, content_width, + t->inner_padding, + t->min_rowheight, t->max_rowheight, + t->cellhspacing, t->cellvspacing + ) != 0) + { + mssError(0, "HTTBL", "Failed to write JS table layout data."); + goto err; + } + + /** Write selection data. **/ + if (htrAddScriptInit_va(s, + "allow_selection:%INT, " + "show_selection:%INT, " + "initial_selection:%INT, " + "allow_deselection:%INT, ", + t->allow_selection, + t->show_selection, + t->initial_selection, + t->allow_deselection + ) != 0) + { + mssError(0, "HTTBL", "Failed to write JS table selection data."); + goto err; + } + + /** Write scrollbar data. **/ + if (htrAddScriptInit_va(s, + "thumb_name:'tbld%POSthumb', " + "demand_sb:%INT, ", + t->id, + t->demand_scrollbar + ) != 0) + { + mssError(0, "HTTBL", "Failed to write JS table scrollbar data."); + goto err; + } + + /** Write theme data (colors, backgrounds, etc.). **/ + if (htrAddScriptInit_va(s, + "textcolor:'%STR&JSSTR', " + "textcolorhighlight:'%STR&JSSTR', " + "titlecolor:'%STR&JSSTR', " + "colsep_bgnd:'%STR&JSSTR', " + "hdrbgnd:'%STR&JSSTR', " + "newrow_bgnd:'%STR&JSSTR', " + "newrow_textcolor:'%STR&JSSTR', ", + t->textcolor, t->textcolorhighlight, + t->titlecolor, t->colsep_bgnd, t->hdr_bgnd, + t->newrow_bgnd, t->newrow_textcolor + ) != 0) + { + mssError(0, "HTTBL", "Failed to write JS table theme data."); + goto err; + } + + /** Write general row data. **/ + if (htrAddScriptInit_va(s, + "dm:%INT, " + "hdr:%INT, " + "grid_in_empty_rows:%INT, " + "windowsize:%INT, " + "rcsize:%INT, ", + t->data_mode, + t->has_header, + t->grid_in_empty_rows, + t->windowsize, + t->rowcache_size + ) != 0) + { + mssError(0, "HTTBL", "Failed to write JS table general row data."); + goto err; + } + + /** Write general column data. **/ + if (htrAddScriptInit_va(s, + "dragcols:%INT, " + "colsep:%INT, " + "colsep_mode:%INT, ", + t->dragcols, t->colsep, t->colsep_mode + ) != 0) + { + mssError(0, "HTTBL", "Failed to write JS table general column data."); + goto err; + } + + /** Write the cols array with data for each column. **/ + /** ANCHOR[id=table-column] **/ + if (htrAddScriptInit_va(s, "cols:[") != 0) + { + mssError(0, "HTTBL", "Failed to write JS for the start of the table columns list."); + goto err; + } + for (int colid = 0; colid < t->ncols; colid++) + { + httbl_col* col = t->col_infs[colid]; + if (htrAddScriptInit_va(s, + "{ " + "name:'%STR&JSSTR', " + "ns:'%STR&JSSTR', " + "fieldname:'%STR&JSSTR', " + "sort_fieldname:'%STR&JSSTR', " + "title:'%STR&JSSTR', " + "width:%INT, " + "type:'%STR&JSSTR', " + "group:%POS, " + "align:'%STR&JSSTR', " + "wrap:'%STR&JSSTR', " + "caption_fieldname:'%STR&JSSTR', " + "caption_textcolor:'%STR&JSSTR', " + "image_maxwidth:%POS, " + "image_maxheight:%POS, " + "item_separator:'%STR&JSSTR', " + "item_type:'%STR&JSSTR', " + "item_source:'%STR&JSSTR', " + "item_width:%POS, " + "item_height:%POS, " + "item_spacing:%POS, " + "allow_sorting:%INT, " + " }, ", + col->wname, + col->wnamespace, + col->fieldname, + col->sort_fieldname, + col->title, + col->width, + col->type, + col->group[0] ? 1 : 0, + col->align, + col->wrap, + col->caption_fieldname, + col->caption_textcolor, + col->image_maxwidth, + col->image_maxheight, + col->item_separator, + col->item_type, + col->item_source, + col->item_width, + col->item_height, + col->item_spacing, + col->allow_sorting + ) != 0) + { + mssError(0, "HTTBL", + "Failed to write JS for table column #%d/%d, aka. \"%s\".", + colid + 1, t->ncols, col->wname + ); + goto err; + } } - htrAddScriptInit(s,"null]});\n"); - - htrAddBodyItem_va(s,"
\n",t->id); + /** Null terminate the array, and finish writing the init call. **/ + if (htrAddScriptInit(s, "null]});\n") != 0) + { + mssError(0, "HTTBL", "Failed to write JS to finish init call."); + goto err; + } - detailcnt = wgtrGetMatchingChildList(tree, "widget/table-row-detail", children, sizeof(children)/sizeof(pWgtrNode)); - //for (i=0;iChildren));i++) - for (i=0;i\n", t->id) != 0) { - sub_tree = children[i]; - //sub_tree = xaGetItem(&(tree->Children), i); - // - wgtrGetPropertyValue(sub_tree, "outer_type", DATA_T_STRING,POD(&ptr)); - wgtrGetPropertyValue(sub_tree, "name", DATA_T_STRING,POD(&nptr)); + mssError(0, "HTTBL", "Failed to write HTML opening tag for table container."); + goto err; + } - if (strcmp(ptr, "widget/table-row-detail") == 0) + /** Write the table row detail elements. **/ + /** ANCHOR[id=table-row-detail] **/ + int sub_id = 0; + pWgtrNode children[32]; /** Warning: Large local variable in stack. **/ + const int detail_count = wgtrGetMatchingChildList(tree, "widget/table-row-detail", children, sizeof(children)/sizeof(pWgtrNode)); + for (int i = 0; i < detail_count; i++) + { + pWgtrNode row_detail = children[i]; + + /** Only handle table-row-detail widgets. **/ + wgtrGetPropertyValue(row_detail, "outer_type", DATA_T_STRING,POD(&ptr)); + if (strcmp(ptr, "widget/table-row-detail") != 0) continue; + + /** Prepare for the detail widget. **/ + const int detail_id = sub_id++; + if (htrCheckNSTransition(s, tree, row_detail) != 0) goto err_detail; + + /** Write CSS. **/ + int h; + if (wgtrGetPropertyValue(row_detail, "height", DATA_T_INTEGER, POD(&h)) != 0) h = t->min_rowheight; + if (htrAddStylesheetItem_va(s, + "\t\t#tbld%POSsub%POS { " + "position:absolute; " + "visibility:hidden; " + "left:0px; " + "top:0px; " + "width:100%%; " + "height:%POSpx; " + "z-index:%POS; " + "}\n", + t->id, detail_id, + h, + z + 1 + ) != 0) + { + mssError(0, "HTTBL", "Failed to write CSS styleseet item for table row detail."); + goto err_detail; + } + + /** Write HTML for detail content (aka. sub-widgets). **/ + if (htrAddBodyItem_va(s, "
\n", t->id, detail_id) != 0) + { + mssError(0, "HTTBL", "Failed to write HTML opening tag for table row detail."); + goto err_detail; + } + if (htrRenderSubwidgets(s, row_detail, z + 2) != 0) + { + mssError(0, "HTTBL", "Failed to write widgets in table row detail."); + goto err_detail; + } + if (htrAddBodyItem(s, "
\n") != 0) { - htrCheckNSTransition(s, tree, sub_tree); - - if (wgtrGetPropertyValue(sub_tree,"height",DATA_T_INTEGER,POD(&h)) != 0) h = t->min_rowheight; - htrAddStylesheetItem_va(s,"\t#tbld%POSsub%POS { POSITION:absolute; VISIBILITY:hidden; LEFT:0px; TOP:0px; WIDTH:%POSpx; HEIGHT:%POSpx; Z-INDEX:%POS; } \n", - t->id, ++subcnt, t->w-(t->demand_scrollbar?0:18), h, z+1); - htrAddBodyItem_va(s,"
\n", t->id, subcnt); - htrRenderSubwidgets(s, sub_tree, z+2); - htrAddBodyItem(s,"
\n"); - htrAddWgtrObjLinkage_va(s, sub_tree, "tbld%POSsub%POS", t->id, subcnt); - htrCheckAddExpression(s, sub_tree, nptr, "display_for"); - - htrCheckNSTransitionReturn(s, tree, sub_tree); + mssError(0, "HTTBL", "Failed to write HTML closing tag for table row detail."); + goto err_detail; } - //else if (strcmp(ptr,"widget/table-column") != 0) //got columns earlier - //{ - //htrRenderWidget(s, sub_tree, z+3); - //} + + /** Link detail widget to container DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, row_detail, "tbld%POSsub%POS", t->id, detail_id)) goto err_detail; + + /** Add 'display_for' data. **/ + if (wgtrGetPropertyValue(row_detail, "name", DATA_T_STRING, POD(&ptr)) != 0) + { + mssError(1, "HTTBL", "Failed to get name of table row detail widget."); + goto err_detail; + } + if (htrCheckAddExpression(s, row_detail, ptr, "display_for") < 0) goto err; + + /** Done. **/ + if (htrCheckNSTransitionReturn(s, tree, row_detail) != 0) goto err; + + /** Success. **/ + continue; + + err_detail: + mssError(0, "HTTBL", + "Failed to render \"%s\":\"%s\" (#%d/%d).", + row_detail->Name, row_detail->Type, i + 1, detail_count + ); + goto err; } - htrRenderSubwidgets(s, tree, z+2); - - htrAddBodyItem(s,"
\n"); - - /** HTML body
element for the scrollbar layer. **/ - htrAddBodyItem_va(s,"
\n",t->id); - htrAddBodyItem(s,"\n"); - htrAddBodyItem(s,"\n"); - htrAddBodyItem_va(s,"\n", t->id, t->h-2*18-first_offset); - htrAddBodyItem(s,"\n"); - htrAddBodyItem(s,"
\n"); - /*htrAddBodyItem_va(s,"
\n",t->id);*/ - htrAddBodyItem_va(s,"
\n",t->id); - htrAddBodyItem(s,"
\n"); - - htrAddEventHandlerFunction(s,"document","MOUSEOVER","tbld","tbld_mouseover"); - htrAddEventHandlerFunction(s,"document","MOUSEOUT","tbld","tbld_mouseout"); - htrAddEventHandlerFunction(s,"document","MOUSEDOWN","tbld","tbld_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE","tbld","tbld_mousemove"); - htrAddEventHandlerFunction(s, "document","MOUSEUP","tbld","tbld_mouseup"); - htrAddEventHandlerFunction(s, "document","WHEEL","tbld","tbld_wheel"); - htrAddEventHandlerFunction(s, "document","KEYDOWN","tbld","tbld_keydown"); - htrAddEventHandlerFunction(s, "document","TOUCHSTART","tbld","tbld_touchstart"); - htrAddEventHandlerFunction(s, "document","TOUCHEND","tbld","tbld_touchend"); - htrAddEventHandlerFunction(s, "document","TOUCHMOVE","tbld","tbld_touchmove"); - htrAddEventHandlerFunction(s, "document","TOUCHCANCEL","tbld","tbld_touchcancel"); - if (s->Capabilities.Dom1HTML) - htrAddEventHandlerFunction(s, "document", "CONTEXTMENU", "tbld", "tbld_contextmenu"); + + /** Render other children. **/ + if (htrRenderSubwidgets(s, tree, z + 2) != 0) goto err; - return 0; + /** Close the base container. **/ + if (htrAddBodyItem(s, "
\n") != 0) + { + mssError(0, "HTTBL", "Failed to write HTML closing tag for table container."); + goto err; + } + + /** Write HTML for the scrollbar. **/ + if (htrAddBodyItem_va(s, + "
\n" + "\n" + "\n" + "\n" + "\n" + "
\n" + "
\n" + "
\n", + t->id, + t->id, + t->id + ) != 0) + { + mssError(0, "HTTBL", "Failed to write HTML for table scrollbar."); + goto err; + } + + /** Register event handlers. **/ + if (htrAddEventHandlerFunction(s, "document", "CONTEXTMENU", "tbld", "tbld_contextmenu") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "KEYDOWN", "tbld", "tbld_keydown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "tbld", "tbld_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "tbld", "tbld_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "tbld", "tbld_mouseout") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "tbld", "tbld_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "tbld", "tbld_mouseup") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "TOUCHCANCEL", "tbld", "tbld_touchcancel") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "TOUCHEND", "tbld", "tbld_touchend") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "TOUCHMOVE", "tbld", "tbld_touchmove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "TOUCHSTART", "tbld", "tbld_touchstart") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "WHEEL", "tbld", "tbld_wheel") != 0) goto err; + + /** Success. **/ + return 0; + + err: + mssError(0, "HTTBL", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, t->id + ); + return -1; } @@ -303,14 +576,18 @@ httblRender(pHtSession s, pWgtrNode tree, int z) { pWgtrNode sub_tree; char* ptr; - char* nptr; int n, i; httbl_struct* t; int rval; pWgtrNode children[HTTBL_MAX_COLS]; httbl_col* col; - /** Don't try to render table-column, etc. We do that elsewhere **/ + /*** Only render "widget/table". Other widgets (e.g. table-column or + *** table-row-detail) are rendered elsewhere (see links below). + *** + *** LINK #table-column + *** LINK #table-row-detail + ***/ wgtrGetPropertyValue(tree,"outer_type",DATA_T_STRING,POD(&ptr)); if (strcmp(ptr, "widget/table") != 0) return 0; @@ -319,13 +596,10 @@ httblRender(pHtSession s, pWgtrNode tree, int z) if (!t) return -1; memset(t, 0, sizeof(httbl_struct)); - t->x=-1; - t->y=-1; - - /** Get an id for thit. **/ + /** Get an id. **/ t->id = (HTTBL.idcnt++); - /** Backwards compat for the time being **/ + /** Backwards compat for deprecated property names. **/ wgtrRenameProperty(tree, "row_bgcolor1", "row1_bgcolor"); wgtrRenameProperty(tree, "row_background1", "row1_background"); wgtrRenameProperty(tree, "row_bgcolor2", "row2_bgcolor"); @@ -333,141 +607,129 @@ httblRender(pHtSession s, pWgtrNode tree, int z) wgtrRenameProperty(tree, "row_bgcolorhighlight", "rowhighlight_bgcolor"); wgtrRenameProperty(tree, "row_backgroundhighlight", "rowhighlight_background"); - /** Get x,y,w,h of this object **/ - if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&(t->x))) != 0) t->x = -1; - if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&(t->y))) != 0) t->y = -1; - if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&(t->w))) != 0) t->w = -1; - if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&(t->h))) != 0) + /** Get name. **/ + if (wgtrGetPropertyValue(tree, "name", DATA_T_STRING, POD(&ptr)) != 0) { - mssError(1,"HTTBL","'height' property is required"); + nmFree(t, sizeof(httbl_struct)); return -1; } - if (wgtrGetPropertyValue(tree,"windowsize",DATA_T_INTEGER,POD(&(t->windowsize))) != 0) t->windowsize = -1; - if (wgtrGetPropertyValue(tree,"rowheight",DATA_T_INTEGER,POD(&n)) == 0) + strtcpy(t->name, ptr, sizeof(t->name)); + + /** Get object source path. **/ + if (wgtrGetPropertyValue(tree, "objectsource", DATA_T_STRING, POD(&ptr)) != 0) + strcpy(t->osrc, ""); + else + strtcpy(t->osrc, ptr, sizeof(t->osrc)); + + /** Get the location, size, and layout data. **/ + if (wgtrGetPropertyValue(tree, "x", DATA_T_INTEGER, POD(&(t->x))) != 0) t->x = -1; + if (wgtrGetPropertyValue(tree, "y", DATA_T_INTEGER, POD(&(t->y))) != 0) t->y = -1; + if (wgtrGetPropertyValue(tree, "width", DATA_T_INTEGER, POD(&(t->w))) != 0) t->w = -1; + if (wgtrGetPropertyValue(tree, "height", DATA_T_INTEGER, POD(&(t->h))) != 0) { - t->min_rowheight = t->max_rowheight = n; + mssError(1,"HTTBL","'height' property is required"); + return -1; } - else + if (wgtrGetPropertyValue(tree, "rowheight", DATA_T_INTEGER, POD(&n)) != 0) { t->min_rowheight = s->ClientInfo->ParagraphHeight + 2; t->max_rowheight = -1; } - wgtrGetPropertyValue(tree,"min_rowheight",DATA_T_INTEGER,POD(&(t->min_rowheight))); - wgtrGetPropertyValue(tree,"max_rowheight",DATA_T_INTEGER,POD(&(t->max_rowheight))); - if (wgtrGetPropertyValue(tree,"cellhspacing",DATA_T_INTEGER,POD(&(t->cellhspacing))) != 0) t->cellhspacing = 1; - if (wgtrGetPropertyValue(tree,"cellvspacing",DATA_T_INTEGER,POD(&(t->cellvspacing))) != 0) t->cellvspacing = 1; - - if (wgtrGetPropertyValue(tree,"colsep",DATA_T_INTEGER,POD(&(t->colsep))) != 0) t->colsep = 1; - if (wgtrGetPropertyValue(tree,"colsep_mode",DATA_T_STRING,POD(&ptr)) == 0) + else { - if (!strcasecmp(ptr, "full")) - t->colsep_mode = 0; - else if (!strcasecmp(ptr, "header")) - t->colsep_mode = 1; + t->min_rowheight = t->max_rowheight = n; } - - if (wgtrGetPropertyValue(tree,"rowcache_size",DATA_T_INTEGER,POD(&(t->rowcache_size))) != 0) t->rowcache_size = 0; - - t->dragcols = htrGetBoolean(tree, "dragcols", 1); - t->gridinemptyrows = htrGetBoolean(tree, "gridinemptyrows", 1); + if (wgtrGetPropertyValue(tree, "inner_padding", DATA_T_INTEGER, POD(&(t->inner_padding))) != 0) t->inner_padding = 0; + if (wgtrGetPropertyValue(tree, "min_rowheight", DATA_T_INTEGER, POD(&(t->min_rowheight))) != 0); /* Keep value from above. */ + if (wgtrGetPropertyValue(tree, "max_rowheight", DATA_T_INTEGER, POD(&(t->max_rowheight))) != 0); /* Keep value from above. */ + if (wgtrGetPropertyValue(tree, "cellhspacing", DATA_T_INTEGER, POD(&(t->cellhspacing))) != 0) t->cellhspacing = 1; + if (wgtrGetPropertyValue(tree, "cellvspacing", DATA_T_INTEGER, POD(&(t->cellvspacing))) != 0) t->cellvspacing = 1; + + /** Get selection data. **/ t->allow_selection = htrGetBoolean(tree, "allow_selection", 1); t->show_selection = htrGetBoolean(tree, "show_selection", 1); - if (wgtrGetPropertyType(tree, "initial_selection") == DATA_T_STRING && wgtrGetPropertyValue(tree,"initial_selection",DATA_T_STRING,POD(&ptr)) == 0 && !strcasecmp(ptr,"noexpand")) + if (wgtrGetPropertyType(tree, "initial_selection") == DATA_T_STRING + && wgtrGetPropertyValue(tree, "initial_selection", DATA_T_STRING, POD(&ptr)) == 0 + && strcasecmp(ptr, "noexpand") == 0) t->initial_selection = 2; else t->initial_selection = htrGetBoolean(tree, "initial_selection", 1); - t->allow_deselection = htrGetBoolean(tree, "allow_deselection", t->initial_selection?0:1); - t->reverse_order = htrGetBoolean(tree, "reverse_order", 0); + t->allow_deselection = htrGetBoolean(tree, "allow_deselection", (t->initial_selection) ? 0 : 1); + /** Get scrollbar data. **/ t->overlap_scrollbar = htrGetBoolean(tree, "overlap_scrollbar", 0); t->hide_scrollbar = htrGetBoolean(tree, "hide_scrollbar", 0); t->demand_scrollbar = htrGetBoolean(tree, "demand_scrollbar", 0); - t->has_header = htrGetBoolean(tree, "titlebar", 1); t->allow_sorting = htrGetBoolean(tree, "allow_sorting", 1); - - /** Which data mode to use? **/ - if (wgtrGetPropertyValue(tree,"data_mode", DATA_T_STRING, POD(&ptr)) == 0) + + /** Get theme data (colors, backgrounds, etc.). **/ + if (wgtrGetPropertyValue(tree, "textcolor", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(t->textcolor, ptr, sizeof(t->textcolor)); + if (wgtrGetPropertyValue(tree, "textcolorhighlight", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(t->textcolorhighlight, ptr, sizeof(t->textcolorhighlight)); + if (wgtrGetPropertyValue(tree, "titlecolor", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(t->titlecolor, ptr, sizeof(t->titlecolor)); + if (!*t->titlecolor) strcpy(t->titlecolor, t->textcolor); + htrGetBackground(tree, "colsep", !s->Capabilities.Dom0NS, t->colsep_bgnd, sizeof(t->colsep_bgnd)); + htrGetBackground(tree, "hdr", !s->Capabilities.Dom0NS, t->hdr_bgnd, sizeof(t->hdr_bgnd)); + htrGetBackground(tree, "newrow", !s->Capabilities.Dom0NS, t->newrow_bgnd, sizeof(t->newrow_bgnd)); + if (wgtrGetPropertyValue(tree, "textcolornew", DATA_T_STRING, POD(&ptr)) == 0) + strtcpy(t->newrow_textcolor, ptr, sizeof(t->newrow_textcolor)); + + /** Get general row data. **/ + if (wgtrGetPropertyValue(tree, "data_mode", DATA_T_STRING, POD(&ptr)) != 0) + t->data_mode = 0; + else { - if (!strcmp(ptr, "rows")) - t->data_mode = 0; - else if (!strcmp(ptr, "properties")) - t->data_mode = 1; + if (strcasecmp(ptr, "rows") == 0) t->data_mode = 0; + else if (strcasecmp(ptr, "properties") == 0) t->data_mode = 1; + else + { + mssError(1, "HTTBL", "Invalid value for attribute 'data_mode': %s", ptr); + return -1; + } } + t->has_header = htrGetBoolean(tree, "titlebar", 1); /* Whether to render a header row. */ + t->grid_in_empty_rows = htrGetBoolean(tree, "gridinemptyrows", 1); /* Whether to show the grid in empty rows. */ + if (wgtrGetPropertyValue(tree, "windowsize", DATA_T_INTEGER, POD(&(t->windowsize))) != 0) t->windowsize = -1; + if (wgtrGetPropertyValue(tree, "rowcache_size", DATA_T_INTEGER, POD(&(t->rowcache_size))) != 0) t->rowcache_size = 0; - /** Should we follow the current record around? **/ - t->followcurrent = htrGetBoolean(tree, "followcurrent", 1); - - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) + /** Get general column data. **/ + t->dragcols = htrGetBoolean(tree, "dragcols", 1); + if (wgtrGetPropertyValue(tree, "colsep", DATA_T_INTEGER, POD(&(t->colsep))) != 0) t->colsep = 1; + if (wgtrGetPropertyValue(tree, "colsep_mode", DATA_T_STRING, POD(&ptr)) != 0) + t->colsep_mode = 0; + else { - nmFree(t, sizeof(httbl_struct)); - return -1; + if (strcasecmp(ptr, "full") == 0) t->colsep_mode = 0; + else if (strcasecmp(ptr, "header") == 0) t->colsep_mode = 1; + else + { + mssError(1, "HTTBL", "Invalid value for attribute 'colsep_mode': %s", ptr); + return -1; + } } - strtcpy(t->name,ptr,sizeof(t->name)); - if (wgtrGetPropertyValue(tree,"objectsource",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(t->osrc,ptr,sizeof(t->osrc)); - else - strcpy(t->osrc,""); - - /** Get background color/image for table header **/ - htrGetBackground(tree, NULL, !s->Capabilities.Dom0NS, t->tbl_bgnd, sizeof(t->tbl_bgnd)); - - /** Get background color/image for header row **/ - htrGetBackground(tree, "hdr", !s->Capabilities.Dom0NS, t->hdr_bgnd, sizeof(t->hdr_bgnd)); - - /** Get background color/image for rows **/ - htrGetBackground(tree, "row1", !s->Capabilities.Dom0NS, t->row_bgnd1, sizeof(t->row_bgnd1)); - htrGetBackground(tree, "row2", !s->Capabilities.Dom0NS, t->row_bgnd2, sizeof(t->row_bgnd2)); - htrGetBackground(tree, "rowhighlight", !s->Capabilities.Dom0NS, t->row_bgndhigh, sizeof(t->row_bgndhigh)); - htrGetBackground(tree, "colsep", !s->Capabilities.Dom0NS, t->colsep_bgnd, sizeof(t->colsep_bgnd)); - htrGetBackground(tree, "newrow", !s->Capabilities.Dom0NS, t->newrow_bgnd, sizeof(t->newrow_bgnd)); - - /** Get borders and padding information **/ - wgtrGetPropertyValue(tree,"outer_border",DATA_T_INTEGER,POD(&(t->outer_border))); - wgtrGetPropertyValue(tree,"inner_border",DATA_T_INTEGER,POD(&(t->inner_border))); - wgtrGetPropertyValue(tree,"inner_padding",DATA_T_INTEGER,POD(&(t->inner_padding))); - - /** Row decorations **/ - wgtrGetPropertyValue(tree, "row_border_color", DATA_T_STRING, POD(&t->row_border)); - wgtrGetPropertyValue(tree, "row_shadow_color", DATA_T_STRING, POD(&t->row_shadow_color)); - wgtrGetPropertyValue(tree, "row_shadow_offset", DATA_T_INTEGER, POD(&t->row_shadow)); - wgtrGetPropertyValue(tree, "row_shadow_radius", DATA_T_INTEGER, POD(&t->row_shadow_radius)); - wgtrGetPropertyValue(tree, "row_border_radius", DATA_T_INTEGER, POD(&t->row_radius)); - - /** Text color information **/ - if (wgtrGetPropertyValue(tree,"textcolor",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(t->textcolor,ptr,sizeof(t->textcolor)); - - /** Text color information **/ - if (wgtrGetPropertyValue(tree,"textcolorhighlight",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(t->textcolorhighlight,ptr,sizeof(t->textcolorhighlight)); - - /** Text color information for "new row" in process of being created **/ - if (wgtrGetPropertyValue(tree,"textcolornew",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(t->newrow_textcolor,ptr,sizeof(t->newrow_textcolor)); - - /** Title text color information **/ - if (wgtrGetPropertyValue(tree,"titlecolor",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(t->titlecolor,ptr,sizeof(t->titlecolor)); - if (!*t->titlecolor) strcpy(t->titlecolor,t->textcolor); - - /** Get column data **/ + /** Get specific column data for each column. **/ t->ncols = wgtrGetMatchingChildList(tree, "widget/table-column", children, sizeof(children)/sizeof(pWgtrNode)); for (i=0;incols;i++) { sub_tree = children[i]; + + /** Only read table-column widgets. **/ wgtrGetPropertyValue(sub_tree, "outer_type", DATA_T_STRING,POD(&ptr)); - wgtrGetPropertyValue(sub_tree, "name", DATA_T_STRING,POD(&nptr)); - if (!strcmp(ptr,"widget/table-column") != 0) + if (strcmp(ptr, "widget/table-column") == 0) { + /** Allocate space to store the column data. **/ col = (httbl_col*)nmMalloc(sizeof(httbl_col)); memset(col, 0, sizeof(*col)); + + /** Basic column info. **/ t->col_infs[i] = col; strtcpy(col->wname, wgtrGetName(sub_tree), sizeof(col->wname)); strtcpy(col->wnamespace, wgtrGetNamespace(sub_tree), sizeof(col->wnamespace)); - /** no layer associated with this guy **/ + /** The object system doesn't need to render this widget (we will do that). **/ sub_tree->RenderFlags |= HT_WGTF_NOOBJECT; /** Get column properties **/ @@ -486,8 +748,9 @@ httblRender(pHtSession s, pWgtrNode tree, int z) strtcpy(col->title, ptr, sizeof(col->title)); else strtcpy(col->title, col->fieldname, sizeof(col->title)); - htrCheckAddExpression(s, sub_tree, nptr, "title"); - htrCheckAddExpression(s, sub_tree, nptr, "visible"); + wgtrGetPropertyValue(sub_tree, "name", DATA_T_STRING, POD(&ptr)); + htrCheckAddExpression(s, sub_tree, ptr, "title"); + htrCheckAddExpression(s, sub_tree, ptr, "visible"); if (wgtrGetPropertyValue(sub_tree, "align", DATA_T_STRING,POD(&ptr)) == 0) strtcpy(col->align, ptr, sizeof(col->align)); else @@ -533,7 +796,10 @@ httblRender(pHtSession s, pWgtrNode tree, int z) } } + /** Render the table. **/ rval = httblRenderDynamic(s, tree, z, t); + + /** Clean up. **/ for(i=0;incols;i++) nmFree(t->col_infs[i], sizeof(httbl_col)); nmFree(t, sizeof(httbl_struct)); @@ -549,25 +815,22 @@ httblInitialize() { pHtDriver drv; - /** Allocate the driver **/ + /** Allocate the driver struct. **/ drv = htrAllocDriver(); if (!drv) return -1; - /** Fill in the structure. **/ + /** Initialize driver values. **/ strcpy(drv->Name,"DHTML DataTable Driver"); strcpy(drv->WidgetName,"table"); drv->Render = httblRender; xaAddItem(&(drv->PseudoTypes), "table-column"); xaAddItem(&(drv->PseudoTypes), "table-row-detail"); - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"DblClick"); - - /** Register. **/ + /** Register the driver, with dhtml support. **/ htrRegisterDriver(drv); - htrAddSupport(drv, "dhtml"); + /** Initialize the ID counter. **/ HTTBL.idcnt = 0; return 0; diff --git a/centrallix/htmlgen/htdrv_terminal.c b/centrallix/htmlgen/htdrv_terminal.c index a4a8ec44a..e6825ce90 100644 --- a/centrallix/htmlgen/htdrv_terminal.c +++ b/centrallix/htmlgen/htdrv_terminal.c @@ -1,21 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "cxlib/qprintf.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -42,6 +29,16 @@ /* */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/qprintf.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xstring.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -58,27 +55,24 @@ httermRender(pHtSession s, pWgtrNode tree, int z) { char* ptr; char name[64]; - int id; - pWgtrNode sub_tree; #define MAX_COLORS 8 #define MAX_COLOR_LEN 32 char colors[MAX_COLORS][MAX_COLOR_LEN]={"black","red","green","yellow","blue","purple","aqua","white"}; - int i; XString source; int rows, cols, fontsize, x, y; - /** our first Mozilla-only widget :) **/ - if(!s->Capabilities.Dom1HTML) + /** Get an id for this. **/ + const unsigned id = (HTTERM.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTTERM","W3C DOM Level 1 support required"); - return -1; + mssError(1, "HTTERM", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTTERM.idcnt++); - /** read the color specs, leaving the defaults if they don't exist **/ - for(i=0;i element to use as the base **/ - htrAddBodyItem_va(s,"
\n",id); - htrAddBodyItem_va(s," \n",id); - htrAddBodyItem_va(s," \n",id); - - /** write the stylesheet header element **/ - htrAddStylesheetItem_va(s," #term%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INT; TOP:%INT; WIDTH:%POS; HEIGHT:%POS; Z-INDEX:%POS; }\n",id,x,y,cols*fontsize,rows*fontsize,z); - htrAddStylesheetItem_va(s," #term%POSreader { POSITION:absolute; VISIBILITY:hidden; LEFT:0; TOP:0; WIDTH:1; HEIGHT:1; Z-INDEX:-20; }\n",id); - htrAddStylesheetItem_va(s," #term%POSwriter { POSITION:absolute; VISIBILITY:hidden; LEFT:0; TOP:0; WIDTH:1; HEIGHT:1; Z-INDEX:-20; }\n",id); - htrAddStylesheetItem_va(s," .fixed%POS {font-family: fixed; }\n",id); + if (htrAddBodyItem_va(s, + "
" + "" + "\n", + id, id, id + ) != 0) + { + mssError(0, "HTTERM", "Failed to write HTML base, reader, and writer tags."); + goto err; + } - /** init line **/ - htrAddScriptInit_va(s," terminal_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), rdr:\"term%POSreader\", wtr:\"term%POSwriter\", fxd:\"fixed%POS\", source:'%STR&JSSTR', rows:%INT, cols:%INT, colors:new Array(", - name,id,id,id,source.String,rows,cols); - for(i=0;iChildren));i++) + /** Write script initialization. **/ + if (htrAddScriptInit_va(s, + "\tterminal_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "rdr:'term%POSreader', " + "wtr:'term%POSwriter', " + "fxd:'fixed%POS', " + "source:'%STR&JSSTR', " + "rows:%INT, " + "cols:%INT, " + "colors:new Array(", + name, id, id, id, + source.String, rows, cols + ) != 0) { - sub_tree = xaGetItem(&(tree->Children), i); - htrRenderWidget(s, sub_tree, z+1); + mssError(0, "HTTERM", "Failed to write JS init call start."); + goto err; } - - return 0; + for (unsigned int i = 0u; i < MAX_COLORS; i++) + { + if (htrAddScriptInit_va(s, "'%STR&JSSTR',", colors[i]) != 0) + { + mssError(0, "HTTERM", "Failed to write terminal color #%d/%d: \"%s\"", i + 1, MAX_COLORS, colors[i]); + goto err; + } + } + if (htrAddScriptInit(s, ")});\n") != 0) + { + mssError(0, "HTTERM", "Failed to write JS init call end."); + goto err; + } + + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto err; + + /** Success. **/ + return 0; + + err: + mssError(0, "HTTERM", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } @@ -187,15 +252,6 @@ httermInitialize() strcpy(drv->WidgetName,"terminal"); drv->Render = httermRender; - - /** Add our actions **/ - htrAddAction(drv,"Disconnect"); - htrAddAction(drv,"Connect"); - - /** Add our Events **/ - htrAddEvent(drv,"ConnectionOpen"); - htrAddEvent(drv,"ConnectionClose"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_textarea.c b/centrallix/htmlgen/htdrv_textarea.c index 34b975e4d..81f982b97 100644 --- a/centrallix/htmlgen/htdrv_textarea.c +++ b/centrallix/htmlgen/htdrv_textarea.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,15 @@ /* Description: HTML Widget driver for a multi-line textarea. */ /************************************************************************/ +#include +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" /** globals **/ @@ -60,43 +57,42 @@ httxRender(pHtSession s, pWgtrNode tree, int z) char elementid[16]; //char main_bg[128]; int x=-1,y=-1,w,h; - int id, i; int is_readonly = 0; int is_raised = 0; int mode = 0; /* 0=text, 1=html, 2=wiki */ - int maxchars; + int max_chars; char fieldname[HT_FIELDNAME_SIZE]; char form[64]; - int box_offset; - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom0IE && !s->Capabilities.Dom2Events) + /** Get an id for this. **/ + const int id = (HTTX.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTTX","Netscape, IE, or Dom2Events support required"); - return -1; + mssError(1, "HTTX", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTTX.idcnt++); - /** Get x,y,w,h of this object **/ if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { mssError(1,"HTTX","Textarea widget must have a 'width' property"); - return -1; + goto err; } if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) { mssError(1,"HTTX","Textarea widget must have a 'height' property"); - return -1; + goto err; } - /** Maximum characters to accept from the user **/ - if (wgtrGetPropertyValue(tree,"maxchars",DATA_T_INTEGER,POD(&maxchars)) != 0) maxchars=255; + /** Maximum characters to accept from the user. Negative means no limit. **/ + if (wgtrGetPropertyValue(tree,"max_chars",DATA_T_INTEGER,POD(&max_chars)) != 0) max_chars = -1; /** Readonly flag **/ - if (wgtrGetPropertyValue(tree,"readonly",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"yes")) is_readonly = 1; + is_readonly = htrGetBoolean(tree, "readonly", 0); /** Allow HTML? **/ if (wgtrGetPropertyValue(tree,"mode",DATA_T_STRING,POD(&ptr)) == 0) @@ -107,7 +103,7 @@ httxRender(pHtSession s, pWgtrNode tree, int z) else { mssError(1,"HTTX","Textarea widget 'mode' property must be either 'text','html', or 'wiki'"); - return -1; + goto err; } } @@ -115,7 +111,7 @@ httxRender(pHtSession s, pWgtrNode tree, int z) //htrGetBackground(tree, NULL, 1, main_bg, sizeof(main_bg)); /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto err; strtcpy(name,ptr,sizeof(name)); /** Style of Textarea - raised/lowered **/ @@ -132,61 +128,99 @@ httxRender(pHtSession s, pWgtrNode tree, int z) else fieldname[0]='\0'; - if (s->Capabilities.CSSBox) - box_offset = 1; - else - box_offset = 0; - /** Write Style header items. **/ snprintf(elementid, sizeof(elementid), "#tx%dbase", id); - htrFormatElement(s, tree, elementid, 0, - x, y, w-2*box_offset, h-2*box_offset, z, "", - (char*[]){"border_color","#e0e0e0", "border_style",(is_raised?"outset":"inset"), NULL}, - "overflow:hidden; position:absolute;"); - //htrAddStylesheetItem_va(s,"\t#tx%POSbase { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%INT; overflow:hidden; }\n",id,x,y,w-2*box_offset,z); - - /** DOM Linkage **/ - htrAddWgtrObjLinkage_va(s, tree, "tx%POSbase",id); - - /** Global for ibeam cursor layer **/ - htrAddScriptGlobal(s, "text_metric", "null", 0); - htrAddScriptGlobal(s, "tx_current", "null", 0); - htrAddScriptGlobal(s, "tx_cur_mainlayer", "null", 0); - - htrAddScriptInclude(s, "/sys/js/htdrv_textarea.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_cursor.js", 0); - - htrAddEventHandlerFunction(s, "document","MOUSEUP", "tx", "tx_mouseup"); - htrAddEventHandlerFunction(s, "document","MOUSEDOWN", "tx","tx_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEOVER", "tx", "tx_mouseover"); - htrAddEventHandlerFunction(s, "document","MOUSEMOVE", "tx", "tx_mousemove"); - htrAddEventHandlerFunction(s, "document","PASTE", "tx", "tx_paste"); + if (htrFormatElement(s, tree, elementid, 0, + x, y, w, h, z, "", + (char*[]){"border_color","#e0e0e0", "border_style", (is_raised ? "outset" : "inset"), NULL}, + "position:absolute; " + "overflow:hidden; " + ) != 0) + { + mssError(0, "HTTX", "Failed to write styles."); + goto err; + } + + /** Link DOM node to widget data. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "tx%POSbase", id) != 0) goto err; + + /** Write JS globals. **/ + if (htrAddScriptGlobal(s, "text_metric", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "tx_current", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "tx_cur_mainlayer", "null", 0) != 0) goto err; + + /** Write JS script includes. **/ + if (htrAddScriptInclude(s, "/sys/js/ht_utils_cursor.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_textarea.js", 0) != 0) goto err; + + /** Register JS event handlers. **/ + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "tx", "tx_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "tx", "tx_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "tx", "tx_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "tx", "tx_mouseup") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "PASTE", "tx", "tx_paste") != 0) goto err; /** Script initialization call. **/ - htrAddScriptInit_va(s, " tx_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), fieldname:\"%STR&JSSTR\", form:\"%STR&JSSTR\", isReadonly:%INT, mode:%INT});\n", - name, fieldname, form, is_readonly, mode); + if (htrAddScriptInit_va(s, + "\ttx_init({ " + "layer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "fieldname:'%STR&JSSTR', " + "form:'%STR&JSSTR', " + "isReadonly:%INT, " + "mode:%INT, " + "});\n", + name, fieldname, form, is_readonly, mode + ) != 0) + { + mssError(0, "HTTX", "Failed to write JS init call."); + goto err; + } - /** HTML body
element for the base layer. **/ - htrAddBodyItem_va(s, "
\n", + (max_chars > 0), max_chars + ) != 0) + { + mssError(0, "HTTX", "Failed to write HTML textarea tags."); + goto err; + } - /** Use CSS border or table for drawing? **/ - /*if (is_raised) - htrAddStylesheetItem_va(s, "\t#tx%POSbase { border-style:solid; border-width:1px; border-color: white gray gray white; %STR }\n", id, main_bg); - else - htrAddStylesheetItem_va(s, "\t#tx%POSbase { border-style:solid; border-width:1px; border-color: gray white white gray; %STR }\n", id, main_bg); - if (h >= 0) - htrAddStylesheetItem_va(s,"\t#tx%POSbase { height:%POSpx; }\n", id, h-2*box_offset);*/ + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 1) != 0) goto err; - /** Check for more sub-widgets **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+1); + /** Write HTML to close containers. **/ + if (htrAddBodyItem(s, "
") != 0) + { + mssError(0, "HTTX", "Failed to write HTML container closing tags."); + goto err; + } - /** End the containing layer. **/ - htrAddBodyItem(s, "
\n"); + /** Success. **/ + return 0; - return 0; + err: + mssError(0, "HTTX", + "Failed to render \"%s\":\"%s\" (id: %d).", + tree->Name, tree->Type, id + ); + return -1; } @@ -206,27 +240,6 @@ httxInitialize() strcpy(drv->WidgetName,"textarea"); drv->Render = httxRender; - /** Add a 'set value' action **/ - htrAddAction(drv,"SetValue"); - htrAddParam(drv,"SetValue","Value",DATA_T_STRING); /* value to set it to */ - htrAddParam(drv,"SetValue","Trigger",DATA_T_INTEGER); /* whether to trigger the Modified event */ - - /** Value-modified event **/ - htrAddEvent(drv,"Modified"); - htrAddParam(drv,"Modified","NewValue",DATA_T_STRING); - htrAddParam(drv,"Modified","OldValue",DATA_T_STRING); - - /** Events **/ - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - htrAddEvent(drv,"DataChange"); - htrAddEvent(drv,"GetFocus"); - htrAddEvent(drv,"LoseFocus"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_textbutton.c b/centrallix/htmlgen/htdrv_textbutton.c deleted file mode 100644 index 34575df52..000000000 --- a/centrallix/htmlgen/htdrv_textbutton.c +++ /dev/null @@ -1,318 +0,0 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - -/************************************************************************/ -/* Centrallix Application Server System */ -/* Centrallix Core */ -/* */ -/* Copyright (C) 1998-2004 LightSys Technology Services, Inc. */ -/* */ -/* This program 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 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program 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 this program; if not, write to the Free Software */ -/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ -/* 02111-1307 USA */ -/* */ -/* A copy of the GNU General Public License has been included in this */ -/* distribution in the file "COPYING". */ -/* */ -/* Module: htdrv_textbutton.c */ -/* Author: Greg Beeley (GRB) */ -/* Creation: January 5, 1998 */ -/* Description: HTML Widget driver for a 'text button', which frames a */ -/* text string in a 3d-like box that simulates the 3d */ -/* clicking action when the user points and clicks. */ -/************************************************************************/ - - -/** globals **/ -static struct - { - int idcnt; - } - HTTBTN; - - -/*** httbtnRender - generate the HTML code for the page. - ***/ -int -httbtnRender(pHtSession s, pWgtrNode tree, int z) - { - char* ptr; - char name[64]; - char text[64]; - char fgcolor1[64]; - char fgcolor2[64]; - char bgstyle[128]; - char disable_color[64]; - int x,y,w,h; - int id, i; - int is_ts = 1; - int is_enabled = 1; - pExpression code; - int box_offset; - //int clip_offset; - int border_radius; - char border_style[32]; - char border_color[64]; - char image_position[16]; /* top, left, right, bottom */ - char image[OBJSYS_MAX_PATH]; - char h_align[16]; - int image_width=0, image_height=0, image_margin=0; - - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom0IE && !(s->Capabilities.Dom1HTML && s->Capabilities.Dom2CSS)) - { - mssError(1,"HTTBTN","Netscape DOM or (W3C DOM1 HTML and W3C DOM2 CSS) support required"); - return -1; - } - - /** Get an id for this. **/ - id = (HTTBTN.idcnt++); - - /** Get x,y,w,h of this object **/ - if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) - { - mssError(1,"HTTBTN","TextButton widget must have an 'x' property"); - return -1; - } - if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) - { - mssError(1,"HTTBTN","TextButton widget must have a 'y' property"); - return -1; - } - if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) - { - mssError(1,"HTTBTN","TextButton widget must have a 'width' property"); - return -1; - } - if (wgtrGetPropertyValue(tree,"height",DATA_T_INTEGER,POD(&h)) != 0) h = -1; - if (wgtrGetPropertyType(tree,"enabled") == DATA_T_STRING && wgtrGetPropertyValue(tree,"enabled",DATA_T_STRING,POD(&ptr)) == 0 && ptr) - { - if (!strcasecmp(ptr,"false") || !strcasecmp(ptr,"no")) is_enabled = 0; - } - - /** Border radius, color, and style. For style, we only support outset, - ** solid, and none here. - **/ - if (wgtrGetPropertyValue(tree,"border_radius",DATA_T_INTEGER,POD(&border_radius)) != 0) - border_radius=0; - if (wgtrGetPropertyValue(tree,"border_color",DATA_T_STRING,POD(&ptr)) != 0) - strcpy(border_color, "#c0c0c0"); - else - strtcpy(border_color, ptr, sizeof(border_color)); - if (wgtrGetPropertyValue(tree,"border_style",DATA_T_STRING,POD(&ptr)) != 0 || (strcmp(ptr,"outset") && strcmp(ptr,"solid") && strcmp(ptr,"none"))) - strcpy(border_style, "outset"); - else - strtcpy(border_style, ptr, sizeof(border_style)); - - /** Alignment **/ - if (wgtrGetPropertyValue(tree,"align",DATA_T_STRING,POD(&ptr)) == 0 && (!strcmp(ptr,"left") || !strcmp(ptr,"right") || !strcmp(ptr,"center"))) - strtcpy(h_align, ptr, sizeof(h_align)); - else - strcpy(h_align, "center"); - - /** Image location **/ - if (wgtrGetPropertyValue(tree,"image_position",DATA_T_STRING,POD(&ptr)) != 0 || (strcmp(ptr,"top") && strcmp(ptr,"right") && strcmp(ptr,"bottom") && strcmp(ptr, "left"))) - strcpy(image_position, "top"); - else - strtcpy(image_position, ptr, sizeof(image_position)); - - /** Image source **/ - if (wgtrGetPropertyValue(tree,"image",DATA_T_STRING,POD(&ptr)) != 0) - strcpy(image, ""); - else - strtcpy(image, ptr, sizeof(image)); - - /** Image sizing **/ - if (wgtrGetPropertyValue(tree,"image_width",DATA_T_INTEGER, POD(&image_width)) != 0) - image_width = 0; - if (wgtrGetPropertyValue(tree,"image_height",DATA_T_INTEGER, POD(&image_height)) != 0) - image_height = 0; - if (wgtrGetPropertyValue(tree,"image_margin",DATA_T_INTEGER, POD(&image_margin)) != 0) - image_margin = 0; - - /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; - strtcpy(name,ptr,sizeof(name)); - - /** box adjustment... arrgh **/ - if (s->Capabilities.CSSBox) - box_offset = 1; - else - box_offset = 0; - //clip_offset = s->Capabilities.CSSClip?1:0; - - /** User requesting expression for enabled? **/ - if (wgtrGetPropertyType(tree,"enabled") == DATA_T_CODE) - { - wgtrGetPropertyValue(tree,"enabled",DATA_T_CODE,POD(&code)); - is_enabled = 0; - htrAddExpression(s, name, "enabled", code); - } - - /** Threestate button or twostate? **/ - if (wgtrGetPropertyValue(tree,"tristate",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"no")) is_ts = 0; - - /** Get normal, point, and click images **/ - ptr = "-"; - if (!htrCheckAddExpression(s, tree, name, "text") && wgtrGetPropertyValue(tree,"text",DATA_T_STRING,POD(&ptr)) != 0) - { - mssError(1,"HTTBTN","TextButton widget must have a 'text' property"); - return -1; - } - strtcpy(text,ptr,sizeof(text)); - - /** Get fgnd colors 1,2, and background color **/ - htrGetBackground(tree, NULL, 1, bgstyle, sizeof(bgstyle)); - - if (wgtrGetPropertyValue(tree,"fgcolor1",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(fgcolor1,ptr,sizeof(fgcolor1)); - else - strcpy(fgcolor1,"white"); - if (wgtrGetPropertyValue(tree,"fgcolor2",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(fgcolor2,ptr,sizeof(fgcolor2)); - else - strcpy(fgcolor2,"black"); - if (wgtrGetPropertyValue(tree,"disable_color",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(disable_color,ptr,sizeof(disable_color)); - else - strcpy(disable_color,"#808080"); - - htrAddScriptGlobal(s, "tb_current", "null", 0); - - /** DOM Linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "tb%POSpane",id); - - /** Include the javascript code for the textbutton **/ - htrAddScriptInclude(s, "/sys/js/htdrv_textbutton.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); - - /** Initial CSS styles **/ - htrAddStylesheetItem_va(s,"\t#tb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; %[HEIGHT:%POSpx; %]WIDTH:%POSpx; Z-INDEX:%POS; OVERFLOW:hidden; display:table; }\n", - id, - x, y, h>=0, h-1-2*box_offset, w-1-2*box_offset, z - ); - htrAddStylesheetItem_va(s, "\t#tb%POSpane .cell { height:100%%; width:100%%; vertical-align:middle; display:table-cell; padding:1px; font-weight:bold; cursor:default; text-align:%STR; border-width:1px; border-style:%STR&CSSVAL; border-color:%STR&CSSVAL; border-radius:%INTpx; color:%STR&CSSVAL; %[text-shadow:1px 1px %STR&CSSVAL; %]%STR }\n", - /* clipping no longer needed: 0, w-1-2*box_offset+2*clip_offset, h-1-2*box_offset+2*clip_offset, 0, */ - id, - h_align, - border_style, border_color, border_radius, - is_enabled?fgcolor1:disable_color, is_enabled, fgcolor2, - bgstyle - ); - - /** CSS for image on the button **/ - if (image[0] && (image_width || image_height || image_margin)) - { - htrAddStylesheetItem_va(s, "\t#tb%POSpane img { %[height:%POSpx; %]%[width:%POSpx; %]%[margin:%POSpx;%] }\n", - id, - image_height, image_height, - image_width, image_width, - image_margin, image_margin); - } - -#if 00 - if(h >=0 ) - { - htrAddStylesheetItem_va(s,"\t#tb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; HEIGHT:%POSpx; WIDTH:%POSpx; Z-INDEX:%POS; OVERFLOW:hidden; clip:rect(%INTpx %INTpx %INTpx %INTpx)}\n", id, x, y, h-1-2*box_offset, w-1-2*box_offset, z, 0, w-1-2*box_offset+2*clip_offset, h-1-2*box_offset+2*clip_offset, 0); - } - else - { - htrAddStylesheetItem_va(s,"\t#tb%POSpane { POSITION:absolute; VISIBILITY:inherit; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; OVERFLOW:hidden; clip:rect(%INTpx %INTpx auto %INTpx)}\n",id,x,y,w-1-2*box_offset,z,0,w-1-2*box_offset+2*clip_offset,0); - } - htrAddStylesheetItem_va(s,"\t#tb%POSpane { cursor:default; text-align: center; %STR border-width: 1px; border-style: solid; border-color: white gray gray white; border-radius: %INTpx; }\n",id,bgstyle, border_radius); - if (is_enabled) - htrAddStylesheetItem_va(s,"\t#tb%POSspan { color:%STR&CSSVAL; text-shadow:1px 1px %STR&CSSVAL; }\n", id, fgcolor1, fgcolor2); - else - htrAddStylesheetItem_va(s,"\t#tb%POSspan { color:%STR&CSSVAL; }\n", id, disable_color); -#endif - - //htrAddBodyItem_va(s,"
%STR&HTE
\n", id, h-3, id, text); - //htrAddBodyItem(s, "
"); - - /** We need two DIVs here because of a long-outstanding Firefox bug :( **/ - htrAddBodyItem_va(s,"
%[
%]%[%]%STR&HTE%[%]%[
%]
", - id, - image[0] && !strcmp(image_position, "top"), image, - image[0] && !strcmp(image_position, "left"), image, - text, - image[0] && !strcmp(image_position, "right"), image, - image[0] && !strcmp(image_position, "bottom"), image - ); - - /** Script initialization call. **/ - //htrAddScriptInit_va(s, " tb_init({layer:wgtrGetNodeRef(ns,'%STR&SYM'), span:document.getElementById(\"tb%POSspan\"), ena:%INT, c1:\"%STR&JSSTR\", c2:\"%STR&JSSTR\", dc1:\"%STR&JSSTR\", top:null, bottom:null, right:null, left:null, width:%INT, height:%INT, tristate:%INT, name:\"%STR&SYM\", text:'%STR&JSSTR'});\n", - //name, id, is_enabled, fgcolor1, fgcolor2, disable_color, w, h, is_ts, name, text); - htrAddScriptInit_va(s, " tb_init({layer:wgtrGetNodeRef(ns,'%STR&SYM'), ena:%INT, c1:\"%STR&JSSTR\", c2:\"%STR&JSSTR\", dc1:\"%STR&JSSTR\", top:null, bottom:null, right:null, left:null, width:%INT, height:%INT, tristate:%INT, name:\"%STR&SYM\", text:'%STR&JSSTR'});\n", - name, is_enabled, fgcolor1, fgcolor2, disable_color, w, h, is_ts, name, text); - - /** Add the event handling scripts **/ - htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "tb", "tb_mousedown"); - htrAddEventHandlerFunction(s, "document", "MOUSEUP", "tb", "tb_mouseup"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "tb", "tb_mouseover"); - htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "tb", "tb_mouseout"); - htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "tb", "tb_mousemove"); - - /** IE handles dblclick strangely **/ - if (s->Capabilities.Dom0IE) - htrAddEventHandlerFunction(s, "document", "DBLCLICK", "tb", "tb_dblclick"); - - /** Check for more sub-widgets within the textbutton. **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+3); - - return 0; - } - - -/*** httbtnInitialize. - register with the ht_render module. - ***/ -int -httbtnInitialize() - { - pHtDriver drv; - - /** Allocate the driver **/ - drv = htrAllocDriver(); - if (!drv) return -1; - - /** Fill in the structure. **/ - strcpy(drv->Name,"HTML Text Button Widget Driver"); - strcpy(drv->WidgetName,"textbutton"); - drv->Render = httbtnRender; - - /** Add the 'click' event **/ - htrAddEvent(drv, "Click"); - htrAddEvent(drv, "MouseUp"); - htrAddEvent(drv, "MouseDown"); - htrAddEvent(drv, "MouseOver"); - htrAddEvent(drv, "MouseOut"); - htrAddEvent(drv, "MouseMove"); - - /** Register. **/ - htrRegisterDriver(drv); - - htrAddSupport(drv, "dhtml"); - - HTTBTN.idcnt = 0; - - return 0; - } diff --git a/centrallix/htmlgen/htdrv_timer.c b/centrallix/htmlgen/htdrv_timer.c index 65be49c7a..d5cc45c62 100644 --- a/centrallix/htmlgen/htdrv_timer.c +++ b/centrallix/htmlgen/htdrv_timer.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -42,6 +30,14 @@ /* other actions on the page to occur. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /*** httmRender - generate the HTML code for the timer nonvisual widget. ***/ @@ -54,17 +50,18 @@ httmRender(pHtSession s, pWgtrNode tree, int z) int auto_reset = 0; int auto_start = 1; - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom1HTML) + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTTM","Netscape 4.x or W3C DOM support required"); - return -1; + mssError(1, "HTTM", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } /** Get msec for timer countdown **/ if (wgtrGetPropertyValue(tree,"msec",DATA_T_INTEGER,POD(&msec)) != 0) { mssError(1,"HTTM","Timer widget must have 'msec' time specified"); - return -1; + goto err; } /** Get auto reset and auto start settings **/ @@ -72,18 +69,39 @@ httmRender(pHtSession s, pWgtrNode tree, int z) if (wgtrGetPropertyValue(tree,"auto_start",DATA_T_INTEGER,POD(&auto_start)) != 0) auto_start = 1; /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto err; strtcpy(name,ptr,sizeof(name)); - htrAddScriptInclude(s, "/sys/js/htdrv_timer.js", 0); + /** Write JS include. **/ + if (htrAddScriptInclude(s, "/sys/js/htdrv_timer.js", 0) != 0) goto err; /** Script initialization call. **/ - htrAddScriptInit_va(s, " tm_init({node:wgtrGetNodeRef(ns,\"%STR&SYM\"), time:%INT, autoreset:%INT, autostart:%INT});\n", name, msec, auto_reset, auto_start); + if (htrAddScriptInit_va(s, + "\ttm_init({ " + "node:wgtrGetNodeRef(ns, '%STR&SYM'), " + "time:%INT, " + "autoreset:%INT, " + "autostart:%INT, " + "});\n", + name, msec, auto_reset, auto_start + ) != 0) + { + mssError(0, "HTTM", "Failed to write JS init call."); + goto err; + } - /** Check for objects within the timer. **/ - htrRenderSubwidgets(s, tree, z+2); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 2) != 0) goto err; - return 0; + /** Success. **/ + return 0; + + err: + mssError(0, "HTTM", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); + return -1; } @@ -103,17 +121,6 @@ httmInitialize() strcpy(drv->WidgetName,"timer"); drv->Render = httmRender; - /** Add an 'expired' event **/ - htrAddEvent(drv,"Expire"); - - /** Add a 'set timer' action **/ - htrAddAction(drv,"SetTimer"); - htrAddParam(drv,"SetTimer","Time",DATA_T_INTEGER); - htrAddParam(drv,"SetTimer","AutoReset",DATA_T_INTEGER); - - /** Add a 'cancel timer' action **/ - htrAddAction(drv,"CancelTimer"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_treeview.c b/centrallix/htmlgen/htdrv_treeview.c index 413a2123c..f805523dc 100644 --- a/centrallix/htmlgen/htdrv_treeview.c +++ b/centrallix/htmlgen/htdrv_treeview.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,15 @@ /* Description: HTML Widget driver for a treeview. */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -69,43 +66,42 @@ httreeRender(pHtSession s, pWgtrNode tree, int z) char hfgcolor[64]; char selected_bg[128]; int x,y,w; - int id, i; int show_root = 1; int show_branches = 1; int show_root_branch = 1; int use_3d_lines; int order_desc = 0; - pWgtrNode sub_tree; - if(!s->Capabilities.Dom0NS && !s->Capabilities.Dom0IE && !(s->Capabilities.Dom1HTML && s->Capabilities.Dom2CSS)) + /** Get an id for this. **/ + const int id = (HTTREE.idcnt++); + + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - mssError(1,"HTTREE","Netscape DOM or W3C DOM1 HTML and DOM2 CSS support required"); - return -1; + mssError(1, "HTTREE", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTTREE.idcnt++); - /** Get x,y,w,h of this object **/ if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) { mssError(1,"HTTREE","TreeView widget must have an 'x' property"); - return -1; + goto err; } if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) { mssError(1,"HTTREE","TreeView widget must have a 'y' property"); - return -1; + goto err; } if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) { mssError(1,"HTTREE","TreeView widget must have a 'width' property"); - return -1; + goto err; } /** Are we showing root of tree or the trunk? **/ show_root = htrGetBoolean(tree, "show_root", 1); - if (show_root < 0) return -1; + if (show_root < 0) goto err; /** How about branches? (branch decorations, etc.) **/ show_branches = htrGetBoolean(tree, "show_branches", 1); @@ -136,7 +132,7 @@ httreeRender(pHtSession s, pWgtrNode tree, int z) } /** Get name **/ - if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) return -1; + if (wgtrGetPropertyValue(tree,"name",DATA_T_STRING,POD(&ptr)) != 0) goto err; strtcpy(name,ptr,sizeof(name)); /** Selected item background color **/ @@ -165,76 +161,152 @@ httreeRender(pHtSession s, pWgtrNode tree, int z) if (wgtrGetPropertyValue(tree,"source",DATA_T_STRING,POD(&ptr)) != 0) { mssError(1,"HTTREE","TreeView widget must have a 'source' property"); - return -1; + goto err; } strtcpy(src,ptr,sizeof(src)); - /** Ok, write the style header items. **/ - if (s->Capabilities.Dom0NS) + /** Write CSS. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#tv%POSload { " + "position:absolute; " + "visibility:hidden; " + "overflow:hidden; " + "left:0px; " + "top:0px; " + "width:0px; " + "height:0px; " + "z-index:0; " + "}\n", + id + ) != 0) + { + mssError(0, "HTTREE", "Failed to write treeview loader CSS."); + goto err; + } + + /*** htdrv_treeview.js appends the row layers inside the root layer, + *** giving each row its indentation (left edge). + ***/ + if (htrAddStylesheetItem_va(s, + "\t\tdiv.tv%POS a { %[color:%STR&CSSVAL;%] }\n" + "\t\tdiv.tv%POSh a { %[color:%STR&CSSVAL;%] }\n" + "\t\t.tv%POS, .tv%POSh { " + "cursor:pointer; " + "overflow:hidden; " + "white-space:nowrap; " + "}\n" + "\t\t#tv%POSroot { cursor:default; }\n" + "\t\t#tv%POSroot > span, #tv%POSroot > nobr { cursor:pointer; }\n", + id, (*fgcolor), fgcolor, + id, (*hfgcolor), hfgcolor, + id, id, id, id, id + ) != 0) { - htrAddStylesheetItem_va(s,"\t#tv%POSroot { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; Z-INDEX:%POS; }\n",id,show_root?"inherit":"hidden",x,y,w,z); + mssError(0, "HTTREE", "Failed to write treeview entry CSS."); + goto err; } - htrAddStylesheetItem_va(s,"\t#tv%POSload { POSITION:absolute; VISIBILITY:hidden; OVERFLOW:hidden; LEFT:0px; TOP:0px; WIDTH:0px; HEIGHT:0px; clip:rect(0px,0px,0px,0px); Z-INDEX:0; }\n",id); - htrAddStylesheetItem_va(s,"\tdiv.tv%POS a { %[color:%STR&CSSVAL;%] }\n", id, *fgcolor, fgcolor); - htrAddStylesheetItem_va(s,"\tdiv.tv%POSh a { %[color:%STR&CSSVAL;%] }\n", id, *hfgcolor, hfgcolor); + + /** Link the widget to the DOM node. **/ + if (htrAddWgtrObjLinkage_va(s, tree, "tv%POSroot", id) != 0) goto err; /** Write globals for internal use **/ - htrAddScriptGlobal(s, "tv_tgt_layer", "null", 0); - htrAddScriptGlobal(s, "tv_target_img","null",0); - htrAddScriptGlobal(s, "tv_layer_cache","null",0); - htrAddScriptGlobal(s, "tv_alloc_cnt","0",0); - htrAddScriptGlobal(s, "tv_cache_cnt","0",0); + if (htrAddScriptGlobal(s, "tv_alloc_cnt", "0", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "tv_cache_cnt", "0", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "tv_layer_cache", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "tv_target_img", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "tv_tgt_layer", "null", 0) != 0) goto err; - /** DOM Linkage on client **/ - htrAddWgtrObjLinkage_va(s, tree, "tv%POSroot",id); + /** Write JS script includes. **/ + if (htrAddScriptInclude(s, "/sys/js/ht_utils_info.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_treeview.js", 0) != 0) goto err; - /** Script initialization call. **/ - htrAddScriptInit_va(s," tv_init({layer:wgtrGetNodeRef(ns,\"%STR&SYM\"), fname:\"%STR&JSSTR\", loader:htr_subel(wgtrGetParentContainer(wgtrGetNodeRef(ns,\"%STR&SYM\")),\"tv%POSload\"), width:%INT, newroot:null, branches:%INT, use3d:%INT, showrb:%INT, icon:\"%STR&JSSTR\", divclass:\"tv%POS\", sbg:\"%STR&JSSTR\", desc:%INT});\n", - name, src, name, id, w, show_branches, use_3d_lines, show_root_branch, icon, id, selected_bg, order_desc); + /** Add the event handling scripts **/ + if (htrAddEventHandlerFunction(s, "document", "CLICK", "tv", "tv_click") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "tv", "tv_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "tv", "tv_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "tv", "tv_mouseout") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "tv", "tv_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "tv", "tv_mouseup") != 0) goto err; - /** Script includes **/ - htrAddScriptInclude(s, "/sys/js/htdrv_treeview.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_string.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_info.js", 0); + /** Script initialization call. **/ + if (htrAddScriptInit_va(s, "\t{ " + "const layer = wgtrGetNodeRef(ns, '%STR&SYM'); " + "tv_init({ " + "layer, " + "loader:htr_subel(wgtrGetParentContainer(layer),'tv%POSload'), " + "divclass:'tv%POS', " + "fname:'%STR&JSSTR', " + "newroot:null, " + "branches:%INT, " + "use3d:%INT, " + "showroot:%INT, " + "showrb:%INT, " + "icon:'%STR&JSSTR', " + "sbg:'%STR&JSSTR', " + "desc:%INT, " + "}); }\n", + name, id, id, src, + show_branches, use_3d_lines, show_root, show_root_branch, + icon, selected_bg, order_desc + ) != 0) + { + mssError(0, "HTTREE", "Failed to write script init call."); + goto err; + } - /** HTML body
elements for the layers. **/ - if (s->Capabilities.Dom0NS) + /*** Write HTML. The root layer holds the row layers, so it always + *** stays visible; a hidden root only hides its own row content. + ***/ + if (htrAddBodyItem_va(s, + "
" + "" + "folder" + " %STR&HTE" + "" + "
\n", + id, /* class */ + id, /* id */ + ht_flex_x(x, tree), + ht_flex_y(y, tree), + ht_flex_w(w, tree), + z, + (show_root) ? "inherit" : "hidden", + (*icon) ? icon : "/sys/images/ico02b.gif", src + ) != 0) { - htrAddBodyItem_va(s, "
 %STR&HTE
\n", id, id, (*icon)?icon:"/sys/images/ico02b.gif", src); - htrAddBodyItem_va(s, "
\n",id); + mssError(0, "HTTREE", "Failed to write HTML for treeview root."); + goto err; } - else + if (htrAddBodyItemLayer_va(s, HTR_LAYER_F_DYNAMIC, "tv%POSload", id, NULL, "") != 0) { - htrAddBodyItem_va(s, "
 %STR&HTE
\n",id,id,show_root?"inherit":"hidden",x,y,w,z,(*icon)?icon:"/sys/images/ico02b.gif", src); - htrAddBodyItemLayer_va(s, HTR_LAYER_F_DYNAMIC, "tv%POSload", id, NULL, ""); - /*htrAddBodyItem_va(s, "
\n",id);*/ + mssError(0, "HTTREE", "Failed to write HTML for treeview loader."); + goto err; } - /** Event handler for click-on-url **/ - htrAddEventHandlerFunction(s, "document","CLICK","tv","tv_click"); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 2) != 0) goto err; - /** Add the event handling scripts **/ - htrAddEventHandlerFunction(s, "document","MOUSEDOWN","tv","tv_mousedown"); - htrAddEventHandlerFunction(s, "document","MOUSEUP","tv","tv_mouseup"); - htrAddEventHandlerFunction(s,"document","MOUSEOVER","tv","tv_mouseover"); - htrAddEventHandlerFunction(s,"document","MOUSEMOVE","tv","tv_mousemove"); - htrAddEventHandlerFunction(s,"document","MOUSEOUT","tv", "tv_mouseout"); - - /** Check for more sub-widgets within the treeview. **/ - for (i=0;iChildren));i++) - { - sub_tree = xaGetItem(&(tree->Children), i); - /*if (wgtrGetPropertyValue(sub_tree, "outer_type", DATA_T_STRING, POD(&ptr)) == 0 && !strcmp(ptr, "widget/osrc-rule")) - { - httree_internal_AddRule(s, tree, name, sub_tree); - } - else - {*/ - htrRenderWidget(s, sub_tree, z+2); - /*}*/ - } + /** Success. **/ + return 0; - return 0; + err: + mssError(0, "HTTREE", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); + return -1; } @@ -254,20 +326,6 @@ httreeInitialize() strcpy(drv->WidgetName,"treeview"); drv->Render = httreeRender; - /** Add the 'click item' event **/ - htrAddEvent(drv,"ClickItem"); - htrAddParam(drv,"ClickItem","Pathname",DATA_T_STRING); - htrAddEvent(drv,"Click"); - htrAddEvent(drv,"MouseUp"); - htrAddEvent(drv,"MouseDown"); - htrAddEvent(drv,"MouseOver"); - htrAddEvent(drv,"MouseOut"); - htrAddEvent(drv,"MouseMove"); - - /** Add the 'rightclick item' event **/ - htrAddEvent(drv,"RightClickItem"); - htrAddParam(drv,"RightClickItem","Pathname",DATA_T_STRING); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/htmlgen/htdrv_uawindow.c b/centrallix/htmlgen/htdrv_uawindow.c index 3349c4c6c..9fddbd5a3 100644 --- a/centrallix/htmlgen/htdrv_uawindow.c +++ b/centrallix/htmlgen/htdrv_uawindow.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1999-2010 LightSys Technology Services, Inc. */ +/* Copyright (C) 1999-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,6 +28,15 @@ /* Description: HTML Widget driver for a user-agent window */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "obj.h" +#include "wgtr.h" + /*** htuawinRender - generate the HTML code for the page. ***/ @@ -69,9 +66,11 @@ htuawinRender(pHtSession s, pWgtrNode tree, int z) /** Share this window with other references in the same session context? (no) **/ is_shared = htrGetBoolean(tree, "shared", 0); + if (is_shared < 0) goto err; /** Allow multiple instances of the window? (no) **/ is_multi = htrGetBoolean(tree, "multiple_instantiation", 0); + if (is_multi < 0) goto err; /** Routing of actions to windows when there are multiple of them **/ if (wgtrGetPropertyValue(tree, "action_routing", DATA_T_STRING, POD(&ptr)) == 0) @@ -81,26 +80,46 @@ htuawinRender(pHtSession s, pWgtrNode tree, int z) else if (!strcmp(ptr, "all")) action_routing = 2; else { - mssError(1, "HTUAWIN", "Invalid action_routing '%s' for widget '%s'", ptr, name); - return -1; + mssError(1, "HTUAWIN", "Invalid action_routing: \"%s\"", ptr); + goto err; } } /** widget init **/ - htrAddScriptInit_va(s, " uw_init(wgtrGetNodeRef(ns,\"%STR&SYM\"), {shared:%INT, multi:%INT, routing:%INT, path:\"%STR&JSSTR\", w:%INT, h:%INT} );\n", - name, is_shared, is_multi, action_routing, path, width, height - ); + if (htrAddScriptInit_va(s, + "\tuw_init(wgtrGetNodeRef(ns, '%STR&SYM'), { " + "shared:%INT, " + "multi:%INT, " + "routing:%INT, " + "path:'%STR&JSSTR', " + "w:%INT, " + "h:%INT, " + "});\n", + name, is_shared, is_multi, action_routing, path, width, height + ) != 0) + { + mssError(0, "HTUAWIN", "Failed to write JS init call."); + goto err; + } /** JavaScript include file **/ - htrAddScriptInclude(s, "/sys/js/htdrv_uawindow.js", 0); + if (htrAddScriptInclude(s, "/sys/js/htdrv_uawindow.js", 0) != 0) goto err; - /** object linkages **/ - htrAddWgtrCtrLinkage(s, tree, "_parentctr"); + /** Link the widget to its container. **/ + if (htrAddWgtrCtrLinkage(s, tree, "_parentctr") != 0) goto err; - /** Check for more sub-widgets within the vbl entity. **/ - htrRenderSubwidgets(s, tree, z+2); + /** Render children. **/ + if (htrRenderSubwidgets(s, tree, z + 2) != 0) goto err; - return 0; + /** Success. **/ + return 0; + + err: + mssError(0, "HTUAWIN", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); + return -1; } diff --git a/centrallix/htmlgen/htdrv_variable.c b/centrallix/htmlgen/htdrv_variable.c index 13b5a5a0a..a10744a60 100644 --- a/centrallix/htmlgen/htdrv_variable.c +++ b/centrallix/htmlgen/htdrv_variable.c @@ -1,20 +1,8 @@ -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1999-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1999-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -41,6 +29,17 @@ /* provides a place to store a value. */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/newmalloc.h" +#include "cxlib/strtcpy.h" +#include "expression.h" +#include "ht_render.h" +#include "obj.h" +#include "wgtr.h" + /*** htvblRender - generate the HTML code for the page. ***/ @@ -51,8 +50,7 @@ htvblRender(pHtSession s, pWgtrNode tree, int z) char name[64]; char fieldname[HT_FIELDNAME_SIZE]; char form[64]; - int t; - int i; + int rval = -1; int n = 0; char* vptr = NULL; int is_null = 1; @@ -73,7 +71,7 @@ htvblRender(pHtSession s, pWgtrNode tree, int z) form[0]='\0'; /** Value of label **/ - t = wgtrGetPropertyType(tree,"value"); + int t = wgtrGetPropertyType(tree,"value"); if (t < 0 || t >= OBJ_TYPE_NAMES_CNT) { t = DATA_T_ANY; @@ -95,29 +93,51 @@ htvblRender(pHtSession s, pWgtrNode tree, int z) } /** widget init **/ - htrAddScriptInit_va(s, " vbl_init(wgtrGetNodeRef(ns,\"%STR&SYM\"), {type:\"%STR&JSSTR\", value:%[null%]%[\"%STR&JSSTR\"%]%[%INT%], field:\"%STR&JSSTR\", form:\"%STR&JSSTR\"} );\n", - name, - obj_type_names[t], - is_null, - (!is_null) && t == DATA_T_STRING, vptr, - (!is_null) && t == DATA_T_INTEGER, n, - fieldname, form); + if (htrAddScriptInit_va(s, + "\tvbl_init(wgtrGetNodeRef(ns, '%STR&SYM'), { " + "type:'%STR&JSSTR', " + "value:%['%STR&JSSTR'%]%[%INT%]%[null%], " + "field:'%STR&JSSTR', " + "form:'%STR&JSSTR', " + "});\n", + name, + obj_type_names[t], + (!is_null && t == DATA_T_STRING), vptr, + (!is_null && t == DATA_T_INTEGER), n, + (is_null), + fieldname, form + ) != 0) + { + mssError(0, "HTVBL", "Failed to write JS init call."); + goto end; + } /** JavaScript include file **/ - htrAddScriptInclude(s, "/sys/js/htdrv_variable.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0); + if (htrAddScriptInclude(s, "/sys/js/ht_utils_hints.js", 0) != 0) goto end; + if (htrAddScriptInclude(s, "/sys/js/htdrv_variable.js", 0) != 0) goto end; /** object linkages **/ - htrAddWgtrCtrLinkage(s, tree, "_parentctr"); + if (htrAddWgtrCtrLinkage(s, tree, "_parentctr") != 0) goto end; /** Check for more sub-widgets within the vbl entity. **/ - for (i=0;iChildren));i++) - htrRenderWidget(s, xaGetItem(&(tree->Children), i), z+2); + if (htrRenderSubwidgets(s, tree, z + 2) != 0) goto end; - if (vptr) - nmSysFree(vptr); + /** Success. **/ + rval = 0; - return 0; + end: + if (rval != 0) + { + mssError(0, "HTVBL", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); + } + + /** Clean up. **/ + if (vptr != NULL) nmSysFree(vptr); + + return rval; } @@ -127,8 +147,6 @@ int htvblInitialize() { pHtDriver drv; - /*pHtEventAction action; - pHtParam param;*/ /** Allocate the driver **/ drv = htrAllocDriver(); diff --git a/centrallix/htmlgen/htdrv_window.c b/centrallix/htmlgen/htdrv_window.c index 9937ebff5..95df8cd39 100644 --- a/centrallix/htmlgen/htdrv_window.c +++ b/centrallix/htmlgen/htdrv_window.c @@ -1,21 +1,8 @@ -#include -#include -#include -#include -#include -#include "ht_render.h" -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/xarray.h" -#include "cxlib/xhash.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1999-2014 LightSys Technology Services, Inc. */ +/* Copyright (C) 1999-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -43,6 +30,15 @@ /* a 'titlebar' with a close (X) button on it. */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/strtcpy.h" +#include "ht_render.h" +#include "wgtr.h" + /** globals **/ static struct @@ -59,46 +55,36 @@ htwinRender(pHtSession s, pWgtrNode tree, int z) { char* ptr; char name[64]; - pWgtrNode sub_tree; - int x,y,w,h; - int tbw,tbh;//bx,by,bw,bh; - int id, i; int visible = 1; - char bgnd_style[128] = ""; - char hdr_bgnd_style[128] = ""; - char txtcolor[64] = ""; + char background_style[128] = ""; + char header_background_style[128] = ""; + char text_color[64] = ""; int has_titlebar = 1; char title[128]; int is_dialog_style = 0; int gshade = 0; - int closetype = 0; - int box_offset = 1; int is_toplevel = 0; int is_modal = 0; char icon[128]; - int shadow_offset,shadow_radius,shadow_angle; + int shadow_offset, shadow_radius, shadow_angle; char shadow_color[128]; int border_radius; char border_color[128]; char border_style[32]; int border_width; - if(!(s->Capabilities.Dom0NS || s->Capabilities.Dom1HTML)) - { - mssError(1,"HTWIN","Netscape DOM support or W3C DOM Level 1 support required"); - return -1; - } + /** Get an id for this. **/ + const int id = (HTWIN.idcnt++); - /** IE puts css box borders inside box width/height **/ - if (!s->Capabilities.CSSBox) + /** Verify browser capabilities. **/ + if (!s->Capabilities.Dom1HTML || !s->Capabilities.Dom2CSS) { - box_offset = 0; + mssError(1, "HTWIN", "Unsupported browser: W3C DOM1 HTML and DOM2 CSS support required."); + goto err; } - /** Get an id for this. **/ - id = (HTWIN.idcnt++); - /** Get x,y,w,h of this object **/ + int x, y, w, h; if (wgtrGetPropertyValue(tree,"x",DATA_T_INTEGER,POD(&x)) != 0) x = 0; if (wgtrGetPropertyValue(tree,"y",DATA_T_INTEGER,POD(&y)) != 0) y = 0; if (wgtrGetPropertyValue(tree,"width",DATA_T_INTEGER,POD(&w)) != 0) @@ -156,17 +142,17 @@ htwinRender(pHtSession s, pWgtrNode tree, int z) is_modal = htrGetBoolean(tree, "modal", 0); /** Check background color **/ - htrGetBackground(tree, NULL, 1, bgnd_style, sizeof(bgnd_style)); + htrGetBackground(tree, NULL, 1, background_style, sizeof(background_style)); /** Check header background color/image **/ - if (htrGetBackground(tree, "hdr", 1, hdr_bgnd_style, sizeof(hdr_bgnd_style)) < 0) - strcpy(hdr_bgnd_style, bgnd_style); + if (htrGetBackground(tree, "hdr", 1, header_background_style, sizeof(header_background_style)) != 0) + strcpy(header_background_style, background_style); /** Check title text color. **/ if (wgtrGetPropertyValue(tree,"textcolor",DATA_T_STRING,POD(&ptr)) == 0) - strtcpy(txtcolor,ptr,sizeof(txtcolor)); + strtcpy(text_color, ptr, sizeof(text_color)); else - strcpy(txtcolor,"black"); + strcpy(text_color, "black"); /** Check window title. **/ if (wgtrGetPropertyValue(tree,"title",DATA_T_STRING,POD(&ptr)) == 0) @@ -178,24 +164,14 @@ htwinRender(pHtSession s, pWgtrNode tree, int z) visible = htrGetBoolean(tree, "visible", 1); /** No titlebar? **/ - if (wgtrGetPropertyValue(tree,"titlebar",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"no")) - has_titlebar = 0; + has_titlebar = htrGetBoolean(tree, "titlebar", 1); /** Dialog or window style? **/ if (wgtrGetPropertyValue(tree,"style",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"dialog")) is_dialog_style = 1; /** Graphical window shading? **/ - if (wgtrGetPropertyValue(tree,"gshade",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"true")) - gshade = 1; - - /** Graphical window close? **/ - if (wgtrGetPropertyValue(tree,"closetype",DATA_T_STRING,POD(&ptr)) == 0) - { - if (!strcmp(ptr,"shrink1")) closetype = 1; - else if (!strcmp(ptr,"shrink2")) closetype = 2; - else if (!strcmp(ptr,"shrink3")) closetype = 1 | 2; - } + gshade = htrGetBoolean(tree, "gshade", 0); /** Window icon? **/ if (wgtrGetPropertyValue(tree, "icon", DATA_T_STRING, POD(&ptr)) == 0) @@ -208,142 +184,269 @@ htwinRender(pHtSession s, pWgtrNode tree, int z) } /** Compute titlebar width & height - includes edge below titlebar. **/ - if (has_titlebar) - { - tbw = w-2; - if (is_dialog_style || !s->Capabilities.Dom0NS) - tbh = 24; - else - tbh = 23; - } - else + int title_bar_height = (has_titlebar) ? 24 : 0; + + /** Error if the specified size is too small to draw. **/ + int min_width = 2*border_width; + int min_height = 2*border_width + title_bar_height; + if (w < min_width || h < min_height) { - tbw = w-2; - tbh = 0; + mssError(1, "HTWIN", + "Window \"%s\" is %dx%d, too small to draw: a window with %s " + "and a %dpx border must be at least %dx%d.", + tree->Name, w, h, + (has_titlebar) ? "a titlebar" : "no titlebar", + border_width, min_width, min_height + ); + goto err; } - /** Compute window body geometry **/ - // if (is_dialog_style) - // { - // bx = 1; - // by = 1+tbh; - // bw = w-2; - // bh = h-tbh-2; - // } - // else - // { - // bx = 2; - // bw = w-4; - // if (has_titlebar) - // { - // by = 1+tbh; - // bh = h-tbh-3; - // } - // else - // { - // by = 2; - // bh = h-4; - // } - // } - /** Draw the main window layer and outer edge. **/ - /*htrAddStylesheetItem_va(s,"\t#wn%POSbase { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; overflow: hidden; clip:rect(0px, %INTpx, %INTpx, 0px); Z-INDEX:%POS;}\n", - id,visible?"inherit":"hidden",x,y,w-2*box_offset,h-2*box_offset, w, h, z+100);*/ - htrAddStylesheetItem_va(s,"\t#wn%POSbase { POSITION:absolute; VISIBILITY:%STR; LEFT:%INTpx; TOP:%INTpx; WIDTH:%POSpx; HEIGHT:%POSpx; overflow: hidden; Z-INDEX:%POS;}\n", - id,visible?"inherit":"hidden",x,y,w-2*box_offset,h-2*box_offset, z+100); - htrAddStylesheetItem_va(s,"\t#wn%POSbase { border-style: %STR&CSSVAL; border-width: %INTpx; border-color: %STR&CSSVAL; border-radius: %INTpx; }\n", - id, border_style, border_width, border_color, border_radius); - if (shadow_radius > 0) + /*** We don't even bother making the position flex responsively because + *** the JS re-places the window on resize anyway. The size is not + *** responsive: a window keeps the size it was given. + *** + *** border-box means width/height are the size the app asked for, with + *** the border drawn inside them, so no border arithmetic is needed here. + ***/ + if (htrAddStylesheetItem_va(s, + "\t\t#wn%POSbase { " + "position:absolute; " + "visibility:%STR; " + "overflow:hidden; " + "box-sizing:border-box; " + "left:%INTpx; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "z-index:%POS; " + "border-style:%STR&CSSVAL; " + "border-width:%INTpx; " + "border-color:%STR&CSSVAL; " + "border-radius:%INTpx; " + "}\n", + id, + (visible) ? "inherit" : "hidden", + x, y, w, h, + z + 100, + border_style, + border_width, + border_color, + border_radius + ) != 0) { - htrAddStylesheetItem_va(s,"\t#wn%POSbase { box-shadow: %DBLpx %DBLpx %POSpx %STR&CSSVAL; }\n", id, - sin(shadow_angle*M_PI/180)*shadow_offset, cos(shadow_angle*M_PI/180)*(-shadow_offset), shadow_radius, shadow_color); + mssError(0, "HTWIN", "Failed to write CSS for window base."); + goto err; } - - /** draw titlebar div **/ - if (has_titlebar) + if (shadow_radius > 0) { - htrAddStylesheetItem_va(s,"\t#wn%POStitlebar { POSITION: absolute; VISIBILITY: inherit; LEFT: 0px; TOP: 0px; HEIGHT: %POSpx; WIDTH: 100%%; overflow: hidden; Z-INDEX: %POS; color:%STR&CSSVAL; cursor:default; %STR}\n", id, tbh-1-box_offset, z+1, txtcolor, hdr_bgnd_style); - htrAddStylesheetItem_va(s,"\t#wn%POStitlebar { border-style: solid; border-width: 0px 0px 1px 0px; border-color: gray; }\n", id); + double shadow_angle_radians = (double)shadow_angle * M_PI/180; + if (htrAddStylesheetItem_va(s, + "\t\t#wn%POSbase { " + "box-shadow: " + "%DBLpx " + "%DBLpx " + "%POSpx " + "%STR&CSSVAL; " + "}\n", id, + sin(shadow_angle_radians) * shadow_offset, + cos(shadow_angle_radians) * (-shadow_offset), + shadow_radius, + shadow_color + ) != 0) + { + mssError(0, "HTWIN", "Failed to write CSS for window shadow."); + goto err; + } } - /** inner structure depends on dialog vs. window style **/ + /*** The inner container fills what is left of the base below the titlebar. + *** The base is border-box, so its content box is the size it was given + *** less its border. + ***/ + int main_width = w - 2*border_width; + int main_height = h - 2*border_width - title_bar_height; + + /** Only the edges around the inner container depend on dialog vs. window style **/ + int main_side_width, main_top_width; + char* border_color_str; if (is_dialog_style) { - /** window inner container -- dialog **/ - htrAddStylesheetItem_va(s,"\t#wn%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:%INTpx; WIDTH: %POSpx; HEIGHT:%POSpx; overflow: hidden; clip:rect(0px, %INTpx, %INTpx, 0px); Z-INDEX:%POS; %STR}\n", - id, tbh?(tbh-1):0, w-2, h-tbh-1, w, h-tbh+1, z+1, bgnd_style); - htrAddStylesheetItem_va(s,"\t#wn%POSmain { border-style: solid; border-width: %POSpx 0px 0px 0px; border-color: white; }\n", id, has_titlebar?1:0); + main_side_width = 0; + main_top_width = (has_titlebar) ? 1 : 0; + border_color_str = "white"; } else { - /** window inner container -- window **/ - htrAddStylesheetItem_va(s,"\t#wn%POSmain { POSITION:absolute; VISIBILITY:inherit; LEFT:0px; TOP:%INTpx; WIDTH: %POSpx; HEIGHT:%POSpx; overflow: hidden; clip:rect(0px, %INTpx, %INTpx, 0px); Z-INDEX:%POS; %STR}\n", - id, tbh?(tbh-1):0, w-2-2*box_offset, h-tbh-(has_titlebar?1:2)-(has_titlebar?1:2)*box_offset, w, h-tbh+(has_titlebar?1:0)-2*box_offset, z+1, bgnd_style); - htrAddStylesheetItem_va(s,"\t#wn%POSmain { border-style: solid; border-width: %POSpx 1px 1px 1px; border-color: gray white white gray; }\n", id, has_titlebar?0:1); + main_side_width = 1; + main_top_width = (has_titlebar) ? 0 : 1; + border_color_str = "gray white white gray"; + } + if (htrAddStylesheetItem_va(s, + "\t\t#wn%POSmain { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "box-sizing:border-box; " + "left:0px; " + "top:%INTpx; " + "width:%POSpx; " + "height:%POSpx; " + "clip:rect(0px, %INTpx, %INTpx, 0px); " + "border-style:solid; " + "border-color:%STR&CSSVAL; " + "border-width:%POSpx %POSpx %POSpx %POSpx; " + "z-index:%POS; " + "%STR" + "}\n", + id, + title_bar_height, + main_width, + main_height, + main_width, main_height, + border_color_str, + main_top_width, main_side_width, main_side_width, main_side_width, + z + 1, + background_style + ) != 0) + { + mssError(0, "HTWIN", "Failed to write CSS for window main container."); + goto err; } - /** Write globals for internal use **/ - htrAddScriptGlobal(s, "wn_top_z","10000",0); - htrAddScriptGlobal(s, "wn_list","[]",0); - htrAddScriptGlobal(s, "wn_current","null",0); - htrAddScriptGlobal(s, "wn_newx","null",0); - htrAddScriptGlobal(s, "wn_newy","null",0); - htrAddScriptGlobal(s, "wn_topwin","null",0); - htrAddScriptGlobal(s, "wn_msx","null",0); - htrAddScriptGlobal(s, "wn_msy","null",0); - htrAddScriptGlobal(s, "wn_moved","0",0); - htrAddScriptGlobal(s, "wn_clicked","0",0); + /** Write JS globals and includes. **/ + if (htrAddScriptGlobal(s, "wn_clicked", "0", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "wn_current", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "wn_list", "[]", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "wn_moved", "0", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "wn_msx", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "wn_msy", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "wn_new_x", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "wn_new_y", "null", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "wn_top_z", "10000", 0) != 0) goto err; + if (htrAddScriptGlobal(s, "wn_topwin", "null", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0) != 0) goto err; + if (htrAddScriptInclude(s, "/sys/js/htdrv_window.js", 0) != 0) goto err; /** DOM Linkages **/ - htrAddWgtrObjLinkage_va(s, tree, "wn%POSbase",id); - htrAddWgtrCtrLinkage_va(s, tree, "htr_subel(_obj, \"wn%POSmain\")",id); - - htrAddScriptInclude(s, "/sys/js/htdrv_window.js", 0); - htrAddScriptInclude(s, "/sys/js/ht_utils_layers.js", 0); + if (htrAddWgtrObjLinkage_va(s, tree, "wn%POSbase", id) != 0) goto err; + if (htrAddWgtrCtrLinkage_va(s, tree, "htr_subel(_obj, 'wn%POSmain')", id) != 0) goto err; /** Event handler for mousedown/up/click/etc **/ - htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "wn", "wn_mousedown"); - htrAddEventHandlerFunction(s, "document", "MOUSEUP", "wn", "wn_mouseup"); - htrAddEventHandlerFunction(s, "document", "DBLCLICK", "wn", "wn_dblclick"); + if (htrAddEventHandlerFunction(s, "document", "DBLCLICK", "wn", "wn_dblclick") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEDOWN", "wn", "wn_mousedown") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEUP", "wn", "wn_mouseup") != 0) goto err; /** Mouse move event handler -- when user drags the window **/ - htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "wn", "wn_mousemove"); - htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "wn", "wn_mouseover"); - htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "wn", "wn_mouseout"); + if (htrAddEventHandlerFunction(s, "document", "MOUSEMOVE", "wn", "wn_mousemove") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOVER", "wn", "wn_mouseover") != 0) goto err; + if (htrAddEventHandlerFunction(s, "document", "MOUSEOUT", "wn", "wn_mouseout") != 0) goto err; /** Script initialization call. **/ - if (has_titlebar) + if (htrAddScriptInit_va(s, + "\twn_init({ " + "mainlayer:wgtrGetNodeRef(ns, '%STR&SYM'), " + "clayer:wgtrGetContainer(wgtrGetNodeRef(ns, '%STR&SYM')), " + "titlebar:%[htr_subel(wgtrGetNodeRef(ns, '%STR&SYM'), 'wn%POStitlebar')%]%[null%], " + "gshade:%INT, " + "toplevel:%INT, " + "modal:%INT, " + "centeredx:%INT, " + "centeredy:%INT, " + "});\n", + name, + name, + has_titlebar, name, id, !has_titlebar, + gshade, + is_toplevel, + is_modal, + !!(tree->Flags & WGTR_F_CENTEREDX), + !!(tree->Flags & WGTR_F_CENTEREDY) + ) != 0) { - htrAddScriptInit_va(s," wn_init({mainlayer:wgtrGetNodeRef(ns,'%STR&SYM'), clayer:wgtrGetContainer(wgtrGetNodeRef(ns,'%STR&SYM')), gshade:%INT, closetype:%INT, toplevel:%INT, modal:%INT, titlebar:htr_subel(wgtrGetNodeRef(ns,'%STR&SYM'),'wn%POStitlebar')});\n", - name,name,gshade,closetype, is_toplevel, is_modal, name, id); + mssError(0, "HTWIN", "Failed to write JS init call."); + goto err; } - else + + /** Write HTML for the child window. **/ + if (htrAddBodyItem_va(s, "
\n", id) != 0) { - htrAddScriptInit_va(s," wn_init({mainlayer:wgtrGetNodeRef(ns,'%STR&SYM'), clayer:wgtrGetNodeRef(ns,'%STR&SYM'), gshade:%INT, closetype:%INT, toplevel:%INT, modal:%INT, titlebar:null});\n", - name,name,gshade,closetype, is_toplevel, is_modal); + mssError(0, "HTWIN", "Failed to write HTML for window container."); + goto err; } - - /** HTML body
elements for the layers. **/ - htrAddBodyItem_va(s,"
\n",id); if (has_titlebar) { - htrAddBodyItem_va(s,"
\n",id); - htrAddBodyItem_va(s,"
 %STR&HTE
\n", - tbh-1, tbw-2, icon, txtcolor, title); - htrAddBodyItem(s, "
\n"); + /** Write CSS and HTML for the title bar. **/ + if (htrAddStylesheetItem_va(s, + "\t\t#wn%POStitlebar { " + "position:absolute; " + "visibility:inherit; " + "overflow:hidden; " + "box-sizing:border-box; " + "cursor:grab; " + "left:0px; " + "top:0px; " + "height:%POSpx; " + "width:100%%; " + "z-index:%POS; " + "color:%STR&CSSVAL; " + "border-style:solid; " + "border-width:0px 0px 1px 0px; " + "border-color:gray; " + "%STR" + "}\n", + id, + title_bar_height, + z + 1, + text_color, + header_background_style + ) != 0) + { + mssError(0, "HTWIN", "Failed to write styles for window title bar."); + goto err; + } + if (htrAddBodyItem_va(s, + /*** Note: The user grabs the title bar to drag a window. + *** Thus, its child layers need pointer-events:none to avoid + *** stealing this event. The close button is an exception. + ***/ + "
" + "" + "
%STR&HTE
" + "" + "
\n", + id, + icon, + text_color, title + ) != 0) + { + mssError(0, "HTWIN", "Failed to write HTML for window title bar."); + goto err; + } } - htrAddBodyItem_va(s,"
\n",id); - /** Check for more sub-widgets within the page. **/ - for (i=0;iChildren));i++) + /** Render child widgets inside window container. **/ + if (htrAddBodyItem_va(s,"
\n",id) != 0) + { + mssError(0, "HTWIN", "Failed to write HTML opening tag for window container."); + goto err; + } + if (htrRenderSubwidgets(s, tree, z + 2) != 0) goto err; + if (htrAddBodyItem(s,"
\n") != 0) { - sub_tree = xaGetItem(&(tree->Children), i); - htrRenderWidget(s, sub_tree, z+2); + mssError(0, "HTWIN", "Failed to write HTML closing tag for window container."); + goto err; } - htrAddBodyItem(s,"
\n"); + /** Success. **/ + return 0; - return 0; + err: + mssError(0, "HTWIN", + "Failed to render \"%s\":\"%s\".", + tree->Name, tree->Type + ); + return -1; } @@ -363,22 +466,6 @@ htwinInitialize() strcpy(drv->WidgetName,"childwindow"); drv->Render = htwinRender; - /** Add the 'click' event **/ - htrAddEvent(drv, "MouseUp"); - htrAddEvent(drv, "MouseDown"); - htrAddEvent(drv, "MouseOver"); - htrAddEvent(drv, "MouseOut"); - htrAddEvent(drv, "MouseMove"); - - /** Add the 'set visibility' action **/ - htrAddAction(drv,"ToggleVisibility"); - htrAddAction(drv,"SetVisibility"); - htrAddParam(drv,"SetVisibility","IsVisible",DATA_T_INTEGER); - htrAddParam(drv,"SetVisibility","NoInit",DATA_T_INTEGER); - - /** Add the 'window closed' event **/ - htrAddEvent(drv,"Close"); - /** Register. **/ htrRegisterDriver(drv); diff --git a/centrallix/include/apos.h b/centrallix/include/apos.h index a40d3d26d..8ef29f7dc 100644 --- a/centrallix/include/apos.h +++ b/centrallix/include/apos.h @@ -5,7 +5,7 @@ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -31,6 +31,8 @@ /* Description: Applies layout logic to the widgets of an application. */ /************************************************************************/ +#include +#include #include "wgtr.h" #include "cxlib/xarray.h" @@ -51,8 +53,16 @@ struct _APOS_L XArray CWidgets; //widgets that cross the line pAposSection SSection; // section starting with this line pAposSection ESection; // section ending with this line - }; + /*** Used to find the distance the line should move when the parent + *** container is resized. loc_fl (local flex) is the weight that this + *** line moves relative to the container. my_fl (my flex) is the amount + *** that this line moves relative to the line before (left of) it. + *** Used for generating responsive CSS. + ***/ + float loc_fl, my_fl; + }; + /**Section Structure (used for both rows and columns)**/ struct _APOS_S { @@ -80,18 +90,15 @@ typedef struct /**Function Definitions**/ int aposAutoPositionWidgetTree(pWgtrNode); /**top-level function, called from wgtr module**/ int aposAutoPositionContainers (pWgtrNode); /**Auto-positions all widgets inside a container**/ -int aposInit(); /**Registers datastructures used in auto-positioning**/ -int aposInitiallizeGrid (pAposGrid); /**Initiallizes the XArrays in the grid object**/ -int aposFree(pAposGrid); /**Frees dynamically allocated memory**/ -int aposFreeGrids(pWgtrNode); /**Frees dynamically allocated memory**/ -int aposSetOffsetBools(pWgtrNode, int*, int*, int*, int*, int*); /**sets bools used to offset widgets**/ +void aposInit(); /**Registers datastructures used in auto-positioning**/ +int aposInitializeGrid(pAposGrid); /**Initializes the XArrays in the grid object**/ +void aposFree(pAposGrid); /**Frees dynamically allocated memory**/ +void aposFreeGrids(pWgtrNode); /**Frees dynamically allocated memory**/ int aposBuildGrid(pWgtrNode); /** builds the layout grids **/ int aposSetLimits(pWgtrNode); /** enforce min/max sizing **/ /**Tree Preparation**/ -int aposPrepareTree(pWgtrNode, pXArray); /**Prepares widget tree for auto-positioning**/ -int aposPatchNegativeHeight(pWgtrNode, pXArray);/**Temporarily sets unspecified heights**/ -int aposSetContainerFlex(pWgtrNode); /**Determines a container's flexibility**/ +void aposSetContainerFlex(pWgtrNode); /**Determines a container's flexibility**/ int aposSetFlexibilities(pWgtrNode); /**Determines a container's flexibility**/ int aposSetSectionFlex(pAposSection sect, int type); @@ -105,34 +112,69 @@ int aposFillInCWidget(pXArray, pXArray, pXArray); /**Fills in the CWidget array /**Section Creation**/ int aposAddSectionsToGrid(pAposGrid, int, int); /**Adds all of the necessary sections to the grid**/ int aposCreateSection(pXArray, pAposLine, pAposLine, int, int); /**Creates a section object**/ -int aposIsSpacer(pAposLine, pAposLine, int, int);/**Determines if a section is a space between widgets**/ -int aposNonFlexChildren(pAposLine, int); /**Checks section after line for non-flexible widgets**/ -int aposAverageChildFlex(pAposLine, int); /**Returns average flexibility of widgets**/ +bool aposIsSpacer(pAposLine, pAposLine, int, int);/**Determines if a section is a space between widgets**/ +bool aposNonFlexChildren(pAposLine, int); /**Checks section after line for non-flexible widgets**/ int aposMinimumChildFlex(pAposLine, int); /**Returns minimum flexibility of widgets**/ /**Resizing and Repositioning**/ int aposSpaceOutLines(pXArray, pXArray, int); /**Adjusts spaces between lines to expand or contract grid**/ -int aposSnapWidgetsToGrid(pXArray, int); /**Refreshes widget dimensions to match adjusted grid**/ +int aposSpaceOutLines_r(pXArray, pXArray, int, bool); /**Recursive worker for aposSpaceOutLines()**/ +void aposSnapWidgetsToGrid(pXArray, int, pWgtrClientInfo); /**Refreshes widget dimensions to match adjusted grid**/ int aposProcessWindows(pWgtrNode, pWgtrNode); /**Makes a pass through the tree to process windows**/ -/** # defines **/ + +/** #define names for values to improve readability. **/ + +/** Indicates how a line links to a widget. **/ +#define APOS_NOT_LINKED 0 #define APOS_SWIDGETS 1 #define APOS_EWIDGETS 2 +/** Indicates if a line is vertical. **/ +#define APOS_VERTICAL 1 +#define APOS_HORIZONTAL 0 + +/*** Indicates if a section or line is a row (horizontal) or a column (vertical). + *** A row spans horizontally between two vertical lines, and a column spans + *** vertically between two horizontal lines. + ***/ #define APOS_ROW 1 #define APOS_COL 2 +/** Indicates if a line is a border. **/ +#define APOS_IS_BORDER 1 +#define APOS_NOT_BORDER 0 + +/** Allows rounding when casting floats or doubles to ints. **/ #define APOS_FUDGEFACTOR 0.5 -/** The greatest width between two widgets that still defines them as "adjacent," -*** indicating that we don't want to increase the distance between them **/ +/*** The greatest width between two widgets that still defines them as + *** "adjacent," indicating that we don't want to increase the distance + *** between them. Therefore, a section of this size or less is considered + *** a "spacer" which will not be resized (aka. flex = 0). + ***/ #define APOS_MINSPACE 20 -/**Lowest acceptable width or height for a widget**/ +/** The lowest acceptable width or height for a widget. **/ #define APOS_MINWIDTH 30 -/**Default flexibilities for widgetless gaps in expanding or contracting applications **/ +/** Default flexibilities for widgetless gaps in expanding or contracting applications. **/ +/*** Israel: I don't know the difference between these two values. I'm guessing + *** E stands enlarge and C stands for contract, thus (30, 50) makes it + *** easier for gaps to grown than for them to shrink. + ***/ #define APOS_EGAPFLEX 30 #define APOS_CGAPFLEX 50 +/*** Sentinel for a minimum-flexibility search that has not seen a widget yet. + *** Flexibilities are unbounded, so this must be larger than any of them. + ***/ +#define APOS_NOFLEXFOUND INT_MAX + +/** Flexibility reported for a line that holds no widgets at all. **/ +#define APOS_NOCHILDFLEX 100 + +/** Macros for readability and anticipation-of-change. **/ +#define isScrollpane(widget) (strcmp((widget)->Type, "widget/scrollpane") == 0) + #endif diff --git a/centrallix/include/ht_render.h b/centrallix/include/ht_render.h index aeded08fe..722733a2a 100644 --- a/centrallix/include/ht_render.h +++ b/centrallix/include/ht_render.h @@ -5,7 +5,7 @@ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -77,25 +77,6 @@ typedef struct _HTN HtNamespace, *pHtNamespace; -/** Widget parameter structure, also used for positioning params **/ -typedef struct - { - char ParamName[32]; /* Name of parameter */ - int DataType; /* DATA_T_xxx, from obj.h */ - XArray EnumValues; /* If enumerated, values listing */ - } - HtParam, *pHtParam; - - -/** Widget event or action structure, used for handling connectors **/ -typedef struct - { - char Name[32]; /* Name of event or action */ - XArray Parameters; /* Listing of parameters */ - } - HtEventAction, *pHtEventAction; - - /** WGTR RenderFlag defines **/ #define HT_WGTF_NOOBJECT 1 /* wgt node does not have a corresponding DHTML object */ #define HT_WGTF_NORENDER 2 /* do not deploy this node to the client. */ @@ -107,39 +88,12 @@ typedef struct char WidgetName[64]; /* Name of widget. */ int (*Setup)(); int (*Render)(); /* Function to render the page */ - XArray PosParams; /* Positioning parameter listing. */ XArray Properties; /* Properties this thing will have. */ - XArray Events; /* Events for this widget type */ - XArray Actions; /* Actions on this widget type */ XArray PseudoTypes; /* Pseudo-types this widget driver will handle */ } HtDriver, *pHtDriver; -/** Widget parameter value structure. **/ -typedef struct - { - pHtParam Parameter; /* Param that this value is for. */ - int IntVal; /* if DATA_T_INTEGER, the int value */ - char* StringVal; /* if DATA_T_STRING, the string value */ - int StringAlloc; /* set if string was malloc()'d. */ - DateTime DateVal; /* if DATA_T_DATETIME, the date value */ - int EnumVal; /* if DATA_T_ENUM, the enumerated id */ - } - HtValue, *pHtValue; - - -/** Widget parameterized tree structure for pre-rendering **/ -typedef struct _HT - { - pHtDriver Driver; /* Driver to handle this object. */ - XArray Values; /* Parameters for widget construction */ - XArray LocValues; /* Positioning params for this widget */ - XArray Children; - } - HtTree, *pHtTree; - - /** Structure for holding string-string-value data. **/ typedef struct { @@ -253,7 +207,6 @@ typedef struct typedef struct { HtPage Page; /* the generated page... */ - pHtTree Tree; /* tree page metainfo structure */ int DisableBody; char* Tmpbuf; /* temp buffer used in _va() functions */ size_t TmpbufSize; @@ -264,7 +217,6 @@ typedef struct int Width; /* target container (browser) width in pixels */ int Height; /* target container height in pixels */ char* GraftPoint; /* name of target container */ - pWgtrVerifySession VerifySession; /* name of the current verification session */ pWgtrClientInfo ClientInfo; pHtNamespace Namespace; /* current namespace */ int IsDynamic; @@ -298,7 +250,6 @@ int htrAddBodyItem_va(pHtSession s, char* fmt, ... ); int htrAddBodyParam(pHtSession s, char* html_param); /*int htrAddBodyParam_va(pHtSession s, char* fmt, ... ) __attribute__((format(printf, 2, 3)));*/ int htrAddBodyParam_va(pHtSession s, char* fmt, ... ); -int htrAddEventHandler(pHtSession s, char* event_src, char* event, char* drvname, char* handler_code); int htrAddEventHandlerFunction(pHtSession s, char* event_src, char* event, char* drvname, char* function); int htrAddScriptFunction(pHtSession s, char* fn_name, char* fn_text, int flags); int htrAddScriptGlobal(pHtSession s, char* var_name, char* initialization, int flags); @@ -347,12 +298,11 @@ int htrAddBodyItemLayerEnd(pHtSession s, int flags); /** Administrative functions **/ int htrRegisterDriver(pHtDriver drv); int htrInitialize(); +char* htrGetErrorHTMLMsg(char* title, char* err_str); +char* htrGetErrorHTML(char* title); int htrRender(void* stream, int (*stream_write)(void*, char*, int, int, int), pObjSession s, pWgtrNode tree, pStruct params, pWgtrClientInfo c_info); int htrWrite(pHtSession s, char* buf, int len); int htrQPrintf(pHtSession s, char* fmt, ...); -int htrAddAction(pHtDriver drv, char* action_name); -int htrAddEvent(pHtDriver drv, char* event_name); -int htrAddParam(pHtDriver drv, char* eventaction, char* param_name, int datatype); pHtDriver htrAllocDriver(); int htrAddSupport(pHtDriver drv, char* className); char* htrParamValue(pHtSession s, char* paramname); @@ -362,5 +312,80 @@ int htrBuildClientWgtr(pHtSession s, pWgtrNode tree); /** For the rule module... **/ int htruleRegister(char* ruletype, ...); -#endif /* _HT_RENDER_H */ +/*** Write strings known at compile time slightly more efficiently. + *** + *** @param str A string literal known at compile time. This must be a + *** literal value and it may be applied multiple times. + ***/ +#define htrWriteConst(s, str) htrWrite(s, (str), sizeof(str) - 1) + + + +/** ===================================================== **/ +/** Define macros for implementing responsive dimensions. **/ +/** ===================================================== **/ + +/*** Brief explanation of the responsiveness formula. + *** + *** Responsive dimensions in widgets use the following formula: + *** Original px + (100% - Total px) * Flex + *** Where "Original" is the original size of the object in an adaptive layout, + *** "Total" is the total size of the widget's parent container, and + *** "Flex" is the widget flexibility (where all flexibilities add to 1). + *** All, with respect to the given dimension. + *** + *** The intuition behind this formula is that (100% - Total px) is 0px + *** if the parent container is the size intended by the adaptive design. + *** However, if the user resizes the window, (100% - Total px) is the + *** difference between the size in the adaptive design and the current + *** size, so the widget changes size with respect to that difference. + ***/ + +/** @brief The qprintf format to specify a responsive dimension. **/ +#define ht_flex_format "calc(%INTpx + (100%% - %POSpx) * %DBL)" + +/*** @brief The function which generates the values that should be passed to + *** qprintf in order to satisfy an ht_flex_format. + *** + *** @param size The original size of the ui element. + *** @param total The total size of the ui element's container. + *** @param flex The flexibility of the ui element. It is strongly recommended + *** to generate this with an ht_get_fl function call. + *** @returns Several values to serve as parameters for a qprintf call. + ***/ +#define ht_flex(size, total, flex) (size), (total), (flex) + + +/** ====[ Macros for getting flex-related values ]==== **/ +#define ht_get_parent_w(widget) wgtrGetContainerWidth(widget) +#define ht_get_parent_h(widget) wgtrGetContainerHeight(widget) + +/*** @param widget The widget to be queried. + *** @returns The flexibility of the widget in the x direction. + ***/ +#define ht_get_fl_x(widget) ((widget)->fl_scale_x) + +/*** @param widget The widget to be queried. + *** @returns The flexibility of the widget in the y direction. + ***/ +#define ht_get_fl_y(widget) ((widget)->fl_scale_y) + +/*** @param widget The widget to be queried. + *** @returns The flexibility of the widget in the width direction. + ***/ +#define ht_get_fl_w(widget) ((widget)->fl_scale_w) + +/*** @param widget The widget to be queried. + *** @returns The flexibility of the widget in the height direction. + ***/ +#define ht_get_fl_h(widget) ((widget)->fl_scale_h) + + +/** ====[ Macros for simple flex cases ]==== **/ + +#define ht_flex_x(x, widget) ht_flex(x, ht_get_parent_w(widget), ht_get_fl_x(widget)) +#define ht_flex_y(y, widget) ht_flex(y, ht_get_parent_h(widget), ht_get_fl_y(widget)) +#define ht_flex_w(w, widget) ht_flex(w, ht_get_parent_w(widget), ht_get_fl_w(widget)) +#define ht_flex_h(h, widget) ht_flex(h, ht_get_parent_h(widget), ht_get_fl_h(widget)) +#endif /* _HT_RENDER_H */ diff --git a/centrallix/include/wgtr.h b/centrallix/include/wgtr.h index 99f8c3760..0accfd92b 100644 --- a/centrallix/include/wgtr.h +++ b/centrallix/include/wgtr.h @@ -5,7 +5,7 @@ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1999-2006 LightSys Technology Services, Inc. */ +/* Copyright (C) 1999-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -39,20 +39,28 @@ #include "cxlib/xarray.h" #include "iface.h" -#define WGTR_MAX_TEMPLATE 8 /** Maximum templates concurrently applying to page **/ -#define WGTR_MAX_OVERLAY 16 /** Maximum overlays in an app **/ - -#define WGTR_F_NONVISUAL 1 /** a widget is visual by default, non-visual if this is set **/ -#define WGTR_F_CONTAINER 2 /** set for container widgets **/ -#define WGTR_F_FLOATING 4 /** widgets that 'float' - childwindows and popup menus **/ -#define WGTR_F_CONTROL 8 /** control structure (i.e. repeat) that should be 'looked through' to its subobjects **/ -#define WGTR_F_VSCROLLABLE 16 /** contents are vertically scrollable. Do not resize Y/height **/ -#define WGTR_F_HSCROLLABLE 32 /** contents are horiz scrollable. Do not resize X/width **/ - -#define WGTR_PF_NOTEMPLATE 1 /** Don't handle templates on this wgtr Parse operation **/ -#define WGTR_PF_NOSECURITY 2 /** Don't handle security settings on this wgtr Parse operation **/ - -#define WGTR_DEFAULT_SPACING (10) /** default spacing between widgets **/ +#define WGTR_MAX_TEMPLATE 8 /** Maximum templates concurrently applying to page **/ +#define WGTR_MAX_OVERLAY 16 /** Maximum overlays in an app **/ + +#define WGTR_F_NONE (0 << 0) /** value for a widget with no flags. **/ +#define WGTR_F_NONVISUAL (1 << 0) /** a widget is visual by default, non-visual if this is set **/ +#define WGTR_F_CONTAINER (1 << 1) /** set for container widgets **/ +#define WGTR_F_FLOATING (1 << 2) /** widgets that 'float' - childwindows and popup menus **/ +#define WGTR_F_CONTROL (1 << 3) /** control structure (i.e. repeat) that should be 'looked through' to its subobjects **/ +#define WGTR_F_VSCROLLABLE (1 << 4) /** contents are vertically scrollable. Do not resize Y/height **/ +#define WGTR_F_HSCROLLABLE (1 << 5) /** contents are horiz scrollable. Do not resize X/width **/ +#define WGTR_F_AUTOHEIGHT (1 << 6) /** height was unspecified; the widget sizes itself to its content **/ +#define WGTR_F_FLWIDTHSET (1 << 7) /** fl_width came from the structure file; do not recompute it **/ +#define WGTR_F_FLHEIGHTSET (1 << 8) /** fl_height came from the structure file; do not recompute it **/ +#define WGTR_F_CENTEREDX (1 << 9) /** a floating widget the layout centered horizontally **/ +#define WGTR_F_CENTEREDY (1 << 10) /** a floating widget the layout centered vertically **/ +#define WGTR_F_VISUAL_CONTAINER (1 << 11) /** the widget will emit an visual container **/ + +#define WGTR_PF_NONE (0 << 0) /** value for no flags. **/ +#define WGTR_PF_NOTEMPLATE (1 << 0) /** Don't handle templates on this wgtr Parse operation **/ +#define WGTR_PF_NOSECURITY (1 << 1) /** Don't handle security settings on this wgtr Parse operation **/ + +#define WGTR_DEFAULT_SPACING (10) /** default spacing between widgets **/ #define WGTR_REPEAT_PREFIX "__rpt_" @@ -69,6 +77,7 @@ typedef struct int CharWidth; int CharHeight; int ParagraphHeight; /* total height of one line of text */ + int IsDesign; /* 1 if the page is rendered with cx__geom=design, 0 otherwise. */ char AKey[256]; char* Templates[WGTR_MAX_TEMPLATE]; char* Overlays[WGTR_MAX_OVERLAY]; @@ -100,14 +109,22 @@ typedef struct _WN char Namespace[64]; /** Namespace this widget and subwidgets are in **/ int r_x, r_y, r_width, r_height; /** Requested geometry **/ int pre_x, pre_y, pre_width, pre_height; /** pre-layout geom. **/ - int fl_x, fl_y, fl_width, fl_height;/** Flexibility **/ - double fx, fy, fw, fh; /** internal flexibility calculations **/ + int fl_width, fl_height; /** Flexibilities as specified by the designer **/ + double fx, fy, fw, fh; /** internal flexibility calculations **/ + double fl_scale_x, fl_scale_y; /** Scaled x and y flexibilities calculated for this layout. */ + double fl_scale_w, fl_scale_h; /** Scaled w and h flexibilities calculated for this layout. */ + int fl_parent_w, fl_parent_h; /** The expected size of the parent container, used when it flexes. */ int min_width, min_height; /** absolute minimums **/ - int x, y, width, height; /** actual geometry **/ - int top, bottom, left, right; /** container offsets **/ + int x, y, width, height; /** actual geometry (outer box) **/ + /*** The space that a widget needs in its outer box (x/y/width/height) for + *** its UI (e.g. title bars, tabs, or scrollbars). The remaining space is + *** the widget's client area where children are laid out. + *** @see wgtrSetInsets() + ***/ + int inset_top, inset_bottom, inset_left, inset_right; XArray Properties; /** Array of widget properties **/ XArray Children; /** Array of child widgets **/ - struct _WN* Parent; + struct _WN* Parent; struct _WN* Root; int CurrProperty; /** Property to return on next call to wgtNextProperty **/ int CurrChild; /** Child to return on next call to wgtrNextChild **/ @@ -170,8 +187,9 @@ pWgtrNode wgtrParseOpenObject(pObject obj, pStruct app_params, pWgtrClientInfo c void wgtrFree(pWgtrNode tree); /** frees memory associated with a widget tree **/ pWgtrNode wgtrNewNode( char* name, char* type, pObjSession s, int rx, int ry, int rwidth, int rheight, - int flx, int fly, int flwidth, int flheight); /** create a new widget node **/ + int flwidth, int flheight); /** create a new widget node **/ int wgtrSetupNode(pWgtrNode node); +void wgtrSetInsets(pWgtrNode node, int top, int bottom, int left, int right); /** declare the widget's own reserved space **/ int wgtrMergeOverlays(pWgtrNode node, char* objpath, char* app_path, char* overlays[], char* templates[]); /** wgtr iterator functions **/ @@ -204,8 +222,9 @@ pWgtrNode wgtrFirstChild(pWgtrNode tree); /** return the first child **/ int wgtrImplementsInterface(pWgtrNode this, char* iface_ref); /** state that a wgt implements an interface **/ int wgtrSetDMPrivateData(pWgtrNode tree, void* data); void* wgtrGetDMPrivateData(pWgtrNode tree); -int wgtrGetContainerHeight(pWgtrNode tree); +pWgtrNode wgtrGetHtmlContainer(pWgtrNode tree); int wgtrGetContainerWidth(pWgtrNode tree); +int wgtrGetContainerHeight(pWgtrNode tree); int wgtrMoveChildren(pWgtrNode tree, int x_offset, int y_offset); /** misc. functions **/ diff --git a/centrallix/netdrivers/net_http.c b/centrallix/netdrivers/net_http.c index 0a6ef8f46..cd5bd9f22 100755 --- a/centrallix/netdrivers/net_http.c +++ b/centrallix/netdrivers/net_http.c @@ -3,6 +3,8 @@ #include "cxlib/memstr.h" #include "cxlib/strtcpy.h" #include "json/json.h" +#include "ht_render.h" +#include "cxlib/check.h" /************************************************************************/ /* Centrallix Application Server System */ @@ -768,25 +770,19 @@ nht_i_QPrintfConn(pNhtConn conn, int is_hdr, char* fmt, ...) void nht_i_ErrorExit(pNhtConn conn, int code, char* text) { - pXString err_message; - /** Write the standard HTTP response **/ nht_i_WriteResponse(conn, code, text, NULL); /** Display error info **/ - err_message = xsNew(); - if (err_message) - { - mssStringError(err_message); - fdQPrintf(conn->ConnFD, "\r\n\r\nError\r\n\r\n

%POS %STR&HTE

\r\n
\r\n
%STR&HTE
\r\n\r\n\r\n", - code, - text, - xsString(err_message) - ); - xsFree(err_message); - } + mssError(0, "NHT", "Net HTTP driver failed (error code: %d): %s", code, text); + char* error_title = "Error!"; + char* error_html = checkPtr(htrGetErrorHTML(error_title)); + if (UNLIKELY(error_html == NULL)) error_html = error_title; + checkNeg(nht_i_WriteConn(conn, error_html, -1, 0)); /* Failure ignored. */ + check(mssClearError()); /* Failure ignored. */ /** Shutdown the connection and free memory **/ + if (LIKELY(error_html != error_title)) nmSysFree(error_html); nht_i_FreeConn(conn); thExit(); /* no return */ @@ -1149,7 +1145,11 @@ nht_i_ErrorHandler(pNhtConn net_conn) /** Format the error and print it as HTML. **/ net_conn->NoCache = 1; nht_i_WriteResponse(net_conn, 200, "OK", NULL); - nht_i_QPrintfConn(net_conn, 0, "
%STR&HTE
\r\n", errmsg->String); + char* error_title = "An error occurred!"; + char* error_html = checkPtr(htrGetErrorHTMLMsg(error_title, errmsg->String)); + if (UNLIKELY(error_html == NULL)) error_html = error_title; /* Plaintext fallback. */ + checkNeg(nht_i_WriteConn(net_conn, error_html, -1, 0)); /* Failure ignored. */ + if (LIKELY(error_html != error_title)) nmSysFree(error_html); /** Discard the string **/ xsDeInit(errmsg); @@ -1820,7 +1820,6 @@ nht_i_GET(pNhtConn conn, pStruct url_inf, char* if_modified_since) pNhtApp app = NULL; pNhtAppGroup group = NULL; char* kname; - pXString err_xs; acceptencoding=(char*)mssGetParam("Accept-Encoding"); @@ -2093,6 +2092,9 @@ nht_i_GET(pNhtConn conn, pStruct url_inf, char* if_modified_since) wgtr_params.CharWidth = 7; wgtr_params.CharHeight = 16; wgtr_params.ParagraphHeight = 16; + + /** Specify design geometry **/ + wgtr_params.IsDesign = 1; } else { @@ -2217,13 +2219,16 @@ nht_i_GET(pNhtConn conn, pStruct url_inf, char* if_modified_since) if (nhtRenderApp(conn, target_obj->Session, target_obj, url_inf, &wgtr_params, "DHTML", nsess) < 0) { mssError(0, "HTTP", "Unable to render application %s of type %s", url_inf->StrVal, ptr); - err_xs = xsNew(); - if (err_xs) - { - mssStringError(err_xs); - nht_i_QPrintfConn(conn, 0, "

An error occurred while constructing the application:

%STR&HTE\r\n
", xsString(err_xs)); - xsFree(err_xs); - } + + /** Render an error page to gracefully recover from the error. **/ + char* error_title = "An error occurred while constructing the application."; + char* error_html = checkPtr(htrGetErrorHTML(error_title)); + if (UNLIKELY(error_html == NULL)) error_html = error_title; + checkNeg(nht_i_WriteConn(conn, error_html, -1, 0)); /* Failure ignored. */ + check(mssClearError()); /* Failure ignored. */ + + /** Clean up. **/ + if (LIKELY(error_html != error_title)) nmSysFree(error_html); objClose(target_obj); if (tptr) nmSysFree(tptr); if (lptr) nmSysFree(lptr); diff --git a/centrallix/netdrivers/net_http_app.c b/centrallix/netdrivers/net_http_app.c index f903c538d..83acc26db 100644 --- a/centrallix/netdrivers/net_http_app.c +++ b/centrallix/netdrivers/net_http_app.c @@ -4,7 +4,7 @@ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -24,7 +24,8 @@ /* A copy of the GNU General Public License has been included in this */ /* distribution in the file "COPYING". */ /* */ -/* Module: net_http.h, net_http.c, net_http_conn.c, net_http_sess.c, net_http_osml.c, net_http_app.c */ +/* Module: net_http.h, net_http.c, net_http_conn.c, */ +/* net_http_sess.c, net_http_osml.c, net_http_app.c */ /* Author: Greg Beeley (GRB) */ /* Creation: December 8, 1998 */ /* Description: Network handler providing an HTTP interface to the */ @@ -58,11 +59,11 @@ nht_i_GetGeom(pObject target_obj, pNhtConn output) /** Do we have a bgcolor / background? **/ if (objGetAttrValue(target_obj, "bgcolor", DATA_T_STRING, POD(&ptr)) == 0) { - snprintf(bgnd, sizeof(bgnd), "bgcolor='%.100s'", ptr); + snprintf(bgnd, sizeof(bgnd), "background-color:%.100s", ptr); } else if (objGetAttrValue(target_obj, "background", DATA_T_STRING, POD(&ptr)) == 0) { - snprintf(bgnd, sizeof(bgnd), "background='%.100s'", ptr); + snprintf(bgnd, sizeof(bgnd), "background:url(%.100s)", ptr); } else { @@ -70,22 +71,29 @@ nht_i_GetGeom(pObject target_obj, pNhtConn output) } /** Generate the snippet **/ - nht_i_QPrintfConn(output, 0, "\n" - "\n" - " \n" - " \n" - "\n" - "\n" - "\n" - " \n" - "
x
x
\n" - "
xx
\n" - "\n" - "\n", font_size > 0, font_size, *font_name, font_name, bgnd); + nht_i_QPrintfConn(output, 0, + "\n" + "\n" + " \n" + " Loading...\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " loading...\n" + "
x
x
\n" + "
xx
\n" + " \n" + "\n", + (font_size > 0), font_size, + (font_name != NULL && font_name[0] != '\0'), font_name, + bgnd + ); return 0; } @@ -149,4 +157,3 @@ nhtRenderApp(pNhtConn conn, pObjSession s, pObject obj, pStruct url_inf, pWgtrCl return rval; } - diff --git a/centrallix/netdrivers/net_http_conn.c b/centrallix/netdrivers/net_http_conn.c index bc136affe..4b4c6cb33 100644 --- a/centrallix/netdrivers/net_http_conn.c +++ b/centrallix/netdrivers/net_http_conn.c @@ -28,7 +28,8 @@ /* A copy of the GNU General Public License has been included in this */ /* distribution in the file "COPYING". */ /* */ -/* Module: net_http.h, net_http.c, net_http_conn.c, net_http_sess.c, net_http_osml.c, net_http_app.c */ +/* Module: net_http.h, net_http.c, net_http_conn.c, */ +/* net_http_sess.c, net_http_osml.c, net_http_app.c */ /* Author: Greg Beeley (GRB) */ /* Creation: December 8, 1998 */ /* Description: Network handler providing an HTTP interface to the */ @@ -307,9 +308,10 @@ nht_i_SendRefreshDocument(pNhtConn conn, char* url) /** This is a simple HTML document that loads the url we give it. **/ nht_i_QPrintfConn(conn, 0, "\r\n" - "\r\n" + "\r\n" " \r\n" - " \r\n" " \r\n" diff --git a/centrallix/osdrivers/objdrv_shell.c b/centrallix/osdrivers/objdrv_shell.c index 494c5627c..57e2a6ef3 100644 --- a/centrallix/osdrivers/objdrv_shell.c +++ b/centrallix/osdrivers/objdrv_shell.c @@ -4,7 +4,6 @@ #include #include "obj.h" #include "cxlib/mtask.h" -#include "cxlib/mtsession.h" #include "cxlib/xarray.h" #include "cxlib/xhash.h" #include "stparse.h" @@ -1255,3 +1254,4 @@ MODULE_PREFIX("shl"); MODULE_DESC("SHL ObjectSystem Driver"); MODULE_VERSION(0,1,0); MODULE_IFACE(CX_CURRENT_IFACE); + diff --git a/centrallix/utility/iface_html.c b/centrallix/utility/iface_html.c index deb63c7dc..661c15dce 100644 --- a/centrallix/utility/iface_html.c +++ b/centrallix/utility/iface_html.c @@ -337,10 +337,10 @@ ifcHtmlInit(pHtSession s, pWgtrNode tree) /** first add the necessary DHTML, and call to init **/ htrAddScriptInclude(s, "/sys/js/ht_utils_iface.js", 0); - htrAddStylesheetItem(s, " #ifc_layer {position: absolute; visibility: hidden;}\n"); + htrAddStylesheetItem(s, "\t\t#ifc_layer { position: absolute; visibility: hidden; }\n"); htrAddBodyItem(s, "
\n"); - htrAddScriptInit_va(s, " ifcInitialize(\"%STR&JSSTR\");\n", IFC.IfaceDir); - htrAddScriptInit(s, " init_inline_interfaces();\n"); + htrAddScriptInit_va(s, "\tifcInitialize('%STR&JSSTR');\n", IFC.IfaceDir); + htrAddScriptInit(s, "\tinit_inline_interfaces();\n"); /** now create all the interface info we know of in-line **/ xaInit(&AlreadyProcessed, 16); @@ -359,4 +359,3 @@ ifcHtmlInit(pHtSession s, pWgtrNode tree) return 0; } - diff --git a/centrallix/wgtr/apos.c b/centrallix/wgtr/apos.c index 01f099433..f6ed66363 100644 --- a/centrallix/wgtr/apos.c +++ b/centrallix/wgtr/apos.c @@ -1,8 +1,8 @@ /************************************************************************/ /* Centrallix Application Server System */ -/* Centrallix Core */ +/* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -22,42 +22,123 @@ /* A copy of the GNU General Public License has been included in this */ /* distribution in the file "COPYING". */ /* */ -/* Module: Auto-Positioning */ +/* Module: Auto-Positioning */ /* Author: Nathaniel Colson */ /* Creation: August 9, 2005 */ /* Description: Applies layout logic to the widgets of an application. */ -/* See centrallix-sysdoc/Auto-Positioning.md for more information. */ +/* See centrallix-sysdoc/Auto-Positioning.md for more information. */ /************************************************************************/ - +/*** Author: Israel Fuller + *** Date: June, 2025 + *** + *** See also: Auto-Positioning.md + *** + *** I wasn't the one to write most of this (although I did write a ton of + *** comments), but after doing my best to understand it, I hope that you will + *** find the compiled information below helpful. + *** + *** Execution of this file usually begins when wgtrVerify() in wgtr.c calls + *** aposAutoPositionWidgetTree(). To auto position the tree, the code first + *** it draws four lines on the four edges of every visible widget (with some + *** exceptions, @see aposAddLinesToGrid()). These lines divide the page into + *** into horizontal sections (rows) and vertical sections (columns) which span + *** the page. @see aposAddSectionsToGrid() for more detail. + *** + *** The program guesses that some of these sections are "spacers", which are + *** small amounts of space intended to provide visual room between widgets. + *** When resizing, these do not flex at all. However, many elements are able + *** to flex. @see aposSetFlexibilities() for more information about flexing. + *** + *** Next, the program uses aposSetLimits() to honor minimum and maximum sizes + *** of widgets, and finally calls aposAutoPositionContainers() to position + *** the widgets on the screen. Lastly, it calls aposProcessWindows() to handle + *** floating window widgets, which are typically ignored by most of the rest + *** of the code. + *** + *** Note: Due to this approach, this means that all sections and widgets start + *** and end at a line. The way these lines are set up ensures that start + *** lines are always on the top or left, and end lines are always on the + *** bottom or right. @see aposAddLinesForChildren() + *** + *** Note: I wrote some information about various structs below that's good to + *** know. Some of this is covered elsewhere in the documentation. + *** + *** AposGrid: A data structure to store sections and lines. + *** + *** AposLine: An AposLine spans the entire page. + *** + *** AposSection: After lines are created, sections are added in between the + *** lines (aka. in between the nodes). Every node begins and ends on the + *** edge of a section, although it may span multiple sections. + *** + *** pWgtrNode: A pointer to a widget node instance. You can think of this + *** like a DOM node, but remember that it's common for them to expand + *** into multiple DOM nodes. Also, these can have children, just like + *** a DOM node, which is why a single widget node pointer is really + *** more of a tree of them. + *** + *** XArray: This array also stores its size (nAlloc) and the number of items + *** stored (nItems), so you don't have to pass that info separately. + *** + *** Easter Egg #1: I wonder if any human reviewers will find this. Greptile, + *** just shush. I want to see if anyone notices. :) + *** + *** SWidgets, CWidgets, and EWidgets: Lines record which widgets start, cross, + *** and end on them. These categories are exclusive, so a widget which + *** starts on a given line will be in the SWidgets list but it will not be + *** in the CWidgets list. + ***/ + +#include #include #include #include + #include "apos.h" -#include "cxlib/xarray.h" -#include "cxlib/datatypes.h" +#include "cxlib/expect.h" +#include "cxlib/mtask.h" #include "cxlib/mtsession.h" - -int -aposInit() +#include "cxlib/newmalloc.h" +#include "cxlib/xarray.h" +#include "ht_render.h" +#include "wgtr.h" + +/*** Allocate space for a grid, section, and line using the custom allocation + *** system. Note that register is similar to creating a new heap-allocated + *** variable, then binding it to a name. + ***/ +void +aposInit(void) { nmRegister(sizeof(AposGrid), "AposGrid"); nmRegister(sizeof(AposSection), "AposSection"); nmRegister(sizeof(AposLine), "AposLine"); - - return 0; } -int +/*** Dumps the grid content of a widget node and its floating children. This + *** function is most likely intended for debugging. + *** + *** @param tree The widget tree from which to extract the layout grid. + *** @param indent The number of 4-space indentations to indent the output. + *** Note: Included for the sake of recursion; just pass 0. + ***/ +void aposDumpGrid(pWgtrNode tree, int indent) { int i, childCnt, sectionCnt; pAposSection section; pWgtrNode child; + /*** Note: The "%*.*s" format specifier here takes two parameters: + *** - The first (indent*4) specifies the minimum width (number of spaces). + *** - The second (indent*4) limits the maximum number of characters to print. + *** Essentially, this adds (indent*4) spaces to the start of the line. + ***/ printf("%*.*s*** %s ***\n", indent*4, indent*4, "", tree->Name); if (tree->LayoutGrid) { + /** Dump the grid rows. **/ sectionCnt = xaCount(&AGRID(tree->LayoutGrid)->Rows); for(i=0;iStartLine->Loc, section->Width); printf("\n"); } + + /** Dump the grid columns. **/ sectionCnt = xaCount(&AGRID(tree->LayoutGrid)->Cols); for(i=0;iChildren); for(i=0;iName); - -return 0; } +/*** Automatically positions widgets in a widget tree. This function is + *** assumed to be called at-most once per subtree. + *** + *** @param tree The tree being auto-positioned. + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposAutoPositionWidgetTree(pWgtrNode tree) -{ -XArray PatchedWidgets; -int i=0, count=0; - - xaInit(&(PatchedWidgets),16); - - /** Preliminary adjustments before BuildGrid is done **/ - aposPrepareTree(tree, &PatchedWidgets); +{ +int rval = -1; - /** Build the layout grids **/ - if (aposBuildGrid(tree) < 0) + /** Recursively build the layout grids, including lines and sections, for this tree. **/ + if (UNLIKELY(aposBuildGrid(tree) < 0)) { - mssError(0, "APOS", "aposAutoPositionWidgetTree: Couldn't build layout grid for '%s'", tree->Name); - return -1; + mssError(0, "APOS", "Failed to build layout grid."); + goto end; } - /** Set flexibilities on containers **/ - if (aposSetFlexibilities(tree) < 0) + /** Set flexibilities on containers. **/ + if (UNLIKELY(aposSetFlexibilities(tree) < 0)) { - return -1; + mssError(0, "APOS", "Failed to set flexibilities."); + goto end; } - /*aposDumpGrid(tree, 0);*/ - - /** Honor minimum/maximum space requirements **/ - if (aposSetLimits(tree) < 0) + /** Detect and honor minimum/maximum space requirements. **/ + if (UNLIKELY(aposSetLimits(tree) < 0)) { - return -1; + mssError(0, "APOS", "Failed to set limits."); + goto end; } - - /*aposDumpGrid(tree, 0);*/ /**Iteration 2**/ - if(aposAutoPositionContainers(tree) < 0) + if (UNLIKELY(aposAutoPositionContainers(tree) < 0)) { - mssError(0, "APOS", "aposAutoPositionWidgetTree: Couldn't auto-position contents of '%s'", tree->Name); - return -1; + mssError(0, "APOS", "Failed to auto-position containers."); + goto end; } - - /*aposDumpGrid(tree, 0);*/ - + /** Release grid memory **/ aposFreeGrids(tree); /**makes a final pass through the tree and processes html windows**/ - aposProcessWindows(tree, tree); - - /**unpatches all of the heights that were specified in aposPrepareTree**/ - count=xaCount(&PatchedWidgets); - for(i=0; iheight = -1; - ((pWgtrNode)xaGetItem(&PatchedWidgets, i))->pre_height = -1; + mssError(0, "APOS", "Failed to process windows."); + goto end; } - - xaDeInit(&PatchedWidgets); - - return 0; -} + /** Success. **/ + rval = 0; + +end: + if (UNLIKELY(rval != 0)) + { + mssError(0, "APOS", "Failed to auto position widget tree '%s'", tree->Name); + } + + /*** Clean up: Release any grids aposBuildGrid() left behind. This is a + *** no-op on the success path, where the grids were already freed above. + ***/ + aposFreeGrids(tree); + + return rval; +} +/*** Recursively sets flexibility values for containers and their children. + *** + *** @param Parent The parent node who's flexibilities are being set. + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposSetFlexibilities(pWgtrNode Parent) { @@ -157,25 +248,25 @@ pWgtrNode Child; int i=0, childCount=xaCount(&(Parent->Children)); int sectCount; - /** Check recursion **/ - if (thExcessiveRecursion()) + /** Check recursion. **/ + if (UNLIKELY(thExcessiveRecursion())) { - mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); + mssError(1, "APOS", "Failed to layout application: resource exhaustion occurred."); return -1; } - + /** Recursively set the flexibilities of all children. **/ for(i=0; iChildren), i); - if (aposSetFlexibilities(Child) < 0) + if (UNLIKELY(aposSetFlexibilities(Child) < 0)) { return -1; } } - /** Reset flexibility values in the grid **/ + /** Reset flexibility values in the grid. **/ if (theGrid && childCount > 0 && !(Parent->Flags & WGTR_F_NONVISUAL)) { sectCount = xaCount(&(theGrid->Rows)); @@ -192,19 +283,20 @@ int sectCount; } } - /**set the flexibility of the given container, if it is visual**/ - if(!(Parent->Flags & WGTR_F_NONVISUAL)) - if(aposSetContainerFlex(Parent) < 0) - { - mssError(0, "APOS", "aposPrepareTree: Couldn't set %s's flexibility", Parent->Name); - return -1; - } + /** Set the flexibility of the given container, if it is visual. **/ + if (!(Parent->Flags & WGTR_F_NONVISUAL)) + aposSetContainerFlex(Parent); return 0; } - -/** this function is the recursive function that actually does the work **/ +/*** Adjusts space to accommodate children, somehow? I think? + *** + *** @param Parent The widget node parent who's limits are being calculated. + *** @param delta_w The change in width required for children (not set on error). + *** @param delta_h The change in height required for children (not set on error). + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposSetLimits_r(pWgtrNode Parent, int* delta_w, int* delta_h) { @@ -216,28 +308,32 @@ int sectionCount; pAposSection s; pWgtrNode Child; - /** Check recursion **/ - if (thExcessiveRecursion()) + /** Check recursion. **/ + if (UNLIKELY(thExcessiveRecursion())) { - mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); + mssError(1, "APOS", "Failed to layout application: resource exhaustion occurred."); return -1; } - /** Figure what is needed for children **/ + /** Calculate the total required space for children. **/ childCount = xaCount(&(Parent->Children)); total_child_delta_w = total_child_delta_h = 0; for(i=0;iChildren), i); - if (aposSetLimits_r(Child, &child_delta_w, &child_delta_h) < 0) + if (UNLIKELY(aposSetLimits_r(Child, &child_delta_w, &child_delta_h) < 0)) return -1; total_child_delta_w += child_delta_w; total_child_delta_h += child_delta_h; } - /** If space add/sub for children, use that, otherwise go with - ** space add/sub for this widget itself - **/ + /*** If space was added or subtracted for children, use that. Otherwise, + *** calculate and use the space added or subtracted by the parent widget + *** itself. + *** + *** It seems like this makes containers copy the total values of their + *** children, but noncontainers calculate their own values. + ***/ if (total_child_delta_w || total_child_delta_h) { *delta_w = total_child_delta_w; @@ -250,13 +346,15 @@ pWgtrNode Child; if (Parent->LayoutGrid) { sectionCount = xaCount(&(AGRID(Parent->LayoutGrid)->Rows)); - for(i=0;iLayoutGrid)->Rows), i)); + /*** If it has a desired width, increase the height + *** enough to give it that width. + ***/ if (s->DesiredWidth >= 0) { *delta_h += (s->DesiredWidth - s->Width); - /*printf("Changing height of row section at %d from %d to %d\n", s->StartLine->Loc, s->Width, s->DesiredWidth);*/ s->Width = s->DesiredWidth; } } @@ -264,17 +362,19 @@ pWgtrNode Child; for(i=0;iLayoutGrid)->Cols), i)); + /*** If it has a desired width, increase the width + *** enough to give it that width. + ***/ if (s->DesiredWidth >= 0) { *delta_w += (s->DesiredWidth - s->Width); - /*printf("Changing width of column section at %d from %d to %d\n", s->StartLine->Loc, s->Width, s->DesiredWidth);*/ s->Width = s->DesiredWidth; } } } } - /** Make space for this widget bigger **/ + /** If there is extra space, expand this widget to fill that space. **/ if (*delta_w) { if (Parent->StartVLine && ALINE(Parent->StartVLine)->SSection) @@ -283,7 +383,6 @@ pWgtrNode Child; } Parent->pre_width += *delta_w; - /*printf("Adjusting width of %s by %d\n", Parent->Name, *delta_w);*/ } if (*delta_h) { @@ -293,182 +392,78 @@ pWgtrNode Child; } Parent->pre_height += *delta_h; - /*printf("Adjusting height of %s by %d\n", Parent->Name, *delta_h);*/ } return 0; } - -/** This function simply call the recursive version **/ +/*** Adjusts space to accommodate children, somehow? I think? + *** + *** @param Parent The widget node parent who's limits are being calculated. + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposSetLimits(pWgtrNode Parent) { +/** Discard: The root does not need to report deltas. **/ int delta_w, delta_h; -int rval; - - delta_w = 0; - delta_h = 0; - - rval = aposSetLimits_r(Parent, &delta_w, &delta_h); - - return rval; -} - - -int -aposPrepareTree(pWgtrNode Parent, pXArray PatchedWidgets) -{ -pWgtrNode Child; -int i=0, childCount=xaCount(&(Parent->Children)); - - /** Check recursion **/ - if (thExcessiveRecursion()) - { - mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); - return -1; - } - - for(i=0; iChildren), i); - - /**if a visual child has an unspecified height, patch it, unless it is a scrollpane**/ - if((Child->height < 0) && !(Child->Flags & WGTR_F_NONVISUAL) && - strcmp(Parent->Type, "widget/scrollpane")) - aposPatchNegativeHeight(Child, PatchedWidgets); - - /** If child is a container but not a window, recursively prepare it as well **/ - if((Child->Flags & WGTR_F_CONTAINER) && !(Child->Flags & WGTR_F_FLOATING)) - if (aposPrepareTree(Child, PatchedWidgets) < 0) - return -1; - } - - return 0; -} -int -aposPatchNegativeHeight(pWgtrNode Widget, pXArray PatchedWidgets) -{ -ObjData val; - - /** set unspecified height of widget to an educated guess**/ - if(!strcmp(Widget->Type, "widget/editbox")) - { - Widget->height = 16; - } - else if(!strcmp(Widget->Type, "widget/textbutton")) - { - wgtrGetPropertyValue(Widget, "text", DATA_T_STRING, &val); - Widget->height = 13.0 * (0.5 + (float)(strlen(val.String) * 7) / (float)(Widget->width)); - if(Widget->height < 20) Widget->height = 20; - } - else if(!strcmp(Widget->Type, "widget/treeview")) - { - Widget->height = 100; - } - else if(!strcmp(Widget->Type, "widget/html")) - { - Widget->height = Widget->width; - } - else if(!strcmp(Widget->Type, "widget/dropdown")) - { - /*if(xaCount(&(Widget->Children)) < 4) - Widget->height = 40+16*xaCount(&(Widget->Children)); - else Widget->height = 104;*/ - Widget->height = Widget->min_height; - } - else if(!strcmp(Widget->Type, "widget/table")) - { - Widget->height = 100; - } - else if(!strcmp(Widget->Type, "widget/menu")) - { - Widget->height = 20; - } - else - { - return 0; - } - - xaAddItem(PatchedWidgets, Widget); - Widget->pre_height = Widget->height; - - return 0; + return aposSetLimits_r(Parent, &delta_w, &delta_h); } -int +/*** Calculates and sets the flexibility for a container by taking weighted + *** averages in each direction. Skips values explicitly specified by the + *** designer to avoid clobbering their design choices. + *** + *** @param W The container to be set. + ***/ +void aposSetContainerFlex(pWgtrNode W) { pAposGrid theGrid = AGRID(W->LayoutGrid); pAposSection Sect; int i=0, sectCount=0, TotalWidth=0, ProductSum=0; - if (!theGrid) return 0; - - /**calculate average row flexibility, weighted by height **/ - sectCount = xaCount(&(theGrid->Rows)); - for(i=0; iRows), i); - TotalWidth += Sect->Width; - ProductSum += Sect->Flex * Sect->Width; - } - if(TotalWidth) W->fl_height = APOS_FUDGEFACTOR + (float)ProductSum / (float)TotalWidth; - - TotalWidth = ProductSum = 0; - - /**calculate average column flexibility, weighted by width **/ - sectCount = xaCount(&(theGrid->Cols)); - for(i=0; iCols), i); - TotalWidth += Sect->Width; - ProductSum += Sect->Flex * Sect->Width; - } - if(TotalWidth) W->fl_width = APOS_FUDGEFACTOR + (float)ProductSum / (float)TotalWidth; - - return 0; -} + if (theGrid == NULL) return; -int -aposSetOffsetBools(pWgtrNode W, int *isSP, int *isWin, int *isTopTab, int *isSideTab, int *tabWidth) -{ -ObjData val; - - /**set isSP to compensate for scrollpane scrollbars**/ - if(isSP) *isSP = (!strcmp(W->Type, "widget/scrollpane")); - - /**set isWin to compensate windows' titlebars, if any**/ - if(isWin && !strcmp(W->Type, "widget/childwindow")) - { - if(wgtrGetPropertyValue(W, "titlebar", DATA_T_STRING, &val) < 0) - *isWin = 1; //if property not found, assume it has a titlebar - else *isWin = !strcmp(val.String, "yes"); + /*** Calculate average row flexibility, weighted by height. + *** Note: Section height is called width here because rows + *** are one dimensional and the feild is reused. + ***/ + if (!(W->Flags & WGTR_F_FLHEIGHTSET)) + { + sectCount = xaCount(&(theGrid->Rows)); + for(i=0; iRows), i); + TotalWidth += Sect->Width; + ProductSum += Sect->Flex * Sect->Width; + } + if (TotalWidth) W->fl_height = APOS_FUDGEFACTOR + (float)ProductSum / (float)TotalWidth; } + + TotalWidth = ProductSum = 0; - /**isTopTab and isSideTab are used to compensate for tabs**/ - if(isTopTab && !strcmp(W->Type, "widget/tab")) - { - /**set isTopTab and isSideTab**/ - if(wgtrGetPropertyValue(W, "tab_location", DATA_T_STRING, &val) < 0) - *isTopTab = 1; //if property not found, assume top tab**/ - else - { - *isTopTab = (!strcmp(val.String, "top") || !strcmp(val.String, "bottom")); - *isSideTab = (!strcmp(val.String, "left") || (!strcmp(val.String, "right"))); - } - - /**set tabWidth**/ - if(wgtrGetPropertyValue(W, "tab_width", DATA_T_INTEGER, &val) < 0) - *tabWidth = 80; - else *tabWidth = val.Integer; + /** Calculate average column flexibility, weighted by width. **/ + if (!(W->Flags & WGTR_F_FLWIDTHSET)) + { + sectCount = xaCount(&(theGrid->Cols)); + for(i=0; iCols), i); + TotalWidth += Sect->Width; + ProductSum += Sect->Flex * Sect->Width; + } + if (TotalWidth) W->fl_width = APOS_FUDGEFACTOR + (float)ProductSum / (float)TotalWidth; } - - return 0; } - +/*** Builds the layout grid for recursively for this container and all of its + *** children, including the lines and sections required for positioning. + *** + *** @param Parent The parent node who's grid is being built. + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposBuildGrid(pWgtrNode Parent) { @@ -476,106 +471,101 @@ int childCount, i; pWgtrNode Child; pAposGrid theGrid = NULL; - /** Check recursion **/ - if (thExcessiveRecursion()) + /** Check recursion. **/ + if (UNLIKELY(thExcessiveRecursion())) { - mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); + mssError(1, "APOS", "Failed to layout application: resource exhaustion occurred."); return -1; } - /** Allocate a grid **/ + /** Allocate a grid. **/ if (Parent->Flags & WGTR_F_CONTAINER) { if (!(Parent->Flags & WGTR_F_NONVISUAL) || !Parent->Parent) { - theGrid = Parent->LayoutGrid = (pAposGrid)nmMalloc(sizeof(AposGrid)); - if (!Parent->LayoutGrid) goto error; - - /**initiallize the XArrays in the grid**/ - aposInitiallizeGrid(theGrid); + /** Allocate and initialize a new pAposGrid. **/ + theGrid = (pAposGrid)nmMalloc(sizeof(AposGrid)); + if (UNLIKELY(theGrid == NULL)) return -1; + if (UNLIKELY(aposInitializeGrid(theGrid) < 0)) + { + mssError(1, "APOS", "Failed to initialize grid."); + nmFree(theGrid, sizeof(AposGrid)); + return -1; + } + Parent->LayoutGrid = theGrid; - /**Add the lines to the grid**/ - if(aposAddLinesToGrid(Parent, &(theGrid->HLines), &(theGrid->VLines)) < 0) + /** Add lines for children to the grid. **/ + if (UNLIKELY(aposAddLinesToGrid(Parent, &(theGrid->HLines), &(theGrid->VLines)) < 0)) { - mssError(0, "APOS", "aposBuildGrid: Couldn't add lines to %s's grid", - Parent->Name); + mssError(0, "APOS", "Failed to add lines to %s's grid", Parent->Name); return -1; } - /**Add the sections to the grid**/ - if(aposAddSectionsToGrid(theGrid, + /** Add the sections to the grid. **/ + if (UNLIKELY(aposAddSectionsToGrid(theGrid, (Parent->height-Parent->pre_height), - (Parent->width-Parent->pre_width)) < 0) + (Parent->width-Parent->pre_width)) < 0)) { - mssError(0, "APOS", "aposBuildGrid: Couldn't add rows and columns to %s's grid", - Parent->Name); + mssError(0, "APOS", "Failed to add rows and columns to %s's grid", Parent->Name); return -1; } } - - /** Do it for all children of this widget **/ + + /** Recursively build this grid for all children of this widget. **/ childCount = xaCount(&(Parent->Children)); for(i=0; iChildren), i); /**auto-positions subsequent visual container**/ - if(!(Child->Flags & WGTR_F_FLOATING)) - if(aposBuildGrid(Child) < 0) + if (!(Child->Flags & WGTR_F_FLOATING)) + if (UNLIKELY(aposBuildGrid(Child) < 0)) { - /*mssError(0, "APOS", "aposBuildGrid: Couldn't build layout grid for '%s'", Child->Name);*/ + mssError(0, "APOS", "Failed to build layout grid for '%s'", Child->Name); return -1; } } } return 0; - -error: - return -1; } - +/*** Recursively auto-positions containers and their children based on their grids. + *** + *** @param Parent The parent node who's containers are being autopositioned. + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposAutoPositionContainers(pWgtrNode Parent) { pAposGrid theGrid; pWgtrNode Child; int i=0, childCount = xaCount(&(Parent->Children)); -// int rows_extra=0, cols_extra=0; /** Check recursion **/ - if (thExcessiveRecursion()) + if (UNLIKELY(thExcessiveRecursion())) { - mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); + mssError(1, "APOS", "Failed to layout application: resource exhaustion occurred."); return -1; } - /** Grid is pre-built by aposBuildGrid **/ + /** Grid is pre-built by aposBuildGrid() **/ theGrid = (pAposGrid)(Parent->LayoutGrid); /** Autoposition anything with a grid setup **/ - if (theGrid) + if (theGrid != NULL) { /**Adjust the spaces between lines to fit the grid to the container**/ if (!(Parent->Flags & WGTR_F_VSCROLLABLE)) - // rows_extra = - aposSpaceOutLines(&(theGrid->HLines), &(theGrid->Rows), (Parent->height - Parent->pre_height)); //rows + aposSpaceOutLines(&(theGrid->HLines), &(theGrid->Rows), (Parent->height - Parent->pre_height)); if (!(Parent->Flags & WGTR_F_HSCROLLABLE)) - // cols_extra = - aposSpaceOutLines(&(theGrid->VLines), &(theGrid->Cols), (Parent->width - Parent->pre_width)); //columns + aposSpaceOutLines(&(theGrid->VLines), &(theGrid->Cols), (Parent->width - Parent->pre_width)); /**modify the widgets' x,y,w, and h values to snap to their adjusted lines**/ if (!(Parent->Flags & WGTR_F_VSCROLLABLE)) - aposSnapWidgetsToGrid(&(theGrid->HLines), APOS_ROW); //rows + aposSnapWidgetsToGrid(&(theGrid->HLines), APOS_ROW, Parent->Root->ClientInfo); if (!(Parent->Flags & WGTR_F_HSCROLLABLE)) - aposSnapWidgetsToGrid(&(theGrid->VLines), APOS_COL); //columns - - /** did not resize? **/ - /*if (rows_extra < 0) - Parent->height += -rows_extra; - if (cols_extra < 0) - Parent->width += -cols_extra;*/ + aposSnapWidgetsToGrid(&(theGrid->VLines), APOS_COL, Parent->Root->ClientInfo); } /**recursive call to auto-position subsequent visual containers, except windows**/ @@ -584,9 +574,9 @@ int i=0, childCount = xaCount(&(Parent->Children)); Child = (pWgtrNode)xaGetItem(&(Parent->Children), i); /**looks under nonvisual containers for more visual containers**/ - if(aposAutoPositionContainers(Child) < 0) + if (UNLIKELY(aposAutoPositionContainers(Child) < 0)) { - mssError(0, "APOS", "aposAutoPositionContainers: Couldn't auto-position contents of '%s'", Child->Name); + mssError(0, "APOS", "Failed to auto-position contents of '%s'", Child->Name); return -1; } } @@ -594,14 +584,17 @@ int i=0, childCount = xaCount(&(Parent->Children)); return 0; } - -int +/*** Frees memory used by all grids in the widget tree. + *** + *** @param tree The tree containing the grids to be freed. + ***/ +void aposFreeGrids(pWgtrNode tree) { int childCount, i; pWgtrNode Child; - /**deallocate memory and deinit XArrays in the grid**/ + /** Recursively deallocate memory and deinit XArrays in the grid. **/ childCount = xaCount(&(tree->Children)); for(i=0;iLayoutGrid), sizeof(AposGrid)); tree->LayoutGrid = NULL; } - - return 0; } - +/*** Initialize an AposGrid with empty arrays. On failure, none of the grid + *** is initialized, and it should not be used. + *** + *** @param theGrid The grid to be initialized. + *** @returns 0 for success, or -1 if an error occurs. Does not call mssError(). + ***/ int -aposInitiallizeGrid(pAposGrid theGrid) +aposInitializeGrid(pAposGrid theGrid) { - xaInit(&(theGrid->Rows),16); - xaInit(&(theGrid->Cols),16); - xaInit(&(theGrid->HLines),16); - xaInit(&(theGrid->VLines),16); +pXArray rows = NULL, cols = NULL, h_lines = NULL, v_lines = NULL; +int rval = -1; - return 0; + if (UNLIKELY(xaInit(&(theGrid->Rows), 16) < 0)) goto end; + rows = &(theGrid->Rows); + + if (UNLIKELY(xaInit(&(theGrid->Cols), 16) < 0)) goto end; + cols = &(theGrid->Cols); + + if (UNLIKELY(xaInit(&(theGrid->HLines), 16) < 0)) goto end; + h_lines = &(theGrid->HLines); + + if (UNLIKELY(xaInit(&(theGrid->VLines), 16) < 0)) goto end; + v_lines = &(theGrid->VLines); + + /** Success. **/ + rval = 0; + +end: + /** Free data that was allocated successfully. **/ + if (UNLIKELY(rval != 0)) + { + if (v_lines != NULL) xaDeInit(v_lines); + if (h_lines != NULL) xaDeInit(h_lines); + if (cols != NULL) xaDeInit(cols); + if (rows != NULL) xaDeInit(rows); + } + + return rval; } +/*** Adds 4 border lines around the edges of the grid. Then recursively adds 4 + *** lines for each visual child. Searches nonvisual containers recursively for + *** qualifying grandchildren. Floating windows are ignored. + *** + *** Scrollpanes receive only 2 vertical lines (skipping their horizontal edges), + *** and if the parent node is a scrollpane, the horizontal border lines are also + *** skipped. + *** + *** The HLines and VLines arrays will be populated with the generated horizontal + *** and vertical lines respectively. The SWidgets, CWidgets, and EWidgets arrays + *** are also properly populated with the widgets that start, cross, and end each + *** line respectively. + *** + *** @param Parent The parent who's grid is being populated with lines. + *** @param HLines The array of horizontal lines, populated by this function. + *** @param VLines The array of vertical lines, populated by this function. + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposAddLinesToGrid(pWgtrNode Parent, pXArray HLines, pXArray VLines) { -int i=0, count=0, isWin=0, isSP=0, height_adj=0, width_adj=0; +int i=0, count=0, height_adj=0, width_adj=0; pAposLine CurrLine, PrevLine; -// pXArray FirstCross, LastCross; - - aposSetOffsetBools(Parent, &isSP, &isWin, NULL, NULL, NULL); /** Does this widget need more room than it was given? **/ if (Parent->pre_width < Parent->min_width && Parent->min_width != 0) @@ -645,93 +679,95 @@ pAposLine CurrLine, PrevLine; if (Parent->pre_height < Parent->min_height && Parent->min_height != 0) height_adj = Parent->min_height - Parent->pre_height; - /**Add the 2 horizontal border lines, unless parent is a scrollpane**/ - if(strcmp(Parent->Type, "widget/scrollpane")) + /*** Border lines enclose the parent's client area (its box minus its + *** reserved insets). Apos works in client area coordinates, so it + *** never applies the insets to a child's position; the parent's HTML + *** rendering driver does this when it writes the client area container + *** in its HTML. + ***/ + + /** Add the 2 horizontal border lines, unless parent is a scrollpane. **/ + if (!isScrollpane(Parent)) { - if(aposCreateLine(NULL, HLines, 0, 0, 1, 0, 0) < 0) + int minHeightLoc = 0, maxHeightLoc = Parent->pre_height - (Parent->inset_top + Parent->inset_bottom); + if (UNLIKELY(aposCreateLine(NULL, HLines, minHeightLoc, APOS_NOT_LINKED, APOS_IS_BORDER, 0, APOS_HORIZONTAL) < 0)) goto CreateLineError; - if(aposCreateLine(NULL, HLines, (Parent->pre_height-isWin*24), 0, 1, height_adj, 0) < 0) + if (UNLIKELY(aposCreateLine(NULL, HLines, maxHeightLoc, APOS_NOT_LINKED, APOS_IS_BORDER, height_adj, APOS_HORIZONTAL) < 0)) goto CreateLineError; } - /**Add the 2 vertical border lines**/ - if(aposCreateLine(NULL, VLines, 0, 0, 1, 0, 1) < 0) + + /** Add the 2 vertical border lines. **/ + int minWidthLoc = 0, maxWidthLoc = Parent->pre_width - (Parent->inset_left + Parent->inset_right); + if (UNLIKELY(aposCreateLine(NULL, VLines, minWidthLoc, APOS_NOT_LINKED, APOS_IS_BORDER, 0, APOS_VERTICAL) < 0)) goto CreateLineError; - if(aposCreateLine(NULL, VLines, (Parent->pre_width-isSP*18), 0, 1, width_adj, 1) < 0) + if (UNLIKELY(aposCreateLine(NULL, VLines, maxWidthLoc, APOS_NOT_LINKED, APOS_IS_BORDER, width_adj, APOS_VERTICAL) < 0)) goto CreateLineError; - if(aposAddLinesForChildren(Parent, HLines, VLines) < 0) + /** Recursively add the nonborder lines for all child nodes. **/ + if (UNLIKELY(aposAddLinesForChildren(Parent, HLines, VLines) < 0)) goto CreateLineError; - /**populate horizontal line cross XArrays**/ + /** Record the widgets that cross each horizontal line in its CWidgets XArray. **/ count = xaCount(HLines); - for(i=1; iSWidgets), &(CurrLine->EWidgets), &(CurrLine->CWidgets)); - aposFillInCWidget(&(PrevLine->CWidgets), &(CurrLine->EWidgets), &(CurrLine->CWidgets)); + if (UNLIKELY(aposFillInCWidget(&(PrevLine->SWidgets), &(CurrLine->EWidgets), &(CurrLine->CWidgets)) < 0) || + UNLIKELY(aposFillInCWidget(&(PrevLine->CWidgets), &(CurrLine->EWidgets), &(CurrLine->CWidgets)) < 0)) + goto error; } - /**populate vertical line cross XArrays**/ + /** Record the widgets that cross each vertical line in its CWidgets XArray. **/ count = xaCount(VLines); - for(i=1; iSWidgets), &(CurrLine->EWidgets), &(CurrLine->CWidgets)); - aposFillInCWidget(&(PrevLine->CWidgets), &(CurrLine->EWidgets), &(CurrLine->CWidgets)); - } - - /**sanity check to make sure no widgets cross the border lines**/ - if(xaCount(HLines)) //don't test borderlines unless they exist - { - // FirstCross = &(((pAposLine)xaGetItem(HLines, 0))->CWidgets); - // LastCross = &(((pAposLine)xaGetItem(HLines, (xaCount(HLines)-1)))->CWidgets); - /*if(xaCount(FirstCross)) - mssError(1, "APOS", "%d widget(s) crossed the top borderline, including %s '%s'", xaCount(FirstCross), - ((pWgtrNode)xaGetItem(FirstCross, 0))->Type, ((pWgtrNode)xaGetItem(FirstCross, 0))->Name); - if(xaCount(LastCross)) - mssError(1, "APOS", "%d widget(s) crossed the bottom borderline, including %s '%s'", xaCount(LastCross), - ((pWgtrNode)xaGetItem(LastCross, 0))->Type, ((pWgtrNode)xaGetItem(LastCross, 0))->Name);*/ + if (UNLIKELY(aposFillInCWidget(&(PrevLine->SWidgets), &(CurrLine->EWidgets), &(CurrLine->CWidgets)) < 0) || + UNLIKELY(aposFillInCWidget(&(PrevLine->CWidgets), &(CurrLine->EWidgets), &(CurrLine->CWidgets)) < 0)) + goto error; } - -// FirstCross = &(((pAposLine)xaGetItem(VLines, 0))->CWidgets); -// LastCross = &(((pAposLine)xaGetItem(VLines, (xaCount(VLines)-1)))->CWidgets); - /*if(xaCount(FirstCross)) - mssError(1, "APOS", "%d widget(s) crossed the left borderline, including %s '%s'", xaCount(FirstCross), - ((pWgtrNode)xaGetItem(FirstCross, 0))->Type, ((pWgtrNode)xaGetItem(FirstCross, 0))->Name); - if(xaCount(LastCross)) - mssError(1, "APOS", "%d widget(s) crossed the right borderline, including %s '%s'", xaCount(LastCross), - ((pWgtrNode)xaGetItem(LastCross, 0))->Type, ((pWgtrNode)xaGetItem(LastCross, 0))->Name);*/ return 0; - CreateLineError: - mssError(0, "APOS", "aposAddLinesToGrid: Couldn't create a border line"); - return -1; +CreateLineError: + mssError(0, "APOS", "Failed to add border line."); + +error: + mssError(0, "APOS", "Failed to add lines to grid."); + return -1; } +/*** Adds 4 lines for the edges of each visual child. Searches nonvisual + *** containers recursively for qualifying grandchildren. Floating windows + *** are ignored. Scrollpanes receive only 2 vertical lines (skipping their + *** horizontal edges). + *** + *** @param Parent The parent who's children are being given lines. + *** @param HLines The array to which horizontal lines should be added. + *** @param VLines The array to which vertical lines should be added. + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposAddLinesForChildren(pWgtrNode Parent, pXArray HLines, pXArray VLines) { int i=0, childCount=xaCount(&(Parent->Children)); -int isTopTab=0, isSideTab=0, tabWidth=0; int height_adj, width_adj; pWgtrNode C; - /** Check recursion **/ - if (thExcessiveRecursion()) + /** Check recursion. **/ + if (UNLIKELY(thExcessiveRecursion())) { - mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); + mssError(1, "APOS", "Failed to layout application: resource exhaustion occurred."); return -1; } - /**loop through the children and create 4 lines for each child's 4 edges**/ + /** Loop through the children and create 4 lines for each child's 4 edges. **/ for(i=0; iChildren), i); - aposSetOffsetBools(C, NULL, NULL, &isTopTab, &isSideTab, &tabWidth); - + /** Does this widget need more room than it was given? **/ height_adj = width_adj = 0; if (C->pre_width < C->min_width && C->min_width != 0) @@ -739,29 +775,44 @@ pWgtrNode C; if (C->pre_height < C->min_height && C->min_height != 0) height_adj = C->min_height - C->pre_height; - /** If C is a nonvisual container, add lines for - *** the grandchildren. Otherwise, if C is visual - *** and not a window, just add 4 lines for it **/ - if((C->Flags & WGTR_F_NONVISUAL) && (C->Flags & WGTR_F_CONTAINER)) + /** If the child (C) is a nonvisual container, recursively add lines for any grandchildren. **/ + if ((C->Flags & WGTR_F_NONVISUAL) && (C->Flags & WGTR_F_CONTAINER)) { - if (aposAddLinesForChildren(C, HLines, VLines) < 0) + if (UNLIKELY(aposAddLinesForChildren(C, HLines, VLines) < 0)) goto CreateLineError; } - else if(!(C->Flags & WGTR_F_NONVISUAL) && !(C->Flags & WGTR_F_FLOATING)) + /** Otherwise, if child (C) is visual and not a floating window, add 4 lines for it. **/ + else if (!(C->Flags & WGTR_F_NONVISUAL) && !(C->Flags & WGTR_F_FLOATING)) { - /**add horizontal lines, unless parent is a scrollpane**/ - if(strcmp(Parent->Type, "widget/scrollpane")) + /** Add horizontal lines, unless parent is a scrollpane. **/ + if (!isScrollpane(Parent)) { - if(aposCreateLine(C, HLines, (C->y), APOS_SWIDGETS, 0, 0, 0) < 0) + /*** Note: + *** From this code, we see that the start line is + *** always the minY, and the end of the line is + *** always the maxY. Thus, the top line is the + *** start line and the bottom line is the end line + *** because Y increases as we decend the page. + ***/ + int minY = (C->y), maxY = (C->y + C->height); + if (UNLIKELY(aposCreateLine(C, HLines, minY, APOS_SWIDGETS, APOS_NOT_BORDER, 0, APOS_HORIZONTAL) < 0)) goto CreateLineError; - if(aposCreateLine(C, HLines, (C->y + C->height + isTopTab*24), APOS_EWIDGETS, 0, height_adj, 0) < 0) + if (UNLIKELY(aposCreateLine(C, HLines, maxY, APOS_EWIDGETS, APOS_NOT_BORDER, height_adj, APOS_HORIZONTAL) < 0)) goto CreateLineError; } - - /**add vertical lines**/ - if(aposCreateLine(C, VLines, (C->x), APOS_SWIDGETS, 0, 0, 1) < 0) + + /*** Note: + *** From this code, we see that the start line is always + *** the minX, and the end of the line is always the maxX. + *** Thus, the left line is the start line and the right + *** line is the end line because X increases as we move + *** right along the page. + ***/ + /** Add vertical lines. **/ + int minX = (C->x), maxX = (C->x + C->width); + if (UNLIKELY(aposCreateLine(C, VLines, minX, APOS_SWIDGETS, APOS_NOT_BORDER, 0, APOS_VERTICAL) < 0)) goto CreateLineError; - if(aposCreateLine(C, VLines, (C->x + C->width + isSideTab*tabWidth), APOS_EWIDGETS, 0, width_adj, 1) < 0) + if (UNLIKELY(aposCreateLine(C, VLines, maxX, APOS_EWIDGETS, APOS_NOT_BORDER, width_adj, APOS_VERTICAL) < 0)) goto CreateLineError; } } @@ -769,37 +820,60 @@ pWgtrNode C; return 0; CreateLineError: - mssError(0, "APOS", "aposAddLinesForChildren: Couldn't create a new line"); + mssError(0, "APOS", "Failed to create a new line for children."); return -1; } +/*** Creates a new line in the grid or updates an existing line if it exists + *** in the same location. Remember that lines record the widgets that start + *** on them (SWidgets), end on them (EWidgets), and cross them (CWidgets). + *** + *** Note: This function all lines in the given array are oriented in the same + *** direction as the new line. At the time of this writing (June 2025), + *** all known calling functions upheld by maintaining an HLines and a + *** VLines array to store horizontal and vertical lines separately. + *** + *** @param Widget The widget which determined the location of this line, + *** which we add to the SWidgets or EWidgets array. + *** @param Lines The array that stores the lines. + *** @param Loc The location of the line. Only a single coordinate in + *** one dimension is needed since lines span the entire grid. + *** @param type The type, indicating whether the associated widget starts + *** or ends on this line. + *** @param isBorder A boolean that is true if this is a grid border line. + *** @param adj An adjustment added to or subtracted from the line to + *** satisfy min or max constraints (respectively). + *** @param is_vert A boolean that is true if this line is vertical. + *** See APOS_VERTICAL and APOS_HORIZONTAL. + *** + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposCreateLine(pWgtrNode Widget, pXArray Lines, int Loc, int type, int isBorder, int adj, int is_vert) { pAposLine Line = aposExistingLine(Lines, Loc); - /**if there is already a line, just upgrade it**/ - if(Line != NULL) + /** If there is already a line, we upgrade it instead of creating a new one. **/ + if (Line != NULL) { - - /** Change the line position Adjustment value if needed **/ - if (Line->Adj != 0 && adj > Line->Adj) - Line->Adj = adj; - else if (Line->Adj == 0 && adj) + /*** Change the line position adjustment value, if needed. An Adj + *** of 0 means no requested adjustment, so it is always replaced. + *** Otherwise the largest request wins, which favors growing the + *** line (positive adj) over shrinking it (negative). + ***/ + if (Line->Adj == 0 || adj > Line->Adj) Line->Adj = adj; - } else { - - /**otherwise, create and add the new line**/ - if((Line = (pAposLine)nmMalloc(sizeof(AposLine))) < 0) + /** There's not already a line, so we allocate a new one. **/ + if (UNLIKELY((Line = (pAposLine)nmMalloc(sizeof(AposLine))) < 0)) { - mssError(1, "APOS", "aposCreateLine: Couldn't allocate memory for new grid line"); + mssError(1, "APOS", "Failed to allocate memory for new grid line."); return -1; } - /**initiallize new line**/ + /** Initialize the new line. **/ memset(Line, 0, sizeof(AposLine)); xaInit(&(Line->SWidgets),16); xaInit(&(Line->EWidgets),16); @@ -807,14 +881,12 @@ pAposLine Line = aposExistingLine(Lines, Loc); Line->Loc = Loc; Line->isBorder = isBorder; Line->Adj = adj; - Line->SSection = NULL; - Line->ESection = NULL; - /**add new line, sorted by location**/ + /** Add the new line, to the list of lines, sorted by location. **/ xaAddItemSortedInt32(Lines, Line, 0); } - /** Link the line and the widget together **/ + /** Link the line and the widget together. **/ if (type == APOS_SWIDGETS) { xaAddItem(&(Line->SWidgets), Widget); @@ -837,6 +909,16 @@ pAposLine Line = aposExistingLine(Lines, Loc); return 0; } +/*** Gets a line from the array at the location, or returns NULL if none exists. + *** + *** Note: This function all lines in the given array are oriented in the same + *** direction. This is not tested, although at the time of this writing + *** (June, 2025), all calling functions upheld this contract. + *** + *** @param Lines The array of lines to search. + *** @param Loc The location to check for a line. + *** @returns A pointer to the line, if it exists, and NULL otherwise. + ***/ pAposLine aposExistingLine(pXArray Lines, int Loc) { @@ -845,76 +927,126 @@ aposExistingLine(pXArray Lines, int Loc) int i, count = xaCount(Lines); for(i=0; iLoc == Loc) + if (((pAposLine)(xaGetItem(Lines, i)))->Loc == Loc) return (pAposLine)xaGetItem(Lines, i); return NULL; } +/*** Detects if a widget in PrevList (usually the widgets that started in or + *** crossed the previous line) ends on this line (aka. appears in EWidgets). + *** If it does not end on this line, we know it crosses this line, so we add + *** the widget to CWidgets. + *** + *** @param PrevList The list of previous widgets being checked. + *** @param EWidgets The list of widgets ending on the line in question. + *** @param CWidgets The list where we add widgets that cross this line. + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposFillInCWidget(pXArray PrevList, pXArray EWidgets, pXArray CWidgets) { pWgtrNode AddCandidate; int found=0, i=0, j=0, pCount=xaCount(PrevList), eCount=xaCount(EWidgets); - /** loop through the SWidgets or CWidgets array of the previous line**/ + /*** Loop through the array from the previous line. + *** Note: Could be that line's SWidgets OR CWidgets. + **/ for(i=0; iHLines)); for(i=1; iRows), ((pAposLine)xaGetItem(&(theGrid->HLines),(i-1))), - ((pAposLine)xaGetItem(&(theGrid->HLines),(i))), VDiff, APOS_ROW) < 0) + if (UNLIKELY(aposCreateSection(&(theGrid->Rows), ((pAposLine)xaGetItem(&(theGrid->HLines),(i-1))), + ((pAposLine)xaGetItem(&(theGrid->HLines),(i))), VDiff, APOS_ROW) < 0)) { - mssError(1, "APOS", "aposCreateSection(): Couldn't create a new row or column"); + mssError(0, "APOS", "Failed to create a new row."); return -1; } - /**Add columns**/ + /** Add column sections between vertical lines. **/ count = xaCount(&(theGrid->VLines)); for(i=1; iCols), ((pAposLine)xaGetItem(&(theGrid->VLines),(i-1))), - ((pAposLine)xaGetItem(&(theGrid->VLines),(i))), HDiff, APOS_COL) < 0) + if (UNLIKELY(aposCreateSection(&(theGrid->Cols), ((pAposLine)xaGetItem(&(theGrid->VLines),(i-1))), + ((pAposLine)xaGetItem(&(theGrid->VLines),(i))), HDiff, APOS_COL) < 0)) { - mssError(1, "APOS", "aposCreateSection(): Couldn't create a new row or column"); + mssError(0, "APOS", "Failed to create a new column."); return -1; } return 0; } +/*** Calculate and set the flexibility value for a section. Spacers have 0 flex + *** and containers use the flex of their least flexible children. + *** + *** @param sect The section being set. + *** @param type The type of section (either APOS_ROW or APOS_COL). + *** @returns 0 if successful or -1 if a default value should be used instead. + ***/ int aposSetSectionFlex(pAposSection sect, int type) { +/*** Note: + *** sCount + cCount includes all widgets intersecting this section because a + *** widget cannot begin inside a section. It always starts or ends at the edge + *** of a section. + ***/ int sCount = xaCount(&(sect->StartLine->SWidgets)); int cCount = xaCount(&(sect->StartLine->CWidgets)); - /** Set flex to 0 if the section is a spacer or contains non-flexible children, - *** otherwise set it to the average of the children. If none of those apply - *** it must be a wide, widgetless gap, assign a default flexibility **/ - if((sect->isSpacer) || (aposNonFlexChildren(sect->StartLine, type))) + /** Set flex to 0 if the section is a spacer or contains non-flexible + *** children, otherwise set it to the minimum of the children. If none + *** of those apply it must be a wide, widgetless gap. In this case, + *** return -1 to prompt the caller to determine a default flexibility. + ***/ + if ((sect->isSpacer) || (aposNonFlexChildren(sect->StartLine, type))) sect->Flex = 0; - else if(sCount || cCount) + else if (sCount || cCount) sect->Flex = aposMinimumChildFlex(sect->StartLine, type); else return -1; @@ -922,15 +1054,24 @@ int cCount = xaCount(&(sect->StartLine->CWidgets)); return 0; } +/*** Creates a new row or column section between two lines in the grid. + *** + *** @param Sections The array of sections, to which this section will be added. + *** @param StartL The line which starts this section (typically the top/left line). + *** @param EndL The line which ends this section (typically the bottom/right line). + *** @param Diff I had a hard time figuring out what this means. + *** @param type Whether the section is a row (APOS_ROW) or a column (APAS_COL). + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposCreateSection(pXArray Sections, pAposLine StartL, pAposLine EndL, int Diff, int type) { pAposSection NewSect; - /**Allocate and initiallize a new section**/ - if((NewSect = (pAposSection)(nmMalloc(sizeof(AposSection)))) < 0) + /** Allocate and initialize a new section. **/ + if (UNLIKELY((NewSect = (pAposSection)(nmMalloc(sizeof(AposSection)))) < 0)) { - mssError(1, "APOS", "nmMalloc(): Couldn't allocate memory for new row or column"); + mssError(1, "APOS", "Failed to allocate memory for new row or column."); return -1; } memset(NewSect, 0, sizeof(AposSection)); @@ -943,11 +1084,11 @@ pAposSection NewSect; StartL->SSection = NewSect; EndL->ESection = NewSect; - /** Need to adjust section width/height? **/ + /** Apply the adjustment from the end line, if needed. **/ if (EndL->Adj) NewSect->DesiredWidth = NewSect->Width + EndL->Adj; - /** Set section flexibility **/ + /** Set section flexibility. **/ if (aposSetSectionFlex(NewSect, type) < 0) { if (Diff < 0) @@ -956,118 +1097,127 @@ pAposSection NewSect; NewSect->Flex = APOS_EGAPFLEX; } - xaAddItem(Sections, NewSect); - + if (UNLIKELY(xaAddItem(Sections, NewSect) < 0)) + { + mssError(1, "APOS", "Failed to add new section."); + return -1; + } + return 0; } -int +/*** Determines if a section between two lines is a spacer. + *** + *** If a section is a spacer, the assumption is that the designer probably put + *** that space there to provide visual breathing room in their design. Thus, we + *** should avoid resizing it as this may interfere with their design. + *** + *** @param StartL The line starting the section. (I think this is always the left/top.) + *** @param EndL The line ending the section. (I think this is always the right/bottom.) + *** @param type Whether the section is a row (APOS_ROW) or a column (APOS_COL). + *** @param isBorder Whether the section is on the border of the page. + *** @returns true if the section is a spacer, or false if not. + ***/ +bool aposIsSpacer(pAposLine StartL, pAposLine EndL, int type, int isBorder) { -pWgtrNode SW, EW; -int i=0, j=0; -int sCount=xaCount(&(EndL->SWidgets)); -int eCount=xaCount(&(StartL->EWidgets)); - - if((EndL->Loc - StartL->Loc) <= APOS_MINSPACE) //if section is sufficiently narrow - { - /**gap between border and widget**/ - if(isBorder && (sCount || eCount)) return 1; - - /** Checks every widget ending on one side of the section against - *** every widget beginning on the other side to see if any of them - *** are directly across from each other **/ - for(i=0; iSWidgets), i)); - for(j=0; jEWidgets), j)); - - /** if a corner of a widget on one side of the - *** section falls between the two corners of a widget - *** on the other side, return 1 **/ - if((type == APOS_ROW) && (((EW->x >= SW->x) && (EW->x < (SW->x + SW->width))) || - (((EW->x + EW->width) > SW->x) && ((EW->x + EW->width) <= (SW->x + SW->width))))) - return 1; - - else if((type == APOS_COL) && (((EW->y >= SW->y) && (EW->y < (SW->y + SW->height))) || - (((EW->y + EW->height) > SW->y) && ((EW->y + EW->height) <= (SW->y + SW->height))))) - return 1; - } + /** Check for a section that's too wide to be a spacer. **/ + if ((EndL->Loc - StartL->Loc) > APOS_MINSPACE) return false; + + /*** Get the number of widgets at the start of the ending line + *** and at the end of the starting line. + ***/ + const int sCount = xaCount(&(EndL->SWidgets)); + const int eCount = xaCount(&(StartL->EWidgets)); + + /** Gaps between the border and any widget(s) are spacers. **/ + if (isBorder && (sCount > 0 || eCount > 0)) return true; + + /*** Checks every widget ending on one side to see if a widget + *** starts directly across from it on the other side. + ***/ + for (int i = 0; i < sCount; i++) + { + const pWgtrNode SW = (pWgtrNode)(xaGetItem(&(EndL->SWidgets), i)); + for (int j = 0; j < eCount; j++) + { + const pWgtrNode EW = (pWgtrNode)(xaGetItem(&(StartL->EWidgets), j)); + + /*** If the widgets on either side of the section overlap each + *** other along it, they are adjacent and this is a spacer. + *** The test has to run both ways round: either widget may be + *** the wider one, and either may contain the other outright. + ***/ + if ((type == APOS_ROW) + && (EW->x < (SW->x + SW->width)) + && (SW->x < (EW->x + EW->width))) + return true; + if ((type == APOS_COL) + && (EW->y < (SW->y + SW->height)) + && (SW->y < (EW->y + EW->height))) + return true; } - } - - return 0; + } + + return false; } -int +/*** Checks for any widgets starting on or crossing a line that are non-flexible + *** in the relevant dimension. + *** + *** @param L The line along which to check. + *** @param type Specifies the relevant dimension using APOS_ROW or APOS_COL. + *** @returns true if any child widget is non-flexible in the relevant dimension, + *** false otherwise. + ***/ +bool aposNonFlexChildren(pAposLine L, int type) { int i=0; int sCount = xaCount(&(L->SWidgets)); int cCount = xaCount(&(L->CWidgets)); - /** returns 1 if the widgets starting on or crossing the given - *** line have children that are completely non-flexible **/ - if(type == APOS_ROW) + /*** Return 1 if the widgets starting on or crossing the given line have + *** children that are completely non-flexible. + ***/ + if (type == APOS_ROW) { for(i=0; iSWidgets), i)))->fl_height == 0) - return 1; + if (((pWgtrNode)(xaGetItem(&(L->SWidgets), i)))->fl_height == 0) + return true; for(i=0; iCWidgets), i)))->fl_height == 0) - return 1; - } - else //type == APOS_COL - { - for(i=0; iSWidgets), i)))->fl_width == 0) - return 1; - for(i=0; iCWidgets), i)))->fl_width == 0) - return 1; - } - - return 0; -} - -int -aposAverageChildFlex(pAposLine L, int type) -{ -int TotalFlex=0, i=0; -int sCount = xaCount(&(L->SWidgets)); -int cCount = xaCount(&(L->CWidgets)); - - /** Sum the flexibilities of widgets within the section proceeding the line**/ - if(type == APOS_ROW) - { - for(i=0; iSWidgets), i))->fl_height; - for(i=0; iCWidgets), i))->fl_height; + if (((pWgtrNode)(xaGetItem(&(L->CWidgets), i)))->fl_height == 0) + return true; } - else //type == APOS_COL + else // type == APOS_COL { for(i=0; iSWidgets), i))->fl_width; + if (((pWgtrNode)(xaGetItem(&(L->SWidgets), i)))->fl_width == 0) + return true; for(i=0; iCWidgets), i))->fl_width; + if (((pWgtrNode)(xaGetItem(&(L->CWidgets), i)))->fl_width == 0) + return true; } - /**return average flexibility**/ - return (int)(APOS_FUDGEFACTOR + ((float)TotalFlex)/((float)sCount+(float)cCount)); + return false; } +/*** Calculates the minimum flexibility of widgets on a line. + *** + *** @param L The line along which to check. + *** @param type Specifies the relevant dimension using APOS_ROW or APOS_COL. + *** @returns The minimum flexibility of children on the line, or + *** APOS_NOCHILDFLEX if the line holds no widgets at all. + ***/ int aposMinimumChildFlex(pAposLine L, int type) { -int MinFlex=100, i=0, f; +int MinFlex=APOS_NOFLEXFOUND, i=0, f; int sCount = xaCount(&(L->SWidgets)); int cCount = xaCount(&(L->CWidgets)); - /** Find the min flex within the section proceeding the line**/ - if(type == APOS_ROW) + /** Find the min flexibility. **/ + if (type == APOS_ROW) { for(i=0; iCWidgets)); } } - /**return min flexibility**/ - return MinFlex; + /** Return the minimum flexibility. **/ + return (MinFlex == APOS_NOFLEXFOUND) ? APOS_NOCHILDFLEX : MinFlex; } +/*** Distributes extra/missing space among grid lines by section flexibility. + *** + *** @param Lines The array of lines in the relevant direction. + *** @param Sections The array of sections in the relevant direction. + *** @param Diff The current space difference requested. + *** @returns The remaining space difference after spacing out elements as + *** much as possible. Never fails or returns an error code. + ***/ int aposSpaceOutLines(pXArray Lines, pXArray Sections, int Diff) +{ + return aposSpaceOutLines_r(Lines, Sections, Diff, true); +} + +/*** Distributes extra/missing space among grid lines by section flexibility. + *** + *** Recursive calls don't store loc_fl and my_fl. They are to redistribute + *** space freed by clamping a section to APOS_MINSPACE, and would report + *** affected sections as rigid, even though they're only rigid at that size. + *** + *** @param Lines The array of lines in the relevant direction. + *** @param Sections The array of sections in the relevant direction. + *** @param Diff The current space difference requested. + *** @returns The remaining space difference after spacing out elements as + *** much as possible. Never fails or returns an error code. + ***/ +int +aposSpaceOutLines_r(pXArray Lines, pXArray Sections, int Diff, bool store_flex) { pAposLine CurrLine, PrevLine; pAposSection PrevSect, CurrSect; int TotalFlex=0, TotalFlexibleSpace=0, Adj=0, i=0, Extra=0, count=xaCount(Sections); -int FlexibleSections=0; float FlexWeight=0, SizeWeight=0; float TotalSum=0; - /**if there are no sections, don't bother going on**/ - if(!count) return Diff; + /** If there are no sections, we have nothing to space out. **/ + if (!count) return Diff; /**Sum the flexibilities of the sections**/ for(i=0; iFlex; - if(CurrSect->Flex) - { - FlexibleSections++; - TotalFlexibleSpace += CurrSect->Width; - } + if (CurrSect->Flex) + TotalFlexibleSpace += CurrSect->Width; } - /** if there is no flexibility for expansion or contraction return 0**/ - if(TotalFlex == 0) return Diff; - - /** sets each line's location equal to the previous line's location - *** plus the adjusted width of the preceding section **/ + /*** If there is no flex or flex space, we can't space anything out. + *** Return the original difference so this can be spaced out elsewhere. + ***/ + if (TotalFlex == 0 || TotalFlexibleSpace <= 0) return Diff; + + /** Sum the flex weights of all sections, weighted by their size. **/ count = xaCount(Lines); for(i=1; iFlex) / (float)(TotalFlex); - SizeWeight = 0; - if(FlexWeight > 0) - SizeWeight = (float)(PrevSect->Width) / (float)(TotalFlexibleSpace); + SizeWeight = (FlexWeight > 0) ? (float)(PrevSect->Width) / (float)(TotalFlexibleSpace) : 0; TotalSum += (FlexWeight * SizeWeight); } + /*** TotalSum normalizes the per-section weights so that they add up to 1. + *** Zero therefore means no section has both flexibility and size, and a + *** negative value means some section has a negative width. Neither can + *** be spaced out, so hand the difference back to the caller. + ***/ + if (TotalSum <= 0) return Diff; + + /** The initial borders do not adjust. **/ + if (store_flex) + { + pAposLine leftBorder = (pAposLine)xaGetItem(Lines, 0); + leftBorder->loc_fl = leftBorder->my_fl = 0.0f; + } + for(i=1; iFlex) / (float)(TotalFlex); - SizeWeight = 0; - - /** unless there's at least some flexibility, don't factor in size **/ - if(FlexWeight > 0) - SizeWeight = (float)(PrevSect->Width) / (float)(TotalFlexibleSpace); - - /**for expanding lines**/ - if(Diff > 0) + SizeWeight = (FlexWeight > 0) ? (float)(PrevSect->Width) / (float)(TotalFlexibleSpace) : 0; + + /*** Calculate the adjustment weight, and also save it so we can + *** replicate some of the following logic in the CSS we will + *** eventually send to the client. + ***/ + float fl = (float)(FlexWeight * SizeWeight) / TotalSum; + + /** Store the line adjustment weight for responsive CSS later. **/ + if (store_flex) + { + CurrLine->loc_fl = PrevLine->loc_fl + fl; + CurrLine->my_fl = fl; + } + + /** Expand lines. **/ + if (Diff > 0) { - /*Adj = APOS_FUDGEFACTOR + (float)(Diff) * ((float)(FlexWeight+SizeWeight)/2.0);*/ - Adj = APOS_FUDGEFACTOR + (float)(Diff) * ((float)(FlexWeight*SizeWeight)/TotalSum); - CurrLine->Loc = PrevLine->Loc + PrevSect->Width + Adj; + /** Calculate adjustment using the adjustment weight. **/ + Adj = (float)(Diff) * fl + APOS_FUDGEFACTOR; + + /** Apply the calculated adjustment. **/ PrevSect->Width += Adj; + CurrLine->Loc = PrevLine->Loc + PrevSect->Width; } - /**for contracting lines**/ - else if(Diff < 0) + /** Contract lines. **/ + else if (Diff < 0) { - /*Adj = (float)(Diff) * ((float)(FlexWeight+SizeWeight)/2.0) - APOS_FUDGEFACTOR;*/ - Adj = (float)(Diff) * ((float)(FlexWeight*SizeWeight)/TotalSum) - APOS_FUDGEFACTOR; - + /** Calculate adjustment using the adjustment weight. **/ + Adj = (float)(Diff) * fl - APOS_FUDGEFACTOR; + /** if the section width will be unacceptably *** narrow or negative after the adjustment **/ - if((Adj + PrevSect->Width) <= APOS_MINSPACE) + if ((Adj + PrevSect->Width) <= APOS_MINSPACE) { /** if its width was unacceptable to begin with, pass the *** entire adjustment on to the next recursive call **/ - if(PrevSect->Width <= APOS_MINSPACE) + if (PrevSect->Width <= APOS_MINSPACE) { Extra += Adj; CurrLine->Loc = PrevLine->Loc + PrevSect->Width; @@ -1191,24 +1387,35 @@ float TotalSum=0; } else { - CurrLine->Loc = PrevLine->Loc + PrevSect->Width + Adj; PrevSect->Width += Adj; + CurrLine->Loc = PrevLine->Loc + PrevSect->Width; } } } - /** Spread any homeless extra space out over the remaining flexible sections**/ - if(Extra < 0) - return aposSpaceOutLines(Lines, Sections, Extra); + /*** Spread any homeless extra space out over the remaining flexible + *** sections. This pass does not record flex weights; see above. + ***/ + if (Extra < 0) + return aposSpaceOutLines_r(Lines, Sections, Extra, false); return Extra; } -int -aposSnapWidgetsToGrid(pXArray Lines, int flag) +/*** Adjusts widget positions and sizes to snap them to grid lines. This + *** function should be called after updating grid lines to ensure that + *** widgets properly reflect the changes. + *** + *** @param Lines The lines being updated. + *** @param flag Either APOS_ROW or APOS_COL. + *** @param info Info about the page design, currently used to determine if + *** flexibility should be used. + ***/ +void +aposSnapWidgetsToGrid(pXArray Lines, int flag, pWgtrClientInfo info) { +const int is_design = info->IsDesign; int i=0, j=0, count=0, lineCount = xaCount(Lines); -int isTopTab=0, isSideTab=0, tabWidth=0; int newsize; pAposLine CurrLine; pWgtrNode Widget; @@ -1222,75 +1429,115 @@ pWgtrNode Widget; for(j=0; jSWidgets), j); - if(flag == APOS_ROW) Widget->y = CurrLine->Loc; - else Widget->x = CurrLine->Loc; + if (flag == APOS_ROW) + { + Widget->y = CurrLine->Loc; + Widget->fl_scale_y = (is_design) ? 0.0 : CurrLine->loc_fl; + } + else + { + Widget->x = CurrLine->Loc; + Widget->fl_scale_x = (is_design) ? 0.0 : CurrLine->loc_fl; + } } - /** Adjusts width or height of widgets ending on this line **/ + /** Adjusts width or height of widgets ending on this line. **/ count = xaCount(&(CurrLine->EWidgets)); for(j=0; jEWidgets), j); - aposSetOffsetBools(Widget, NULL, NULL, &isTopTab, &isSideTab, &tabWidth); - if(flag==APOS_ROW && Widget->fl_height) + if (flag==APOS_ROW && Widget->fl_height) { - newsize = CurrLine->Loc - Widget->y - isTopTab*24; + /** Calculate the new size, taking APOS_MINWIDTH into account. **/ + newsize = CurrLine->Loc - Widget->y; if (newsize < APOS_MINWIDTH && Widget->pre_height >= APOS_MINWIDTH) Widget->height = APOS_MINWIDTH; - else if(newsize >= APOS_MINWIDTH || newsize >= Widget->pre_height) + else if (newsize >= APOS_MINWIDTH || newsize >= Widget->pre_height) Widget->height = newsize; else - /*Widget->height = APOS_MINWIDTH;*/ Widget->height = Widget->pre_height; + + /*** The widget copies the adjustment weight of the + *** line, ignoring APOS_MINWIDTH. + ***/ + Widget->fl_scale_h += (is_design) ? 0.0 : CurrLine->my_fl; } - else if(flag==APOS_COL && Widget->fl_width) + else if (flag==APOS_COL && Widget->fl_width) { - newsize = CurrLine->Loc - Widget->x - isSideTab*tabWidth; + /** Calculate the new size, taking APOS_MINWIDTH into account. **/ + newsize = CurrLine->Loc - Widget->x; + + /** If the new size is now smaller than the minimum, clamp it. **/ if (newsize < APOS_MINWIDTH && Widget->pre_width >= APOS_MINWIDTH) Widget->width = APOS_MINWIDTH; + /** If the size is bigger than the minimum, or growing, that's fine. **/ else if (newsize >= APOS_MINWIDTH || newsize >= Widget->pre_width) Widget->width = newsize; + /** Otherwise, we can't update the size. **/ else - /*Widget->width = APOS_MINWIDTH;*/ Widget->width = Widget->pre_width; + + /*** The widget copies the adjustment weight of the + *** line, ignoring APOS_MINWIDTH. + ***/ + Widget->fl_scale_w += (is_design) ? 0.0 : CurrLine->my_fl; } } + + if (!is_design) + { + /** Adjusts width or height of widgets ending on this line. **/ + count = xaCount(&(CurrLine->CWidgets)); + for(j=0; jCWidgets), j); + if (flag==APOS_ROW && Widget->fl_height) + Widget->fl_scale_h += CurrLine->my_fl; + else if (flag==APOS_COL && Widget->fl_width) + Widget->fl_scale_w += CurrLine->my_fl; + } + } } - - return 0; } +/*** Processes floating windows and recursively positions visual and + *** nonvisual containers. + *** + *** @param VisualRef The last visual container up the inheritance tree. + *** @param Parent The widget being scanned for windows. + *** @returns 0 if successful, or -1 if an error occurs. + ***/ int aposProcessWindows(pWgtrNode VisualRef, pWgtrNode Parent) { -int i=0, isWin=0, isSP=0; +int i=0; int childCount=xaCount(&(Parent->Children)); pWgtrNode Child; int rw, rh, rpw, rph; -char* val; -int ival; /** Check recursion **/ - if (thExcessiveRecursion()) + if (UNLIKELY(thExcessiveRecursion())) { - mssError(1,"APOS","Could not layout application: resource exhaustion occurred"); + mssError(1, "APOS", "Failed to layout application: resource exhaustion occurred."); return -1; } - aposSetOffsetBools(Parent, &isSP, &isWin, NULL, NULL, NULL); - /**loop through children and process any windows**/ for(i=0; iChildren), i)); - if(Child->Flags & WGTR_F_FLOATING) + if (Child->Flags & WGTR_F_FLOATING) { /** Top level or local reference for container? **/ - if ((wgtrGetPropertyValue(Child, "toplevel", DATA_T_STRING, POD(&val)) == 0 && (!strcasecmp(val, "yes") || !strcasecmp(val, "true") || !strcasecmp(val, "1") || !strcmp(val, "on"))) || (wgtrGetPropertyValue(Child, "toplevel", DATA_T_INTEGER, POD(&ival)) == 0 && ival == 1)) + if (htrGetBoolean(Child, "toplevel", 0) == 1) { - rpw = rw = Parent->Root->ClientInfo->AppMaxWidth; - rph = rh = Parent->Root->ClientInfo->AppMaxHeight; + rw = Parent->Root->ClientInfo->AppMaxWidth; + rh = Parent->Root->ClientInfo->AppMaxHeight; + + /** Get the design widths and heights for the test below. **/ + rpw = (Parent->Root->pre_width > 0) ? Parent->Root->pre_width : rw; + rph = (Parent->Root->pre_height > 0) ? Parent->Root->pre_height : rh; } else { @@ -1300,53 +1547,66 @@ int ival; rph = VisualRef->pre_height; } - /** auto-detect centered floating objects **/ + /** Detect centered windows with design values. **/ if (abs(Child->pre_x - (rpw - (Child->pre_x + Child->pre_width))) < 10) { Child->x = (rw - Child->width)/2; if (Child->x < 0) Child->x = 0; + Child->Flags |= WGTR_F_CENTEREDX; } if (abs(Child->pre_y - (rph - (Child->pre_y + Child->pre_height))) < 10) { Child->y = (rh - Child->height)/2; if (Child->y < 0) Child->y = 0; + Child->Flags |= WGTR_F_CENTEREDY; } + + /*** Compute the container's client height and width, aka. + *** the space where child widgets are placed. + ***/ + const int container_width = rw - (Parent->inset_left + Parent->inset_right); + const int container_height = rh - (Parent->inset_top + Parent->inset_bottom); /**if it's larger than its container, shrink it and set flag**/ - if(Child->width > (rw - isSP*18)) + if (Child->width > container_width) { - Child->width = (rw - isSP*18); + Child->width = container_width; } - if(Child->height > (rh - isWin*24)) + if (Child->height > container_height) { - Child->height = (rh - isWin*24); + Child->height = container_height; } /**if the window changed width or height, process it like a widget tree**/ - aposAutoPositionWidgetTree(Child); + if (UNLIKELY(aposAutoPositionWidgetTree(Child) < 0)) + return -1; /**if it's outside the top left corner pull the whole window in**/ - if(Child->x < 0) Child->x = 0; - if(Child->y < 0) Child->y = 0; + if (Child->x < 0) Child->x = 0; + if (Child->y < 0) Child->y = 0; /**if it's outside the bottom right corner, pull the whole window in**/ - if((Child->x + Child->width) > (rw - isSP*18)) - Child->x = (rw - isSP*18) - Child->width; - if((Child->y + Child->height) > (rh - isWin*24)) - Child->y = (rh - isWin*24) - Child->height; + if ((Child->x + Child->width) > container_width) + Child->x = container_width - Child->width; + if ((Child->y + Child->height) > container_height) + Child->y = container_height - Child->height; } - /**recursive call on visual containers; new visual reference is passed**/ - if((Child->Flags & WGTR_F_CONTAINER) && !(Child->Flags & WGTR_F_NONVISUAL)) + /*** Recursive call on visual child containers with a new visual + *** reference. Skips floating children because the call to + *** aposAutoPositionWidgetTree() in the previous if statement + *** already processed those subtrees. + ***/ + else if ((Child->Flags & WGTR_F_CONTAINER) && !(Child->Flags & WGTR_F_NONVISUAL)) { - if (aposProcessWindows(Child, Child) < 0) + if (UNLIKELY(aposProcessWindows(Child, Child) < 0)) return -1; } /**recursive call on nonvisual containers; old visual reference is maintained**/ - if((Child->Flags & WGTR_F_CONTAINER) && (Child->Flags & WGTR_F_NONVISUAL)) + if ((Child->Flags & WGTR_F_CONTAINER) && (Child->Flags & WGTR_F_NONVISUAL)) { - if (aposProcessWindows(VisualRef, Child) < 0) + if (UNLIKELY(aposProcessWindows(VisualRef, Child) < 0)) return -1; } @@ -1355,7 +1615,11 @@ int ival; return 0; } -int +/*** Frees all memory used by a grid, including its lines and sections. + *** + *** @param theGrid The grid being freed. + ***/ +void aposFree(pAposGrid theGrid) { int count=0, i=0; @@ -1370,7 +1634,7 @@ pAposLine Line; count = xaCount(&(theGrid->Cols)); for(i=0; iCols), i)), sizeof(AposSection)); - + /** free horizontal lines **/ count = xaCount(&(theGrid->HLines)); for(i=0; iCWidgets)); nmFree((pAposLine)(xaGetItem(&(theGrid->HLines), i)), sizeof(AposLine)); } - + /** free vertical lines **/ count = xaCount(&(theGrid->VLines)); for(i=0; iCWidgets)); nmFree((pAposLine)(xaGetItem(&(theGrid->VLines), i)), sizeof(AposLine)); } - + /** DeInit XArrays **/ xaDeInit(&(theGrid->Rows)); xaDeInit(&(theGrid->Cols)); xaDeInit(&(theGrid->HLines)); xaDeInit(&(theGrid->VLines)); - - return 0; } diff --git a/centrallix/wgtr/wgtdrv_alerter.c b/centrallix/wgtr/wgtdrv_alerter.c index 284a2fb36..dd0d1b141 100644 --- a/centrallix/wgtr/wgtdrv_alerter.c +++ b/centrallix/wgtr/wgtdrv_alerter.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtalrtVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_autolayout.c b/centrallix/wgtr/wgtdrv_autolayout.c index 3d0f48356..126c07981 100644 --- a/centrallix/wgtr/wgtdrv_autolayout.c +++ b/centrallix/wgtr/wgtdrv_autolayout.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2007 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,13 @@ /* Description: */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/xarray.h" +#include "wgtr.h" int @@ -244,7 +243,7 @@ wgtalVerify(pWgtrVerifySession s) continue; } else - mssError(1, "WGTRAL", "Warning: overflow of end of hbox '%s'",al->Name); + fprintf(stderr, "Warning: overflow of end of hbox '%s'\n", al->Name); } child->x = child->pre_x = xo; child->pre_width = child->width = possible_width; @@ -281,7 +280,7 @@ wgtalVerify(pWgtrVerifySession s) continue; } else - mssError(1, "WGTRAL", "Warning: overflow of end of vbox '%s'",al->Name); + fprintf(stderr, "Warning: overflow of end of vbox '%s'\n", al->Name); } child->y = child->pre_y = yo; child->pre_height = child->height = possible_height; @@ -469,7 +468,7 @@ wgtalNew(pWgtrNode node) } else { - node->Flags |= WGTR_F_CONTAINER; + node->Flags |= WGTR_F_CONTAINER | WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 100; if(node->fl_height < 0) node->fl_height = 100; } diff --git a/centrallix/wgtr/wgtdrv_button.c b/centrallix/wgtr/wgtdrv_button.c index 49e6fdf4b..a1ecc78d4 100644 --- a/centrallix/wgtr/wgtdrv_button.c +++ b/centrallix/wgtr/wgtdrv_button.c @@ -1,11 +1,39 @@ -#include +/************************************************************************/ +/* Centrallix Application Server System */ +/* Centrallix Core */ +/* */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ +/* */ +/* This program 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 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program 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 this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ +/* 02111-1307 USA */ +/* */ +/* A copy of the GNU General Public License has been included in this */ +/* distribution in the file "COPYING". */ +/* */ +/* Module: wgtdrv_button.c */ +/* Author: D. Kasper */ +/* Creation: June 21, 2007 */ +/* Description: Widget driver for a 'generic' button widget to replace */ +/* the (now deprecated) imagebutton/textbutton widgets. */ +/************************************************************************/ + #include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" + +#include "cxlib/datatypes.h" #include "wgtr.h" -#include "iface.h" + /*** wgtbtnVerify - allows the driver to check elsewhere in the tree *** to make sure that the conditions it requires for proper functioning @@ -15,6 +43,37 @@ int wgtbtnVerify(pWgtrVerifySession s) { + pWgtrNode this = s->CurrWidget; + int min_height = s->ClientInfo->ParagraphHeight + 4; + int est_height; + int line_count = 1; + char* text; + + /** 'image_margin' is the deprecated textbutton spelling of 'spacing'. **/ + wgtrRenameProperty(this, "image_margin", "spacing"); + + /*** Only a button carrying text sizes itself to that text. An image-only + *** button is as big as its image, and forcing it up to a line of text + *** would stretch every icon-sized button on the page. + ***/ + if (wgtrGetPropertyType(this, "text") <= 0) return 0; + + if (this->min_height < min_height) this->min_height = min_height; + + /*** A button with no height sizes itself to its text in the browser, so + *** estimate how many lines that text wraps onto at the button's width. + ***/ + if (this->height < 0) + { + if (this->width > 0 && wgtrGetPropertyValue(this, "text", DATA_T_STRING, POD(&text)) == 0) + /** Guess line count, adding `this->width - 1` so the int division rounds up. **/ + line_count = (strlen(text) * s->ClientInfo->CharWidth + this->width - 1) / this->width; + + est_height = min_height + (line_count - 1) * s->ClientInfo->ParagraphHeight; + this->Flags |= WGTR_F_AUTOHEIGHT; + this->height = this->pre_height = (est_height > min_height) ? est_height : min_height; + } + return 0; } @@ -27,8 +86,19 @@ wgtbtnVerify(pWgtrVerifySession s) int wgtbtnNew(pWgtrNode node) { - if(node->fl_width < 0) node->fl_width = 0; - if(node->fl_height < 0) node->fl_height = 0; + /*** widget/textbutton has always defaulted to a flexible width, and + *** existing layouts depend on it. The other two names default rigid. + ***/ + if (strcmp(node->Type, "widget/textbutton") == 0) + { + if(node->fl_width < 0) node->fl_width = 5; + if(node->fl_height < 0) node->fl_height = 1; + } + else + { + if(node->fl_width < 0) node->fl_width = 0; + if(node->fl_height < 0) node->fl_height = 0; + } return wgtrImplementsInterface(node, "net/centrallix/button.ifc?cx__version=1.1"); @@ -43,5 +113,11 @@ wgtbtnInitialize() wgtrRegisterDriver(name, wgtbtnVerify, wgtbtnNew); wgtrAddType(name, "button"); + /*** The deprecated textbutton and imagebutton names remain part of the + *** language and use this driver. Their own drivers are gone. + ***/ + wgtrAddType(name, "textbutton"); + wgtrAddType(name, "imagebutton"); + return 0; } diff --git a/centrallix/wgtr/wgtdrv_calendar.c b/centrallix/wgtr/wgtdrv_calendar.c index 387fb91d4..071f6cd61 100644 --- a/centrallix/wgtr/wgtdrv_calendar.c +++ b/centrallix/wgtr/wgtdrv_calendar.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtcaVerify - allows the driver to check elsewhere in the tree @@ -58,6 +51,7 @@ wgtcaVerify(pWgtrVerifySession s) int wgtcaNew(pWgtrNode node) { + node->Flags |= WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 0; if(node->fl_height < 0) node->fl_height = 0; @@ -75,6 +69,3 @@ wgtcaInitialize() return 0; } - - - diff --git a/centrallix/wgtr/wgtdrv_chart.c b/centrallix/wgtr/wgtdrv_chart.c index bed4d1028..aa0860edf 100644 --- a/centrallix/wgtr/wgtdrv_chart.c +++ b/centrallix/wgtr/wgtdrv_chart.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2008 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,9 @@ /* Description: */ /************************************************************************/ +#include + +#include "wgtr.h" /*** wgtchtVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_checkbox.c b/centrallix/wgtr/wgtdrv_checkbox.c index 60c5ec408..7ade28ecd 100644 --- a/centrallix/wgtr/wgtdrv_checkbox.c +++ b/centrallix/wgtr/wgtdrv_checkbox.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtcbVerify - allows the driver to check elsewhere in the tree @@ -58,8 +51,13 @@ wgtcbVerify(pWgtrVerifySession s) int wgtcbNew(pWgtrNode node) { - if(node->r_width < 0) node->r_width = 12; - if(node->r_height < 0) node->r_height = 12; + /*** 13px, not 12px: the 12x12 gif has a 1px border on its top and left + *** but not its bottom and right, so a 13px box centers the glyph. + ***/ + if(node->r_width < 0) + node->r_width = node->pre_width = node->width = 13; + if(node->r_height < 0) + node->r_height = node->pre_height = node->height = 13; if(node->fl_width < 0) node->fl_width = 0; if(node->fl_height < 0) node->fl_height = 0; return 0; diff --git a/centrallix/wgtr/wgtdrv_clock.c b/centrallix/wgtr/wgtdrv_clock.c index 2230ba8d2..92193cad7 100644 --- a/centrallix/wgtr/wgtdrv_clock.c +++ b/centrallix/wgtr/wgtdrv_clock.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtclVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_component.c b/centrallix/wgtr/wgtdrv_component.c index 337cbd877..2375df45d 100644 --- a/centrallix/wgtr/wgtdrv_component.c +++ b/centrallix/wgtr/wgtdrv_component.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2006 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,10 @@ /* Description: */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "wgtr.h" /*** wgtcmpVerify - allows the driver to check elsewhere in the tree @@ -63,6 +59,7 @@ wgtcmpNew(pWgtrNode node) int v; //node->Flags |= WGTR_F_CONTAINER; + node->Flags |= WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 100; if(node->fl_height < 0) node->fl_height = 100; @@ -88,4 +85,3 @@ wgtcmpInitialize() return 0; } - diff --git a/centrallix/wgtr/wgtdrv_componentdecl.c b/centrallix/wgtr/wgtdrv_componentdecl.c index 74e4b3f60..52cfa98bb 100644 --- a/centrallix/wgtr/wgtdrv_componentdecl.c +++ b/centrallix/wgtr/wgtdrv_componentdecl.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,9 @@ /* Description: */ /************************************************************************/ +#include + +#include "wgtr.h" /*** wgtcmpdVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_connector.c b/centrallix/wgtr/wgtdrv_connector.c index 2d655bf51..068a54045 100644 --- a/centrallix/wgtr/wgtdrv_connector.c +++ b/centrallix/wgtr/wgtdrv_connector.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtconnVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_datetime.c b/centrallix/wgtr/wgtdrv_datetime.c index 03255fa70..de3d1c2cb 100644 --- a/centrallix/wgtr/wgtdrv_datetime.c +++ b/centrallix/wgtr/wgtdrv_datetime.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtdtVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_dropdown.c b/centrallix/wgtr/wgtdrv_dropdown.c index ebea6b185..332f4a201 100644 --- a/centrallix/wgtr/wgtdrv_dropdown.c +++ b/centrallix/wgtr/wgtdrv_dropdown.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtddVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_editbox.c b/centrallix/wgtr/wgtdrv_editbox.c index 741258c93..1f97a62f9 100644 --- a/centrallix/wgtr/wgtdrv_editbox.c +++ b/centrallix/wgtr/wgtdrv_editbox.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtebVerify - allows the driver to check elsewhere in the tree @@ -51,6 +44,11 @@ wgtebVerify(pWgtrVerifySession s) if (this->min_height < min_height) this->min_height = min_height; + /*** If no height is given, estimate the height for one line of text + *** (since htdrv_editbox.c requires one). + ***/ + if (this->height < 0) this->height = this->pre_height = min_height; + return 0; } @@ -63,6 +61,7 @@ wgtebVerify(pWgtrVerifySession s) int wgtebNew(pWgtrNode node) { + node->Flags |= WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 40; if(node->fl_height < 0) node->fl_height = 1; diff --git a/centrallix/wgtr/wgtdrv_execmethod.c b/centrallix/wgtr/wgtdrv_execmethod.c index d3300b08c..0e6a806aa 100644 --- a/centrallix/wgtr/wgtdrv_execmethod.c +++ b/centrallix/wgtr/wgtdrv_execmethod.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtexVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_fileupload.c b/centrallix/wgtr/wgtdrv_fileupload.c index ae87e2b2d..1692220cc 100755 --- a/centrallix/wgtr/wgtdrv_fileupload.c +++ b/centrallix/wgtr/wgtdrv_fileupload.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtfuVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_form.c b/centrallix/wgtr/wgtdrv_form.c index 30a89c788..aef795e78 100644 --- a/centrallix/wgtr/wgtdrv_form.c +++ b/centrallix/wgtr/wgtdrv_form.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtformVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_formbar.c b/centrallix/wgtr/wgtdrv_formbar.c deleted file mode 100644 index 8ec78b3df..000000000 --- a/centrallix/wgtr/wgtdrv_formbar.c +++ /dev/null @@ -1,311 +0,0 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "cxlib/strtcpy.h" -#include "wgtr.h" -#include "expression.h" - -/************************************************************************/ -/* Centrallix Application Server System */ -/* Centrallix Core */ -/* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ -/* */ -/* This program 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 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program 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 this program; if not, write to the Free Software */ -/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ -/* 02111-1307 USA */ -/* */ -/* A copy of the GNU General Public License has been included in this */ -/* distribution in the file "COPYING". */ -/* */ -/* Module: wgtr/wgtdrv_formbar.c */ -/* Author: Matt McGill (MJM) */ -/* Creation: June 30, 2004 */ -/* Description: */ -/************************************************************************/ - -/** globals **/ - -static struct { - int fb_cnt; - int pane_cnt; - int btn_cnt; - int fs_cnt; -} WGTFB; - - -int -wgtfb_internal_AddStringProp(pWgtrNode node, char* name, char* str) - { - ObjData val; - - val.String = str; - return wgtrAddProperty(node, name, DATA_T_STRING, &val, 0); - } - - -/*** wgtfbVerify - allows the driver to check elsewhere in the tree - *** to make sure that the conditions it requires for proper functioning - *** are present - checking for other widgets that might be necessary, - *** checking interface versions on widgets to be interacted with, etc. - ***/ -int wgtfbVerify(pWgtrVerifySession s) - { - pWgtrNode this; - pWgtrNode subtree; - pWgtrNode newnode, newcn; - ObjData val; - char name[32], form_name[64], exp_txt[256]; - int x, y, width, height; - pObjSession os; - - /** Find our node **/ - this = s->CurrWidget; - os = this->ObjSession; - - /** Grab our properties **/ - if (wgtrGetPropertyValue(this,"x",DATA_T_INTEGER,POD(&x)) != 0) x=0; - if (wgtrGetPropertyValue(this,"y",DATA_T_INTEGER,POD(&y)) != 0) y=0; - if (wgtrGetPropertyValue(this,"target",DATA_T_STRING, &val) != 0) - { - mssError(1, "WGTFB", "Form bar '%s' must have a 'target' property", this->Name); - return -1; - } - strtcpy(form_name, val.String, sizeof(form_name)); - - /* - if (wgtrGetPropertyValue(this,"width",DATA_T_INTEGER,POD(&x)) != 0) x=0; - if (wgtrGetPropertyValue(this,"height",DATA_T_INTEGER,POD(&y)) != 0) y=0; - */ - width = 240; - height = 30; - - /** First create the pane - that's the parent **/ - snprintf(name, 32, "fb%dpane%d", WGTFB.fb_cnt, WGTFB.pane_cnt++); - if ( (subtree = wgtrNewNode(name, "widget/pane", os, x, y, width, height, 100, 100, 100, 100)) == NULL) - { - mssError(0, "WGTFB", "Couldn't create pane for form bar '%s'", this->Name); - return -1; - } - val.String = "raised"; - wgtrAddProperty(subtree, "style", DATA_T_STRING, &val, 0); - val.String = "/sys/images/grey_gradient3.png"; - wgtrAddProperty(subtree, "background", DATA_T_STRING, &val, 0); - wgtrSetupNode(subtree); - wgtrAddChild(this, subtree); - wgtrScheduleVerify(s, subtree); - /** Next the formstatus widget **/ - snprintf(name, 32, "fb%dformstatus%d", WGTFB.fb_cnt, WGTFB.fs_cnt++); - if ( (newnode = wgtrNewNode(name, "widget/formstatus", os, 72, 4, -1, -1, 100, 100, 100, 100)) == NULL) - { - mssError(0, "WGTFB", "Couldn't create formstatus for form bar '%s'", this->Name); - return -1; - } - val.String = "largeflat"; - wgtrAddProperty(newnode, "style", DATA_T_STRING, &val, 0); - wgtrSetupNode(newnode); - wgtrAddChild(subtree, newnode); - wgtrScheduleVerify(s, newnode); - - /** Now the 'first' button **/ - snprintf(name, 32, "fb%dfirst", WGTFB.fb_cnt); - if ( (newnode = wgtrNewNode(name, "widget/imagebutton", os, 8, 5, 18, 18, 100, 100, 100, 100)) == NULL) - { - mssError(0, "WGTFB", "Couldn't create imagebutton for form bar '%s'", this->Name); - return -1; - } - wgtfb_internal_AddStringProp(newnode, "image", "/sys/images/ico16aa.gif"); - wgtfb_internal_AddStringProp(newnode, "pointimage", "/sys/images/ico16ab.gif"); - wgtfb_internal_AddStringProp(newnode, "clickimage", "/sys/images/ico16ac.gif"); - wgtfb_internal_AddStringProp(newnode, "disabledimage", "/sys/images/ico16ad.gif"); - - snprintf(exp_txt, 256, "runclient(:%s:recid > 1)", form_name); - if ( (val.Generic = expCompileExpression(exp_txt, NULL, 0, 0)) != NULL) - { - wgtrAddProperty(newnode, "enabled", DATA_T_CODE, &val, 0); - expFreeExpression(val.Generic); - } - - wgtrSetupNode(newnode); - wgtrAddChild(subtree, newnode); - wgtrScheduleVerify(s, newnode); - - /** and the connector for the 'first' button **/ - snprintf(name, 32, "fb%dfirstcn", WGTFB.fb_cnt); - if ( (newcn = wgtrNewNode(name, "widget/connector", os, 0, 0, 0, 0, 0, 0, 0, 0)) == NULL) - { - mssError(0, "WGTFB", "Couldn't create connector for form bar '%s'", this->Name); - return -1; - } - wgtfb_internal_AddStringProp(newcn, "event", "Click"); - wgtfb_internal_AddStringProp(newcn, "target", form_name); - wgtfb_internal_AddStringProp(newcn, "action", "First"); - wgtrSetupNode(newcn); - wgtrAddChild(newnode, newcn); - wgtrScheduleVerify(s, newcn); - - /** And the 'prev' button **/ - snprintf(name, 32, "fb%dprev", WGTFB.fb_cnt); - if ( (newnode = wgtrNewNode(name, "widget/imagebutton", os, 28, 5, 18, 18, 100, 100, 100, 100)) == NULL) - { - mssError(0, "WGTFB", "Couldn't create imagebutton for form bar '%s'", this->Name); - return -1; - } - wgtfb_internal_AddStringProp(newnode, "image", "/sys/images/ico16ba.gif"); - wgtfb_internal_AddStringProp(newnode, "pointimage", "/sys/images/ico16bb.gif"); - wgtfb_internal_AddStringProp(newnode, "clickimage", "/sys/images/ico16bc.gif"); - wgtfb_internal_AddStringProp(newnode, "disabledimage", "/sys/images/ico16bd.gif"); - - snprintf(exp_txt, 256, "runclient(:%s:recid > 1)", form_name); - if ( (val.Generic = expCompileExpression(exp_txt, NULL, 0, 0)) != NULL) - { - wgtrAddProperty(newnode, "enabled", DATA_T_CODE, &val, 0); - expFreeExpression(val.Generic); - } - - wgtrSetupNode(newnode); - wgtrAddChild(subtree, newnode); - wgtrScheduleVerify(s, newnode); - - /** and the connector for the 'prev' button **/ - snprintf(name, 32, "fb%dprevcn", WGTFB.fb_cnt); - if ( (newcn = wgtrNewNode(name, "widget/connector", os, 0, 0, 0, 0, 0, 0, 0, 0)) == NULL) - { - mssError(0, "WGTFB", "Couldn't create connector for form bar '%s'", this->Name); - return -1; - } - wgtfb_internal_AddStringProp(newcn, "event", "Click"); - wgtfb_internal_AddStringProp(newcn, "target", form_name); - wgtfb_internal_AddStringProp(newcn, "action", "Prev"); - wgtrSetupNode(newcn); - wgtrAddChild(newnode, newcn); - wgtrScheduleVerify(s, newcn); - - /** And the 'next' button **/ - snprintf(name, 32, "fb%dnext", WGTFB.fb_cnt); - if ( (newnode = wgtrNewNode(name, "widget/imagebutton", os, 190, 5, 18, 18, 100, 100, 100, 100)) == NULL) - { - mssError(0, "WGTFB", "Couldn't create imagebutton for form bar '%s'", this->Name); - return -1; - } - wgtfb_internal_AddStringProp(newnode, "image", "/sys/images/ico16ca.gif"); - wgtfb_internal_AddStringProp(newnode, "pointimage", "/sys/images/ico16cb.gif"); - wgtfb_internal_AddStringProp(newnode, "clickimage", "/sys/images/ico16cc.gif"); - wgtfb_internal_AddStringProp(newnode, "disabledimage", "/sys/images/ico16cd.gif"); - - snprintf(exp_txt, 256, "runclient(not (:%s:recid == :%s:lastrecid))", form_name, form_name); - if ( (val.Generic = expCompileExpression(exp_txt, NULL, 0, 0)) != NULL) - { - wgtrAddProperty(newnode, "enabled", DATA_T_CODE, &val, 0); - expFreeExpression(val.Generic); - } - - wgtrSetupNode(newnode); - wgtrAddChild(subtree, newnode); - wgtrScheduleVerify(s, newnode); - - /** and the connector for the 'next' button **/ - snprintf(name, 32, "fb%dnextcn", WGTFB.fb_cnt); - if ( (newcn = wgtrNewNode(name, "widget/connector", os, 0, 0, 0, 0, 0, 0, 0, 0)) == NULL) - { - mssError(0, "WGTFB", "Couldn't create connector for form bar '%s'", this->Name); - return -1; - } - wgtfb_internal_AddStringProp(newcn, "event", "Click"); - wgtfb_internal_AddStringProp(newcn, "target", form_name); - wgtfb_internal_AddStringProp(newcn, "action", "Next"); - wgtrSetupNode(newcn); - wgtrAddChild(newnode, newcn); - wgtrScheduleVerify(s, newcn); - - /** And the 'last' button **/ - snprintf(name, 32, "fb%dlast", WGTFB.fb_cnt); - if ( (newnode = wgtrNewNode(name, "widget/imagebutton", os, 210, 5, 18, 18, 100, 100, 100, 100)) == NULL) - { - mssError(0, "WGTFB", "Couldn't create imagebutton for form bar '%s'", this->Name); - return -1; - } - wgtfb_internal_AddStringProp(newnode, "image", "/sys/images/ico16da.gif"); - wgtfb_internal_AddStringProp(newnode, "pointimage", "/sys/images/ico16db.gif"); - wgtfb_internal_AddStringProp(newnode, "clickimage", "/sys/images/ico16dc.gif"); - wgtfb_internal_AddStringProp(newnode, "disabledimage", "/sys/images/ico16dd.gif"); - - snprintf(exp_txt, 256, "runclient(not (:%s:recid == :%s:lastrecid))", form_name, form_name); - if ( (val.Generic = expCompileExpression(exp_txt, NULL, 0, 0)) != NULL) - { - wgtrAddProperty(newnode, "enabled", DATA_T_CODE, &val, 0); - expFreeExpression(val.Generic); - } - - wgtrSetupNode(newnode); - wgtrAddChild(subtree, newnode); - wgtrScheduleVerify(s, newnode); - - /** and the connector for the 'last' button **/ - snprintf(name, 32, "fb%dlastcn", WGTFB.fb_cnt); - if ( (newcn = wgtrNewNode(name, "widget/connector", os, 0, 0, 0, 0, 0, 0, 0, 0)) == NULL) - { - mssError(0, "WGTFB", "Couldn't create connector for form bar '%s'", this->Name); - return -1; - } - wgtfb_internal_AddStringProp(newcn, "event", "Click"); - wgtfb_internal_AddStringProp(newcn, "target", form_name); - wgtfb_internal_AddStringProp(newcn, "action", "Last"); - wgtrSetupNode(newcn); - wgtrAddChild(newnode, newcn); - wgtrScheduleVerify(s, newcn); - - - WGTFB.fb_cnt++; - - return 0; - } - - - -/*** wgtfbNew - after a node has been filled out with initial values, - *** the driver uses this function to take care of any other initialization - *** that needs to be done on a per-node basis. By far the most important - *** is declaring interfaces. - ***/ -int -wgtfbNew(pWgtrNode node) - { - if(node->fl_width < 0) node->fl_width = 0; - if(node->fl_height < 0) node->fl_height = 0; - node->width = node->r_width = 240; - node->height = node->r_height = 30; - - return 0; - } - - -int -wgtfbInitialize() - { - char* name="Form Bar Driver"; - - wgtrRegisterDriver(name, wgtfbVerify, wgtfbNew); - wgtrAddType(name, "formbar"); - - WGTFB.fb_cnt = 0; - WGTFB.pane_cnt = 0; - WGTFB.btn_cnt = 0; - WGTFB.fs_cnt = 0; - - return 0; - } diff --git a/centrallix/wgtr/wgtdrv_formstatus.c b/centrallix/wgtr/wgtdrv_formstatus.c index 61b491a4f..099cef1b1 100644 --- a/centrallix/wgtr/wgtdrv_formstatus.c +++ b/centrallix/wgtr/wgtdrv_formstatus.c @@ -1,17 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "cxlib/datatypes.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -37,6 +28,11 @@ /* Description: */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "wgtr.h" /*** wgtfsVerify - allows the driver to check elsewhere in the tree @@ -71,9 +67,25 @@ wgtfsVerify(pWgtrVerifySession s) int wgtfsNew(pWgtrNode node) { - if(node->fl_width < 0) node->fl_width = 0; - if(node->fl_height < 0) node->fl_height = 0; - + /** Set widget to be inflexible. **/ + if (node->fl_width > 0) + { + fprintf(stderr, + "Warning: widget/formstatus does not flex. Dropping fl_width: %d.\n", + node->fl_width + ); + } + node->fl_width = 0; + + if (node->fl_height > 0) + { + fprintf(stderr, + "Warning: widget/formstatus does not flex. Dropping fl_height: %d.\n", + node->fl_height + ); + } + node->fl_height = 0; + return 0; } diff --git a/centrallix/wgtr/wgtdrv_frameset.c b/centrallix/wgtr/wgtdrv_frameset.c index ff6a706b3..8c88055da 100644 --- a/centrallix/wgtr/wgtdrv_frameset.c +++ b/centrallix/wgtr/wgtdrv_frameset.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtsetVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_hints.c b/centrallix/wgtr/wgtdrv_hints.c index 19f1e1b09..2c180e809 100644 --- a/centrallix/wgtr/wgtdrv_hints.c +++ b/centrallix/wgtr/wgtdrv_hints.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgthintVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_html.c b/centrallix/wgtr/wgtdrv_html.c index 135399b14..b26aa24cc 100644 --- a/centrallix/wgtr/wgtdrv_html.c +++ b/centrallix/wgtr/wgtdrv_html.c @@ -1,17 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "cxlib/datatypes.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -37,6 +28,10 @@ /* Description: */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "wgtr.h" /*** wgthtmlVerify - allows the driver to check elsewhere in the tree @@ -58,6 +53,9 @@ wgthtmlVerify(pWgtrVerifySession s) int Static; wgtrGetPropertyValue(s->CurrWidget, "mode", DATA_T_STRING, &val); Static = ((val.String == NULL) || !strcmp(val.String, "static")); + + /** Only a dynamic html widget wraps its children (in #htNpane). **/ + if (!Static) s->CurrWidget->Flags |= WGTR_F_VISUAL_CONTAINER; if(s->CurrWidget->fl_width < 0) { @@ -74,6 +72,15 @@ wgthtmlVerify(pWgtrVerifySession s) else s->CurrWidget->fl_height = 100; } + + /*** An html widget with no height grows to fit the loaded document. We + *** don't know what height that will be, so we guess that it's a square. + ***/ + if (s->CurrWidget->height < 0 && s->CurrWidget->width >= 0) + { + s->CurrWidget->Flags |= WGTR_F_AUTOHEIGHT; + s->CurrWidget->height = s->CurrWidget->pre_height = s->CurrWidget->width; + } return 0; } diff --git a/centrallix/wgtr/wgtdrv_image.c b/centrallix/wgtr/wgtdrv_image.c index c856fcac5..70cf0676e 100644 --- a/centrallix/wgtr/wgtdrv_image.c +++ b/centrallix/wgtr/wgtdrv_image.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtimgVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_imagebutton.c b/centrallix/wgtr/wgtdrv_imagebutton.c deleted file mode 100644 index c9bd07cf9..000000000 --- a/centrallix/wgtr/wgtdrv_imagebutton.c +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" -#include "iface.h" - -/************************************************************************/ -/* Centrallix Application Server System */ -/* Centrallix Core */ -/* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ -/* */ -/* This program 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 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program 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 this program; if not, write to the Free Software */ -/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ -/* 02111-1307 USA */ -/* */ -/* A copy of the GNU General Public License has been included in this */ -/* distribution in the file "COPYING". */ -/* */ -/* Module: wgtr/wgtdrv_imagebutton.c */ -/* Author: Matt McGill (MJM) */ -/* Creation: June 30, 2004 */ -/* Description: */ -/************************************************************************/ - - - -/*** wgtibtnVerify - allows the driver to check elsewhere in the tree - *** to make sure that the conditions it requires for proper functioning - *** are present - checking for other widgets that might be necessary, - *** checking interface versions on widgets to be interacted with, etc. - ***/ -int -wgtibtnVerify(pWgtrVerifySession s) - { - return 0; - } - - -/*** wgtibtnNew - after a node has been filled out with initial values, - *** the driver uses this function to take care of any other initialization - *** that needs to be done on a per-node basis. By far the most important - *** is declaring interfaces. - ***/ -int -wgtibtnNew(pWgtrNode node) - { - if(node->fl_width < 0) node->fl_width = 0; - if(node->fl_height < 0) node->fl_height = 0; - - return wgtrImplementsInterface(node, "net/centrallix/button.ifc?cx__version=1.1"); - - } - - -int -wgtibtnInitialize() - { - char* name = "ImageButton Widget Driver"; - - wgtrRegisterDriver(name, wgtibtnVerify, wgtibtnNew); - wgtrAddType(name, "imagebutton"); - - return 0; - } diff --git a/centrallix/wgtr/wgtdrv_label.c b/centrallix/wgtr/wgtdrv_label.c index 40989733e..55ccfc06b 100644 --- a/centrallix/wgtr/wgtdrv_label.c +++ b/centrallix/wgtr/wgtdrv_label.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtlblVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_map.c b/centrallix/wgtr/wgtdrv_map.c index 2b3323173..4a65c2897 100755 --- a/centrallix/wgtr/wgtdrv_map.c +++ b/centrallix/wgtr/wgtdrv_map.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 2012 LightSys Technology Services, Inc. */ +/* Copyright (C) 2012-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: Map widget using OpenLayers */ /************************************************************************/ +#include "wgtr.h" /*** wgtmapVerify - allows the driver to check elsewhere in the tree @@ -58,6 +51,7 @@ wgtmapVerify(pWgtrVerifySession s) int wgtmapNew(pWgtrNode node) { + node->Flags |= WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 10; if(node->fl_height < 0) node->fl_height = 10; @@ -75,4 +69,3 @@ wgtmapInitialize() return 0; } - diff --git a/centrallix/wgtr/wgtdrv_menu.c b/centrallix/wgtr/wgtdrv_menu.c index 4312ea556..18c114874 100644 --- a/centrallix/wgtr/wgtdrv_menu.c +++ b/centrallix/wgtr/wgtdrv_menu.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,92 @@ /* Description: */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/range.h" +#include "cxlib/xarray.h" +#include "wgtr.h" + + +/*** Estimated heights of the insets htdrv_menu.c draws around a menu's rows. + *** Each tracks specific markup there, so revisit these if it changes. + ***/ +#define MN_BORDER_H 2 /** #mnNmain 1px border, top plus bottom **/ +#define MN_OUTER_GAP_H 2 /** cellspacing="1" wrapper table, top plus bottom **/ +#define MN_ROW_GAP_H 2 /** cellspacing="2" row table, one gap **/ +#define MN_SEP_H 4 /** the line a widget/menusep draws **/ +#define MN_TRAILING_H 1 /** the trailing position tracking row **/ + + +/*** wgtmenu_internal_RowHeight() - estimate the height of the row that + *** htdrv_menu.c will draw for one child of a menu. Every row holds a + *** position tracking image of row_h, so none of them come out shorter. + *** + *** @param child The child widget to measure. + *** @param row_h Height of one menu row. + *** @returns The row height in px, or -1 if the child draws no row. + ***/ +int +wgtmenu_internal_RowHeight(pWgtrNode child, int row_h) + { + if (!strcmp(child->Type, "widget/menu") + || !strcmp(child->Type, "widget/menuitem") + || !strcmp(child->Type, "widget/menutitle")) + return row_h; + if (!strcmp(child->Type, "widget/menusep")) + return max(row_h, MN_SEP_H); + + return -1; + } + + +/*** wgtmenu_internal_EstimateRows() - sum and count the rows htdrv_menu.c will + *** draw for a vertical menu. Mirrors htmenuRender(), which looks through a + *** control structure (e.g. widget/repeat) exactly one level, and gives every + *** row it draws one call to htmenu_internal_AddDot(). + *** + *** @param menu The menu whose children are to be measured. + *** @param row_h Height of one menu row. + *** @param row_cnt Incremented once per row found. + *** @returns The summed height of the rows, in px. + ***/ +int +wgtmenu_internal_EstimateRows(pWgtrNode menu, int row_h, int* row_cnt) + { + pWgtrNode child; + pWgtrNode sub; + int i, j, cnt, subcnt; + int this_h; + int height = 0; + + cnt = xaCount(&(menu->Children)); + for (i = 0; i < cnt; i++) + { + child = xaGetItem(&(menu->Children), i); + if (child->Flags & WGTR_F_CONTROL) + { + /** Look through the control structure, but no deeper. **/ + subcnt = xaCount(&(child->Children)); + for (j = 0; j < subcnt; j++) + { + sub = xaGetItem(&(child->Children), j); + if ((this_h = wgtmenu_internal_RowHeight(sub, row_h)) < 0) + continue; + height += this_h; + (*row_cnt)++; + } + continue; + } + if ((this_h = wgtmenu_internal_RowHeight(child, row_h)) < 0) + continue; + height += this_h; + (*row_cnt)++; + } + + return height; + } /*** wgtmenuVerify - allows the driver to check elsewhere in the tree @@ -50,6 +128,8 @@ wgtmenuVerify(pWgtrVerifySession s) char* str; int i; int min_height = s->ClientInfo->ParagraphHeight + 4; + int para_h = s->ClientInfo->ParagraphHeight; + int row_h, row_cnt, est_height; if (menu->min_height < min_height) menu->min_height = min_height; @@ -75,6 +155,44 @@ wgtmenuVerify(pWgtrVerifySession s) else if (wgtrGetPropertyValue(menu, "popup", DATA_T_INTEGER, POD(&i)) == 0 && i) menu->Flags |= WGTR_F_FLOATING; + /** Only a menu draws rows; the other types keep the one-line estimate. **/ + est_height = min_height; + if (!strcmp(menu->Type, "widget/menu")) + { + /*** Estimate one row's height. A taller icon or checkbox overflows + *** it, since image sizes are unknown here. + ***/ + if (wgtrGetPropertyValue(menu, "row_height", DATA_T_INTEGER, POD(&row_h)) != 0) + row_h = 0; + row_h = max(row_h, para_h); + + /** A menu is never shorter than one line of text plus its insets. **/ + min_height = para_h + MN_ROW_GAP_H * 2 + MN_OUTER_GAP_H + MN_BORDER_H; + menu->min_height = max(menu->min_height, min_height); + + /*** A horizontal menu fits every item in the one row. A vertical + *** one gets a row per item, plus a trailing tracking row. + ***/ + if (wgtrGetPropertyValue(menu, "direction", DATA_T_STRING, POD(&str)) == 0 + && !strcmp(str, "vertical")) + { + row_cnt = 1; /** the trailing tracking row **/ + est_height = MN_TRAILING_H + + wgtmenu_internal_EstimateRows(menu, row_h, &row_cnt) + + MN_ROW_GAP_H * (row_cnt + 1) + + MN_OUTER_GAP_H + MN_BORDER_H; + } + else + est_height = row_h + MN_ROW_GAP_H * 2 + MN_OUTER_GAP_H + MN_BORDER_H; + } + + /** A menu with no height sizes itself in the browser; estimate it here. **/ + if (menu->height < 0) + { + menu->Flags |= WGTR_F_AUTOHEIGHT; + menu->height = menu->pre_height = max(est_height, min_height); + } + return 0; } @@ -91,6 +209,12 @@ wgtmenuNew(pWgtrNode node) if(node->fl_width < 0) node->fl_width = 25; if(node->fl_height < 0) node->fl_height = 1; + /*** Declare a 1px inset for the border that htdrv_menu.c draws around + *** the menu itself. The other types render no box of their own. + ***/ + if (!strcmp(node->Type, "widget/menu")) + wgtrSetInsets(node, 1, 1, 1, 1); + return 0; } diff --git a/centrallix/wgtr/wgtdrv_multiscroll.c b/centrallix/wgtr/wgtdrv_multiscroll.c index 387e882d7..55f441f7c 100644 --- a/centrallix/wgtr/wgtdrv_multiscroll.c +++ b/centrallix/wgtr/wgtdrv_multiscroll.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2007 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,9 @@ /* Description: */ /************************************************************************/ +#include + +#include "wgtr.h" /*** wgtmsVerify - allows the driver to check elsewhere in the tree @@ -62,7 +57,7 @@ wgtmsNew(pWgtrNode node) { pWgtrNode parent; - node->Flags |= WGTR_F_CONTAINER; + node->Flags |= WGTR_F_CONTAINER | WGTR_F_VISUAL_CONTAINER; if (node->fl_width < 0) node->fl_width = 100; if (!strcmp(node->Type, "widget/multiscrollpart")) diff --git a/centrallix/wgtr/wgtdrv_objcanvas.c b/centrallix/wgtr/wgtdrv_objcanvas.c index 28d2e3221..52e867717 100644 --- a/centrallix/wgtr/wgtdrv_objcanvas.c +++ b/centrallix/wgtr/wgtdrv_objcanvas.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 2004 LightSys Technology Services, Inc. */ +/* Copyright (C) 2004-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtocVerify - allows the driver to check elsewhere in the tree @@ -58,6 +51,7 @@ wgtocVerify(pWgtrVerifySession s) int wgtocNew(pWgtrNode node) { + node->Flags |= WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 10; if(node->fl_height < 0) node->fl_height = 10; @@ -75,4 +69,3 @@ wgtocInitialize() return 0; } - diff --git a/centrallix/wgtr/wgtdrv_osrc.c b/centrallix/wgtr/wgtdrv_osrc.c index d7c0f5d6f..f05adf6d8 100644 --- a/centrallix/wgtr/wgtdrv_osrc.c +++ b/centrallix/wgtr/wgtdrv_osrc.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,9 @@ /* Description: */ /************************************************************************/ +#include + +#include "wgtr.h" /*** wgtosrcVerify - allows the driver to check elsewhere in the tree @@ -75,7 +70,6 @@ wgtosrcInitialize() wgtrRegisterDriver(name, wgtosrcVerify, wgtosrcNew); wgtrAddType(name, "osrc"); - wgtrAddType(name, "osrc-rule"); return 0; } diff --git a/centrallix/wgtr/wgtdrv_page.c b/centrallix/wgtr/wgtdrv_page.c index 0f6533434..005731d31 100644 --- a/centrallix/wgtr/wgtdrv_page.c +++ b/centrallix/wgtr/wgtdrv_page.c @@ -1,17 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" -#include "apos.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -37,6 +28,8 @@ /* Description: */ /************************************************************************/ +#include "apos.h" +#include "wgtr.h" /*** wgtpageVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_pane.c b/centrallix/wgtr/wgtdrv_pane.c index 33f1b0bcd..fa2279bbf 100644 --- a/centrallix/wgtr/wgtdrv_pane.c +++ b/centrallix/wgtr/wgtdrv_pane.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,10 @@ /* Description: */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "wgtr.h" /*** wgtpnVerify - allows the driver to check elsewhere in the tree @@ -61,12 +57,20 @@ wgtpnVerify(pWgtrVerifySession s) int wgtpnNew(pWgtrNode node) { - node->Flags |= WGTR_F_CONTAINER; + int border_width = 1; + char* ptr; + + node->Flags |= WGTR_F_CONTAINER | WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 100; if(node->fl_height < 0) node->fl_height = 100; - node->top = node->bottom = node->left = node->right = 1; - + /*** Declare a 1px inset for the border that htdrv_pane.c draws with + *** every style except "flat". + ***/ + if (wgtrGetPropertyValue(node,"style",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"flat")) + border_width = 0; + wgtrSetInsets(node, border_width, border_width, border_width, border_width); + return 0; } diff --git a/centrallix/wgtr/wgtdrv_parameter.c b/centrallix/wgtr/wgtdrv_parameter.c index a9282b37a..b9483c3f7 100644 --- a/centrallix/wgtr/wgtdrv_parameter.c +++ b/centrallix/wgtr/wgtdrv_parameter.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2007 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtpaVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_radiobutton.c b/centrallix/wgtr/wgtdrv_radiobutton.c index 32532db88..615bc862a 100644 --- a/centrallix/wgtr/wgtdrv_radiobutton.c +++ b/centrallix/wgtr/wgtdrv_radiobutton.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,9 @@ /* Description: */ /************************************************************************/ +#include + +#include "wgtr.h" /*** wgtrbVerify - allows the driver to check elsewhere in the tree @@ -58,6 +53,8 @@ wgtrbVerify(pWgtrVerifySession s) int wgtrbNew(pWgtrNode node) { + if (!strcmp(node->Type, "widget/radiobuttonpanel")) + node->Flags |= WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 1; if(node->fl_height < 0) node->fl_height = 1; diff --git a/centrallix/wgtr/wgtdrv_repeat.c b/centrallix/wgtr/wgtdrv_repeat.c index 9945b23a0..777518219 100644 --- a/centrallix/wgtr/wgtdrv_repeat.c +++ b/centrallix/wgtr/wgtdrv_repeat.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -37,6 +29,7 @@ /* row that is retrieved in a given sql query. */ /************************************************************************/ +#include "wgtr.h" /*** wgtformVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_rule.c b/centrallix/wgtr/wgtdrv_rule.c index 03c2efc25..b4d107544 100644 --- a/centrallix/wgtr/wgtdrv_rule.c +++ b/centrallix/wgtr/wgtdrv_rule.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtruleVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_scrollbar.c b/centrallix/wgtr/wgtdrv_scrollbar.c index 59f98ae94..6b15559c4 100644 --- a/centrallix/wgtr/wgtdrv_scrollbar.c +++ b/centrallix/wgtr/wgtdrv_scrollbar.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,10 @@ /* Description: */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "wgtr.h" /*** wgtsbVerify - allows the driver to check elsewhere in the tree @@ -76,6 +72,7 @@ wgtsbVerify(pWgtrVerifySession s) int wgtsbNew(pWgtrNode node) { + node->Flags |= WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 100; if(node->fl_height < 0) node->fl_height = 100; diff --git a/centrallix/wgtr/wgtdrv_scrollpane.c b/centrallix/wgtr/wgtdrv_scrollpane.c index cc9f57abf..9ccd1f039 100644 --- a/centrallix/wgtr/wgtdrv_scrollpane.c +++ b/centrallix/wgtr/wgtdrv_scrollpane.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtspaneVerify - allows the driver to check elsewhere in the tree @@ -58,12 +51,13 @@ wgtspaneVerify(pWgtrVerifySession s) int wgtspaneNew(pWgtrNode node) { - node->Flags |= WGTR_F_CONTAINER; + node->Flags |= WGTR_F_CONTAINER | WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 100; if(node->fl_height < 0) node->fl_height = 100; - node->right=18; - + /** Declare an 18px right inset for the scrollbar. **/ + wgtrSetInsets(node, 0, 0, 0, 18); + return 0; } diff --git a/centrallix/wgtr/wgtdrv_spinner.c b/centrallix/wgtr/wgtdrv_spinner.c index f767438ce..f1b102191 100644 --- a/centrallix/wgtr/wgtdrv_spinner.c +++ b/centrallix/wgtr/wgtdrv_spinner.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtspnrVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_tab.c b/centrallix/wgtr/wgtdrv_tab.c index 81e571127..0daef2ea9 100644 --- a/centrallix/wgtr/wgtdrv_tab.c +++ b/centrallix/wgtr/wgtdrv_tab.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,13 +28,39 @@ /* Description: */ /************************************************************************/ +#include +#include + +#include "cxlib/datatypes.h" +#include "cxlib/mtsession.h" +#include "cxlib/xarray.h" +#include "wgtr.h" + + +/*** wgttab_internal_ClientSize - shrinks one of a tab control's geometry + *** values by the insets along that axis, yielding the size of its client + *** area. + ***/ +static int +wgttab_internal_ClientSize(int size, int inset) + { + /** A negative size means "unspecified", so pass it through. **/ + if (size < 0) return size; + + /** A tab smaller than its own insets has no room left for children. **/ + if (size < inset) return 0; + + return size - inset; + } int wgttab_internal_SetTabpageGeom(pWgtrNode tab, pWgtrNode container) { int i=0; - int count = xaCount(&(container->Children)); + const int count = xaCount(&(container->Children)); + const int inset_w = tab->inset_left + tab->inset_right; + const int inset_h = tab->inset_top + tab->inset_bottom; pWgtrNode child; for(i=0; iChildren), i)); if(!strcmp(child->Type, "widget/tabpage")) { - /** Set geometry of tabpage, based on the tab control itself **/ + /*** Set the geometry of the tabpage to fill the tab + *** control's client area, which is the area left + *** over inside the tab strip and the border. + ***/ child->r_x = child->r_y = 0; - child->r_width = tab->r_width; - child->r_height = tab->r_height; - child->pre_width = tab->pre_width; - child->pre_height = tab->pre_height; - child->min_width = tab->min_width; - child->min_height = tab->min_height; - + child->r_width = wgttab_internal_ClientSize(tab->r_width, inset_w); + child->r_height = wgttab_internal_ClientSize(tab->r_height, inset_h); + child->pre_width = wgttab_internal_ClientSize(tab->pre_width, inset_w); + child->pre_height = wgttab_internal_ClientSize(tab->pre_height, inset_h); + child->min_width = wgttab_internal_ClientSize(tab->min_width, inset_w); + child->min_height = wgttab_internal_ClientSize(tab->min_height, inset_h); + child->x = child->y = 0; - child->width = tab->width; - child->height = tab->height; + child->width = wgttab_internal_ClientSize(tab->width, inset_w); + child->height = wgttab_internal_ClientSize(tab->height, inset_h); } else if ((child->Flags & WGTR_F_NONVISUAL) && (child->Flags & WGTR_F_CONTAINER)) { @@ -107,13 +128,15 @@ wgttabNew(pWgtrNode node) { int tloc; char* ptr; - int tw; + int tw = 0, th; + int border_width = 1; + int strip_top = 0, strip_bottom = 0, strip_left = 0, strip_right = 0; - node->Flags |= WGTR_F_CONTAINER; + node->Flags |= WGTR_F_CONTAINER | WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 100; if(node->fl_height < 0) node->fl_height = 100; - - /** Set border widths if this is a tab control widget **/ + + /** Set insets if this is a tab control widget **/ if(!strcmp(node->Type, "widget/tab")) { if (wgtrGetPropertyValue(node, "tab_location", DATA_T_STRING, POD(&ptr)) == 0) @@ -143,24 +166,55 @@ wgttabNew(pWgtrNode node) } } - node->top = node->bottom = node->left = node->right = 1; + /*** How thick is the strip of tabs, and which edge is it on? An + *** invalid tab_height is reported by htdrv_tab.c so here we just + *** fall back to the 24px default value. + ***/ + if (wgtrGetPropertyValue(node, "tab_height", DATA_T_INTEGER, POD(&th)) != 0 || th <= 0) + th = 24; switch(tloc) { - case 0: /* top */ - node->top = 25; - break; - case 1: /* bottom */ - node->bottom = 25; - break; - case 2: /* left */ - node->left = tw; - break; - case 3: - node->right = tw; - break; - default: - break; + case 0: strip_top = th; break; /* top */ + case 1: strip_bottom = th; break; /* bottom */ + case 2: strip_left = tw; break; /* left */ + case 3: strip_right = tw; break; /* right */ + default: break; /* none */ } + + /*** BACKWARD COMPATIBILITY HACK: in the structure file language, a + *** tab control's 'height' and 'width' describe only its content + *** area, and the strip of tabs is drawn *outside* of that, so the + *** widget's real footprint is larger than its requested geometry. + *** That contradicts every other widget where x/y/width/height are + *** their requested size, but changing the language to fix this + *** breaks every existing design with tabs. Thus, we grow the + *** requested size by the thickness of the strip here to maintain + *** this incorrect behavior. + *** + *** Remove this hacky patch once the language is fixed to treat a + *** tab control's geometry as part of the tab widget. + ***/ + if (node->r_width >= 0) node->r_width += strip_left + strip_right; + if (node->width >= 0) node->width += strip_left + strip_right; + if (node->pre_width >= 0) node->pre_width += strip_left + strip_right; + if (node->r_height >= 0) node->r_height += strip_top + strip_bottom; + if (node->height >= 0) node->height += strip_top + strip_bottom; + if (node->pre_height >= 0) node->pre_height += strip_top + strip_bottom; + + /*** Declare the insets around the client area, which is the + *** #tcNctrl layer that holds the tab pages. Both the tab strip + *** and the 1px border that htdrv_tab.c draws around that layer + *** are inside this widget's outer box. + ***/ + if (wgtrGetPropertyValue(node, "border_style", DATA_T_STRING, POD(&ptr)) == 0 + && (strcmp(ptr, "none") == 0 || strcmp(ptr, "hidden") == 0)) + border_width = 0; + wgtrSetInsets(node, + strip_top + border_width, + strip_bottom + border_width, + strip_left + border_width, + strip_right + border_width + ); } return 0; diff --git a/centrallix/wgtr/wgtdrv_table.c b/centrallix/wgtr/wgtdrv_table.c index 257958696..f1111665b 100644 --- a/centrallix/wgtr/wgtdrv_table.c +++ b/centrallix/wgtr/wgtdrv_table.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2008 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,13 @@ /* Description: */ /************************************************************************/ +#include + +#include "wgtr.h" +#include "ht_render.h" + + +#define WGTTBL_DEFAULT_HEIGHT (100) /** height to give a table when none is specified, in px **/ /*** wgttblVerify - allows the driver to check elsewhere in the tree @@ -56,6 +55,10 @@ wgttblVerify(pWgtrVerifySession s) //tbl->Flags |= WGTR_F_FLOATING; } + /** Default an unspecified height, since htdrv_table.c requires one. **/ + if (strcmp(tbl->Type, "widget/table") == 0 && tbl->height < 0) + tbl->height = tbl->pre_height = WGTTBL_DEFAULT_HEIGHT; + return 0; } @@ -73,13 +76,17 @@ wgttblNew(pWgtrNode node) { if(node->fl_width < 0) node->fl_width = 100; if(node->fl_height < 0) node->fl_height = 100; - node->Flags |= (WGTR_F_CONTAINER | WGTR_F_VSCROLLABLE); + node->Flags |= (WGTR_F_CONTAINER | WGTR_F_VSCROLLABLE | WGTR_F_VISUAL_CONTAINER); + + /** Declare an 18px right inset for the scrollbar (see content_width in htdrv_table.c). **/ + if (!htrGetBoolean(node, "overlap_scrollbar", 0)) + wgtrSetInsets(node, 0, 0, 0, 18); } else if (!strcmp(node->Type, "widget/table-row-detail")) { if(node->fl_width < 0) node->fl_width = 100; if(node->fl_height < 0) node->fl_height = 0; - node->Flags |= WGTR_F_CONTAINER; + node->Flags |= WGTR_F_CONTAINER | WGTR_F_VISUAL_CONTAINER; } else if (!strcmp(node->Type, "widget/table-column")) { diff --git a/centrallix/wgtr/wgtdrv_template.c b/centrallix/wgtr/wgtdrv_template.c index 7cd99c7c8..2b0e4d5f9 100644 --- a/centrallix/wgtr/wgtdrv_template.c +++ b/centrallix/wgtr/wgtdrv_template.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: Widget template module */ /************************************************************************/ +#include "wgtr.h" /*** wgttplVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_templatefile.c b/centrallix/wgtr/wgtdrv_templatefile.c index c2fc5ecbf..f485348c4 100644 --- a/centrallix/wgtr/wgtdrv_templatefile.c +++ b/centrallix/wgtr/wgtdrv_templatefile.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtxxxVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_terminal.c b/centrallix/wgtr/wgtdrv_terminal.c index 6b48fabbd..82c226d60 100644 --- a/centrallix/wgtr/wgtdrv_terminal.c +++ b/centrallix/wgtr/wgtdrv_terminal.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgttermVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_textarea.c b/centrallix/wgtr/wgtdrv_textarea.c index 91b45a4d2..ac96eaee1 100644 --- a/centrallix/wgtr/wgtdrv_textarea.c +++ b/centrallix/wgtr/wgtdrv_textarea.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgttxVerify - allows the driver to check elsewhere in the tree @@ -58,6 +51,7 @@ wgttxVerify(pWgtrVerifySession s) int wgttxNew(pWgtrNode node) { + node->Flags |= WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 100; if(node->fl_height < 0) node->fl_height = 100; diff --git a/centrallix/wgtr/wgtdrv_textbutton.c b/centrallix/wgtr/wgtdrv_textbutton.c deleted file mode 100644 index 6c095827c..000000000 --- a/centrallix/wgtr/wgtdrv_textbutton.c +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - -/************************************************************************/ -/* Centrallix Application Server System */ -/* Centrallix Core */ -/* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ -/* */ -/* This program 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 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program 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 this program; if not, write to the Free Software */ -/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ -/* 02111-1307 USA */ -/* */ -/* A copy of the GNU General Public License has been included in this */ -/* distribution in the file "COPYING". */ -/* */ -/* Module: wgtr/wgtdrv_textbutton.c */ -/* Author: Matt McGill (MJM) */ -/* Creation: June 30, 2004 */ -/* Description: */ -/************************************************************************/ - - - -/*** wgttbtnVerify - allows the driver to check elsewhere in the tree - *** to make sure that the conditions it requires for proper functioning - *** are present - checking for other widgets that might be necessary, - *** checking interface versions on widgets to be interacted with, etc. - ***/ -int -wgttbtnVerify(pWgtrVerifySession s) - { - pWgtrNode this = s->CurrWidget; - int min_height = s->ClientInfo->ParagraphHeight + 4; - - if (this->min_height < min_height) this->min_height = min_height; - - return 0; - } - - -/*** wgttbtnNew - after a node has been filled out with initial values, - *** the driver uses this function to take care of any other initialization - *** that needs to be done on a per-node basis. By far the most important - *** is declaring interfaces. - ***/ -int -wgttbtnNew(pWgtrNode node) - { - if(node->fl_width < 0) node->fl_width = 5; - if(node->fl_height < 0) node->fl_height = 1; - - return 0; - } - - -int -wgttbtnInitialize() - { - char* name = "Text Button Widget Driver"; - - wgtrRegisterDriver(name, wgttbtnVerify, wgttbtnNew); - wgtrAddType(name, "textbutton"); - - return 0; - } diff --git a/centrallix/wgtr/wgtdrv_timer.c b/centrallix/wgtr/wgtdrv_timer.c index 973820466..a286e6860 100644 --- a/centrallix/wgtr/wgtdrv_timer.c +++ b/centrallix/wgtr/wgtdrv_timer.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgttmVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_treeview.c b/centrallix/wgtr/wgtdrv_treeview.c index 470b8b6bb..fe14dba64 100644 --- a/centrallix/wgtr/wgtdrv_treeview.c +++ b/centrallix/wgtr/wgtdrv_treeview.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,10 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" + + +#define WGTTREE_AUTO_HEIGHT (100) /** height to lay out a treeview with when none is given, in px **/ /*** wgttreeVerify - allows the driver to check elsewhere in the tree @@ -46,6 +42,17 @@ int wgttreeVerify(pWgtrVerifySession s) { + pWgtrNode this = s->CurrWidget; + + /*** A treeview with no height sizes itself to the nodes it loads, whose + *** count is not known until the user expands them, so estimate a height. + ***/ + if (this->height < 0) + { + this->Flags |= WGTR_F_AUTOHEIGHT; + this->height = this->pre_height = WGTTREE_AUTO_HEIGHT; + } + return 0; } diff --git a/centrallix/wgtr/wgtdrv_uawindow.c b/centrallix/wgtr/wgtdrv_uawindow.c index 69e8626e9..c9974f054 100644 --- a/centrallix/wgtr/wgtdrv_uawindow.c +++ b/centrallix/wgtr/wgtdrv_uawindow.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2010 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtuawinVerify - allows the driver to check elsewhere in the tree @@ -78,4 +71,3 @@ wgtuawinInitialize() return 0; } - diff --git a/centrallix/wgtr/wgtdrv_variable.c b/centrallix/wgtr/wgtdrv_variable.c index 5067f21a0..d3778d22e 100644 --- a/centrallix/wgtr/wgtdrv_variable.c +++ b/centrallix/wgtr/wgtdrv_variable.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,7 @@ /* Description: */ /************************************************************************/ +#include "wgtr.h" /*** wgtvblVerify - allows the driver to check elsewhere in the tree diff --git a/centrallix/wgtr/wgtdrv_window.c b/centrallix/wgtr/wgtdrv_window.c index 94cec0830..15d388642 100644 --- a/centrallix/wgtr/wgtdrv_window.c +++ b/centrallix/wgtr/wgtdrv_window.c @@ -1,16 +1,8 @@ -#include -#include -#include -#include "obj.h" -#include "cxlib/mtask.h" -#include "cxlib/mtsession.h" -#include "wgtr.h" - /************************************************************************/ /* Centrallix Application Server System */ /* Centrallix Core */ /* */ -/* Copyright (C) 1998-2001 LightSys Technology Services, Inc. */ +/* Copyright (C) 1998-2026 LightSys Technology Services, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -36,6 +28,11 @@ /* Description: */ /************************************************************************/ +#include + +#include "cxlib/datatypes.h" +#include "wgtr.h" +#include "ht_render.h" /*** wgtwinVerify - allows the driver to check elsewhere in the tree @@ -60,28 +57,52 @@ wgtwinVerify(pWgtrVerifySession s) int wgtwinNew(pWgtrNode node) { - int has_titlebar = 1, is_dialog_style = 0; + int has_titlebar = 1, is_dialog_style = 0, border_width = 1; + int title_bar_height, main_top_width, main_side_width; char* ptr; - node->Flags |= WGTR_F_CONTAINER | WGTR_F_FLOATING; + node->Flags |= WGTR_F_CONTAINER | WGTR_F_FLOATING | WGTR_F_VISUAL_CONTAINER; if(node->fl_width < 0) node->fl_width = 100; if(node->fl_height < 0) node->fl_height = 100; - /** No titlebar? **/ - if (wgtrGetPropertyValue(node,"titlebar",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"no")) - has_titlebar = 0; + /** No titlebar? **/ + has_titlebar = htrGetBoolean(node, "titlebar", 1); /** Dialog or node style? **/ if (wgtrGetPropertyValue(node,"style",DATA_T_STRING,POD(&ptr)) == 0 && !strcmp(ptr,"dialog")) is_dialog_style = 1; + /** A borderless window draws no outer edge. **/ + if (wgtrGetPropertyValue(node,"border_style",DATA_T_STRING,POD(&ptr)) == 0 + && (!strcmp(ptr,"none") || !strcmp(ptr,"hidden"))) + border_width = 0; + + /*** Declare the insets around our client area (#wnNmain) that holds + *** child widgets in the window. These values must match the geometry + *** that htdrv_window.c gives this layer. + *** + *** Both layers are border-box, so every edge is drawn inside the width + *** and height the window was given. The client area is therefore inset + *** by the window's own outer edge, by the titlebar above it, and by + *** whichever edges htdrv_window.c draws on the client layer itself. + ***/ + title_bar_height = (has_titlebar) ? 24 : 0; if (is_dialog_style) - node->left = node->right = node->top = node->bottom = 1; + { + main_side_width = 0; + main_top_width = (has_titlebar) ? 1 : 0; + } else - node->left = node->right = node->top = node->bottom = 2; - - if (has_titlebar) - node->top = (26 - node->top); + { + main_side_width = 1; + main_top_width = (has_titlebar) ? 0 : 1; + } + wgtrSetInsets(node, + border_width + title_bar_height + main_top_width, /* top */ + border_width + main_side_width, /* bottom */ + border_width + main_side_width, /* left */ + border_width + main_side_width /* right */ + ); return 0; } diff --git a/centrallix/wgtr/wgtr.c b/centrallix/wgtr/wgtr.c index 30bef5cd5..e03ffd0ab 100755 --- a/centrallix/wgtr/wgtr.c +++ b/centrallix/wgtr/wgtr.c @@ -31,23 +31,34 @@ /* See centrallix-sysdoc/WidgetTree.md for more information. */ /************************************************************************/ - +#include #include #include #include -#include "obj.h" -#include "wgtr.h" +#include + #include "apos.h" -#include "hints.h" -#include "param.h" -#include "endorsement_utils.h" -#include "cxlib/xarray.h" #include "cxlib/datatypes.h" +#include "cxlib/expect.h" #include "cxlib/magic.h" -#include "cxlib/xhash.h" -#include "cxlib/strtcpy.h" +#include "cxlib/mtask.h" #include "cxlib/mtsession.h" -#include "ht_render.h" +#include "cxlib/newmalloc.h" +#include "cxlib/range.h" +#include "cxlib/strtcpy.h" +#include "cxlib/xarray.h" +#include "cxlib/xhash.h" +#include "cxss/cxss.h" +#include "endorsement_utils.h" +#include "expression.h" +#include "hints.h" +#include "iface.h" +#include "obj.h" +#include "param.h" +#include "ptod.h" +#include "stparse_ne.h" +#include "wgtr.h" + #define WGTR_MAX_PARAMS (24) @@ -284,10 +295,16 @@ wgtrCopyInTemplate(pWgtrNode tree, pObject tree_obj, pWgtrNode match, char* base if (match->r_y >= 0) tree->r_y = match->r_y; if (match->r_width >= 0) tree->r_width = match->r_width; if (match->r_height >= 0) tree->r_height = match->r_height; - if (match->fl_x >= 0) tree->fl_x = match->fl_x; - if (match->fl_y >= 0) tree->fl_y = match->fl_y; - if (match->fl_width >= 0) tree->fl_width = match->fl_width; - if (match->fl_height >= 0) tree->fl_height = match->fl_height; + if (match->fl_width >= 0) + { + tree->fl_width = match->fl_width; + tree->Flags |= (match->Flags & WGTR_F_FLWIDTHSET); + } + if (match->fl_height >= 0) + { + tree->fl_height = match->fl_height; + tree->Flags |= (match->Flags & WGTR_F_FLHEIGHTSET); + } /** Check for substitutions **/ for(prop=objGetFirstAttr(tree_obj);prop;prop=objGetNextAttr(tree_obj)) @@ -357,7 +374,7 @@ wgtrCopyInTemplate(pWgtrNode tree, pObject tree_obj, pWgtrNode match, char* base } if ((new_node = wgtrNewNode(new_name, subtree->Type, subtree->ObjSession, subtree->r_x, subtree->r_y, subtree->r_width, subtree->r_height, - subtree->fl_x, subtree->fl_y, subtree->fl_width, subtree->fl_height)) == NULL) + subtree->fl_width, subtree->fl_height)) == NULL) return -1; if (wgtrSetupNode(new_node) < 0) @@ -576,7 +593,7 @@ wgtr_internal_LoadAttrs(pObject obj, char* name, char* type, pWgtrNode templates /*memcpy(my_templates, templates, sizeof(my_templates));*/ /** create this node **/ - if ( (this_node = wgtrNewNode(name, type, obj->Session, -1, -1, -1, -1, 100, 100, -1, -1)) == NULL) + if ( (this_node = wgtrNewNode(name, type, obj->Session, -1, -1, -1, -1, -1, -1)) == NULL) { mssError(0, "WGTR", "Couldn't create node %s", name); goto error; @@ -648,10 +665,16 @@ wgtr_internal_LoadAttrs(pObject obj, char* name, char* type, pWgtrNode templates else if (!strcmp(prop_name,"y")) this_node->r_y = val.Integer; else if (!strcmp(prop_name,"width")) this_node->r_width = val.Integer; else if (!strcmp(prop_name,"height")) this_node->r_height = val.Integer; - else if (!strcmp(prop_name,"fl_x")) this_node->fl_x = val.Integer; - else if (!strcmp(prop_name,"fl_y")) this_node->fl_y = val.Integer; - else if (!strcmp(prop_name,"fl_width")) this_node->fl_width = val.Integer; - else if (!strcmp(prop_name,"fl_height")) this_node->fl_height = val.Integer; + else if (!strcmp(prop_name,"fl_width")) + { + this_node->fl_width = val.Integer; + this_node->Flags |= WGTR_F_FLWIDTHSET; + } + else if (!strcmp(prop_name,"fl_height")) + { + this_node->fl_height = val.Integer; + this_node->Flags |= WGTR_F_FLHEIGHTSET; + } else wgtrAddProperty(this_node, prop_name, prop_type, &val, rval == 1); } else wgtrAddProperty(this_node, prop_name, prop_type, &val, rval == 1); @@ -783,8 +806,8 @@ wgtr_internal_AddChildren(pObject obj, pWgtrNode this_node, pWgtrNode templates[ /** compute actual offsets that would have been used had the ** widget really been rendered. **/ - nxo = child_node->x + child_node->left; - nyo = child_node->y + child_node->top; + nxo = child_node->x + child_node->inset_left; + nyo = child_node->y + child_node->inset_top; /** Now we no longer need the widget. **/ wgtrFree(child_node); @@ -1307,12 +1330,18 @@ wgtrGetPropertyType(pWgtrNode widget, char* name) pObjProperty prop = NULL; ASSERTMAGIC(widget, MGK_WGTR); - if (!strcmp(name, "name")) return DATA_T_STRING; - else if (!strcmp(name, "outer_type")) return DATA_T_STRING; - else if (!strcmp(name, "x") || !strcmp(name, "y") || !strcmp(name, "width") || !strcmp(name, "height") || - !strcmp(name, "r_x") || !strcmp(name, "r_y") || !strcmp(name, "r_width") || !strcmp(name, "r_height") || - !strcmp(name, "fl_x") || !strcmp(name, "fl_y") || !strcmp(name, "fl_width") || !strcmp(name, "fl_height")) + if (strcmp(name, "name") == 0 || strcmp(name, "outer_type") == 0) + return DATA_T_STRING; + else if (strcmp(name, "x") == 0 || strcmp(name, "y") == 0 || strcmp(name, "width") == 0 || strcmp(name, "height") == 0 || + strcmp(name, "r_x") == 0 || strcmp(name, "r_y") == 0 || strcmp(name, "r_width") == 0 || strcmp(name, "r_height") == 0 || + strcmp(name, "fl_width") == 0 || strcmp(name, "fl_height") == 0 || + strcmp(name, "fl_parent_w") == 0 || strcmp(name, "fl_parent_h") == 0) return DATA_T_INTEGER; + else if (strcmp(name, "fl_scale_x") == 0 || strcmp(name, "fl_scale_y") == 0 || + strcmp(name, "fl_scale_w") == 0 || strcmp(name, "fl_scale_h") == 0 || + strcmp(name, "fx") == 0 || strcmp(name, "fy") == 0 || strcmp(name, "fw") == 0 || strcmp(name, "fh") == 0) + return DATA_T_DOUBLE; + count = xaCount(&(widget->Properties)); for (i=0;iInteger = widget->fl_x; return 0; } - if (!strcmp(name+3, "y")) { val->Integer = widget->fl_y; return 0; } if (!strcmp(name+3, "width")) { val->Integer = widget->fl_width; return 0; } if (!strcmp(name+3, "height")) { val->Integer = widget->fl_height; return 0; } + if (!strcmp(name+3, "parent_w")) { val->Integer = widget->fl_parent_w; return 0; } + if (!strcmp(name+3, "parent_h")) { val->Integer = widget->fl_parent_h; return 0; } + } + } + else if (datatype == DATA_T_DOUBLE) + { + if (strncmp(name, "fl_scale_", 9) == 0) + { + if (strcmp(name+9, "x") == 0) { val->Double = widget->fl_scale_x; return 0; } + else if (strcmp(name+9, "y") == 0) { val->Double = widget->fl_scale_y; return 0; } + else if (strcmp(name+9, "w") == 0) { val->Double = widget->fl_scale_w; return 0; } + else if (strcmp(name+9, "h") == 0) { val->Double = widget->fl_scale_h; return 0; } + } + else if (strncmp(name, "f", 1) == 0) + { + if (strcmp(name+1, "x") == 0) { val->Double = widget->fx; return 0; } + else if (strcmp(name+1, "y") == 0) { val->Double = widget->fy; return 0; } + else if (strcmp(name+1, "w") == 0) { val->Double = widget->fw; return 0; } + else if (strcmp(name+1, "h") == 0) { val->Double = widget->fh; return 0; } } } else if (datatype == DATA_T_STRING) @@ -1549,7 +1595,7 @@ wgtrSetProperty(pWgtrNode widget, char* name, int datatype, pObjData val) pWgtrNode wgtrNewNode( char* name, char* type, pObjSession s, int rx, int ry, int rwidth, int rheight, - int flx, int fly, int flwidth, int flheight) + int flwidth, int flheight) { pWgtrNode node; @@ -1568,18 +1614,24 @@ wgtrNewNode( char* name, char* type, pObjSession s, node->y = node->r_y = ry; node->width = node->r_width = rwidth; node->height = node->r_height = rheight; - node->fl_x = flx; - node->fl_y = fly; node->fl_width = flwidth; node->fl_height = flheight; + node->fl_parent_h = -1; + node->fl_parent_w = -1; + node->fl_scale_x = 0.0; + node->fl_scale_y = 0.0; + node->fl_scale_w = 0.0; + node->fl_scale_h = 0.0; node->ObjSession = s; node->Parent = NULL; - node->min_height = 0; - node->min_width = 0; + node->min_height = node->min_width = 0; node->LayoutGrid = NULL; node->Root = node; /* this will change when it is added as a child */ node->DMPrivate = NULL; - node->top = node->bottom = node->right = node->left = 0; + node->inset_top = 0; + node->inset_bottom = 0; + node->inset_right = 0; + node->inset_left = 0; node->Verified = 0; xaInit(&(node->Properties), 16); @@ -1610,6 +1662,49 @@ wgtrSetupNode(pWgtrNode node) } +/*** wgtrSetInsets() - Declares the space that a widget needs in its outer box + *** (x/y/width/height) for its UI (e.g. title bars, tabs, or scrollbars). + *** The remaining space is the widget's client area where children are laid + *** out. Widget drivers should call this from their New() function, using + *** values based on what their rendering driver needs to draw. + *** + *** Insets shrink the client area but do not alter its coordinate space. + *** Children always render from (0,0), although the top and left insets may + *** shift where that point is rendered. + *** + *** A widget cannot be laid out in a space too small for its insets, so this + *** function raises min_width/min_height to cover the insets. + *** + *** @param node The widget node on which insets are to be set. + *** @param top The number of px required at the top of the widget. + *** @param bottom The number of px required at the bottom of the widget. + *** @param left The number of px required at the left of the widget. + *** @param right The number of px required at the right of the widget. + ***/ +void +wgtrSetInsets(pWgtrNode node, int top, int bottom, int left, int right) + { + if (UNLIKELY(node == NULL)) + { + fprintf(stderr, + "Warning: Call to wgtrSetInsets(null, %d, %d, %d, %d) with null widget tree node?!\n", + top, bottom, left, right + ); + return; + } + ASSERTMAGIC(node, MGK_WGTR); + + node->inset_top = top; + node->inset_bottom = bottom; + node->inset_left = left; + node->inset_right = right; + + /** Raise min_width and min_height to cover insets (if needed). **/ + node->min_width = max(node->min_width, left + right); + node->min_height = max(node->min_height, top + bottom); + } + + int wgtrDeleteChild(pWgtrNode widget, char* child_name) { @@ -2066,14 +2161,12 @@ wgtrInitialize() wgtddInitialize(); wgtebInitialize(); wgtexInitialize(); - wgtfbInitialize(); wgtformInitialize(); wgtfsInitialize(); wgtfuInitialize(); wgtsetInitialize(); wgthintInitialize(); wgthtmlInitialize(); - wgtibtnInitialize(); wgtimgInitialize(); wgtlblInitialize(); wgtmenuInitialize(); @@ -2091,7 +2184,6 @@ wgtrInitialize() wgttblInitialize(); wgttermInitialize(); wgttxInitialize(); - wgttbtnInitialize(); wgttmInitialize(); wgttreeInitialize(); wgtvblInitialize(); @@ -2283,39 +2375,88 @@ wgtrRenameProperty(pWgtrNode tree, char* oldname, char* newname) } -/*** wgtrGetContainerHeight() - returns the height of the (visual) container - *** of a widget. +/*** wgtrGetHtmlContainer() - returns the nearest ancestor that emits an HTML + *** container for its children, or the root if none of them do. Returns NULL + *** only if the widget has no parent. ***/ -int -wgtrGetContainerHeight(pWgtrNode tree) +pWgtrNode +wgtrGetHtmlContainer(pWgtrNode widget) { - int c_height; - do { - tree = tree->Parent; - } while(tree->Parent && (tree->Flags & WGTR_F_NONVISUAL)); - c_height = tree->height - 2; - if (!strcmp(tree->Type, "widget/childwindow")) - c_height -= 24; - else if (!strcmp(tree->Type, "widget/scrollpane")) - c_height += 2; - return c_height; + if (UNLIKELY(widget == NULL)) return NULL; + + pWgtrNode parent = widget->Parent; + while (parent != NULL && parent->Parent != NULL && !(parent->Flags & WGTR_F_VISUAL_CONTAINER)) + parent = parent->Parent; + + return parent; } -/*** wgtrGetContainerWidth() - returns the width of the (visual) container - *** of a widget. - ***/ +/** wgtrGetContainerWidth() - returns the width of the widget's HTML container. **/ int wgtrGetContainerWidth(pWgtrNode tree) { - int c_width; - do { - tree = tree->Parent; - } while(tree->Parent && (tree->Flags & WGTR_F_NONVISUAL)); - c_width = tree->width - 2; - if (!strcmp(tree->Type, "widget/scrollpane")) - c_width = c_width + 2 - 18; - return c_width; + if (UNLIKELY(tree == NULL)) + { + fprintf(stderr, "Warning: Call to wgtrGetContainerWidth(null) with null widget tree node?!\n"); + return -1; + } + + /** Find container. **/ + pWgtrNode container = tree; + do container = wgtrGetHtmlContainer(container); + while (container != NULL && container->width < 0); + + /** Check for getter failure. **/ + if (UNLIKELY(container == NULL)) + { + /*** We can't use mssError() here because, when the return value + *** is passed to qprintf(), it reports a bad value and our error + *** is cleared. + ***/ + fprintf(stderr, + "Warning: No HTML container with a known width for \"%s\":\"%s\".\n", + tree->Name, tree->Type + ); + return -1; + } + + /** Return parent width. **/ + return tree->fl_parent_w = container->width - (container->inset_left + container->inset_right); + } + + +/** wgtrGetContainerHeight() - returns the height of the widget's HTML container. **/ +int +wgtrGetContainerHeight(pWgtrNode tree) + { + if (UNLIKELY(tree == NULL)) + { + fprintf(stderr, "Warning: Call to wgtrGetContainerHeight(null) with null widget tree node?!\n"); + return -1; + } + + /** Find container. **/ + pWgtrNode container = tree; + do container = wgtrGetHtmlContainer(container); + while (container != NULL && container->height < 0); + + /** Check for getter failure. **/ + if (UNLIKELY(container == NULL)) + { + /*** We can't use mssError() here because, when the return value + *** is passed to qprintf(), it reports a bad value and our error + *** is cleared. + ***/ + fprintf(stderr, + "Warning: No HTML container with a known height for \"%s\":\"%s\".\n", + tree->Name, tree->Type + ); + return -1; + } + + /** Return parent height. **/ + return tree->fl_parent_h = container->height - (container->inset_top + container->inset_bottom); } @@ -2330,37 +2471,6 @@ wgtrGetTemplatePath(pWgtrNode tree, int n) } -/*** wgtrMoveChildren() - given a widget tree node, adjust the positioning - *** of all children by the given amount. If some children are nonvisual, - *** then this function adjusts the children in those nonvisual containers, - *** as well. - ***/ -int -wgtrMoveChildren(pWgtrNode tree, int x_offset, int y_offset) - { - int cnt = xaCount(&tree->Children); - int i; - pWgtrNode child; - - for(i=0;iChildren, i); - if (child->Flags & WGTR_F_NONVISUAL) - { - wgtrMoveChildren(child, x_offset, y_offset); - } - else - { - child->x += x_offset; - child->pre_x += x_offset; - child->y += y_offset; - child->pre_y += y_offset; - } - } - - return 0; - } - /*** wgtrRenderObject() - does a wgtrParseOpenObject, wgtrVerify, and *** wgtrRender, as well as handling any application defined parameters *** and expressions.