Built-in Resource Editor


Overview

Some Graphic objects, have a built-in panel that allows the end-user to edit the object resources. The objects that have a built-in resource editor are: Text, Chart, AxisObject, BarLine, Bar3D, HighLow, Histogram, Pie, Surface3D, XYPlot, BarSeries, LineSeries, Symbol and Legend.


Figure 15: Built-in Chart Editor.

The built-in resource editor can be invoked for a specific object using public function XintEditObjectManageResourceDialog. Action ResourceDialog (defined by the EditObject class) is also available for the end-user to interactively edit an object's resources. This action is connected as follows in the default translation table:

ActionDefault Dialog Translation
None <Btn1Down>ResourceDialog()

Note that action ResourceDialog actually checks for a double click, so it will only trigger on a double click of Button1.

You can disable the resource editor panel for a specific object by setting Graphic resource XmNresourceDialog to False when creating the object. You can disable all resource dialogs by registering callback XmNresourceDialogCallback on the EditObject widget and setting the doit flag member of the callback structure to False, as shown in the code sample below:

    static void ResourceDialogCallback(widget, data, cb)
    Widget widget;
    XtPointer data;
    XintEditObjectResourceDialogCallbackStruct *cb;
    {
        cb->doit = False
    }

If you need to unmanage the parent of a chart object (EditObject widget), you should first invoke Graphic function XintGraphicUnmanageDialog on the chart to unmanage all built-in dialogs that may be active. Failure to do so will prevent those dialog panels from ever reappearing the next time the chart's parent is managed (Motif bug).


Customization of a Built-In Resource Editor

Each built-in resource editor panel can be customized to some extent. For example, it is possible to remove an item in the panel, remove an item inside an option menu or rename a component in the resource editor panel. The application has also the option to provide its own custom editor panel using callback XmNresourceDialogCallback.

Note: all objects of the same class share the same built-in editor panel. So, any change to an object panel will automatically apply to all the other objects of the same class.

The widget(s) used to edit a particular resource are encapsulated inside a container widget (box). For example, an editor for a resource of type String is composed of a box containing a label and a Text widget. Function XintResEditGetBox retrieves the box widget ID for a particular resource.

    Widget XintResEditGetBox(Object object, String resource_name, int num);

You can unmanage the box to prevent a particular resource from being displayed and edited. Argument num represents the number of components for the resource and it should be set to 1 in most cases. Some resources however, such as XmNlimits for the AxisObject, have several components (one to edit the minimum value and one to edit the maximum value). Setting argument num to 1 will retrieve the box used to edit the minimum value. Setting argument num to 2 will retrieve the box used to edit the maximum value.

Most resource editors use a label widget or gadget to display the text identifying which resource is being set (see strings "Chart Type", "Title", "Footer", "Show Legend" in Figure 15). Function XintResEditGetLabel retrieves the label ID for a particular resource if there is one. If no label is defined, the function returns NULL. This label ID can be used to modify the text displayed in the editor.

    Widget XintResEditGetLabel(Object object, String resource_name, int num);

Some resources, such as XmNchartType, are edited using an option menu. Function XintResEditGetMenuButton can be used to retrieve the ID of a particular button in the option menu. Argument button_name is the name of the option as it is specified in the menu. Unmanage the button ID returned by function XintResEditGetMenuButton to remove an option from the menu.

    Widget XintResEditGetMenuButton(Object object, String resource_name, 
                                    String button_name);

The following example illustrates how to use the above functions. We modify the chart editor shown in Figure 15 to a) remove the chart footer component, b) rename "Chart Type" to "Type" and, c) remove option "High Low" from the option menu used to select the chart type.

    Object chart;
    Widget button, label;
    ...
    /* Unmanage XmNchartFooter editor */
    box = XintResEditGetBox(chart, XmNchartFooter, 1);
    if (box != NULL) XtUnmanageChild(box);

    /* Change name of label from "Chart Type" to "Type" */
    label = XintResEditGetLabel(chart, XmNchartType, 1);
    if (label) {
        XmString cstring;
        cstring =  XmStringCreateSimple("Type");
        XtVaSetValues(label, XmNlabelString, cstring, NULL);
        XmStringFree(cstring);
    }

    /* Unmanage High Low option in chart type option menu */
    button = XintResEditGetMenuButton(chart, XmNchartType,"High Low");
    if (button) XtUnmanageChild(button);


Creating your own Resource Editor

If the editing options provided to customize a resource editor panel are not sufficient for your application, you may want to create your own custom panel using callback XmNresourceDialogCallback. This callback must be registered on the parent EditObject widget and it is called for all objects that need to be edited. It is important that you check for the object class or the object ID so that you display your custom panel only for the specified object class or object. For objects in a group, this callback always returns the object ID of the top group. So if the object returned is of class ChartObject, you should call function XintChartGetSelectedComponent to retrieve the actual component of the chart that needs to be edited. The example below illustrates how to redefine the resource editor panel for the Axis object class. Our custom panel only lets the end-user edit the axis label. The complete listing of this example can be found in file chart_res.c, in directory examples.

    static void ResourceDialogCallback(widget, data, cb)
    Widget widget;
    XtPointer data;
    XintEditObjectResourceDialogCallbackStruct *cb;
    {
        static Widget my_axis_panel = NULL;
        static Widget axis_widgets[2];
        Object selected_object;
        int code;

        if (XintIsChart(cb->object))
            selected_object = XintChartGetSelectedComponent( cb->object, &code);
        else
            selected_object = cb->object;

        if (XintIsAxisObject(selected_object)) {

            /* Create my own panel if first time */
            if (!my_axis_panel)
                my_axis_panel = BuildAxisPanel(widget, axis_widgets);

            /* load axis panel to contain current axis state */
            LoadAxisPanel(axis_widgets, selected_object);

            /* Display my panel */
            XtManageChild(my_axis_panel);

            /* Turn off built-in panel */
            cb->doit = False;
        }
    }

Function BuildAxisPanel builds the dialog panel and fills the array axis_widgets with the widget ID's it will need later. This function uses an INT convenience function, IntCreateDialogPanel, that creates a dialog widget with a set of buttons as specified.

    static Widget BuildAxisPanel(widget, axis_widget_list)
    Widget widget;
    Widget *axis_widget_list;
    {
        Widget panel, vbox;

        panel = (Widget) IntCreateDialogPanel(XtParent(widget), 
                                              "My Axis Editor",
                                              IntOK | IntAPPLY | IntCANCEL,
                                              IntOK, EditAxisCallback,
                                              (XtPointer)axis_widget_list,
                                              XintVERTICAL, 5, 5, &vbox, NULL);

        /* Create text to edit axis label */
        axis_widget_list[0] = XtVaCreateManagedWidget("axis_label",
                                               xmTextWidgetClass, vbox,
                                               XmNcolumns, 20,
                                               XmNeditMode, XmSINGLE_LINE_EDIT,
                                               NULL);

        /* Save edit object ID. We will need it in EditAxisCallback */
        axis_widget_list[1] = widget;

        return panel;
    }

Function LoadAxisPanel retrieves the resources from the specified objects and loads the values into the panel before we manage it.

    static void LoadAxisPanel(axis_widget_list, object)
    Widget *axis_widget_list;
    Object object;
    {
       char *label_string;

       XtVaGetValues((Widget) object, XmNlabel, &label_string, NULL);

       XmTextSetString(axis_widget_list[0], label_string);
    }

Finally, here is the code for callback EditAxisCallback which is the callback invoked when the OK or APPLY button is selected from the custom menu.

    static void EditAxisCallback(widget, axis_widget_list, cb)
    Widget widget;
    Widget *axis_widget_list;
    XmAnyCallbackStruct   *cb;
    {
        char *axis_label;
        Object *list;
        Object object;
        int i, count;
        int code;

        if (cb->reason == IntOK || IntAPPLY) {
            axis_label = XmTextGetString(axis_widget_list[0]);

            /* 
             * get the list of selected object, and apply SetValues to the
             * Axis object(s)
             */
            list = XintEditObjectSelectList(axis_widget_list[1], &count);

            for (i=0; i<count; i++) {
                 if (XintIsChart(list[i])) {
                     object = XintChartGetSelectedComponent(list[i], &code);
                     if (XintIsAxisObject(object))
                         XtVaSetValues((Widget) object, 
                                       XmNlabel, axis_label, NULL);
                 }
            }
   
            /* Cleanup */
            if (list) XtFree((char *) list);
            if (axis_label) XtFree(axis_label);
        }
    }