Invalid Driver Targets are now indicated appropriately (using Red Backgrounds)
authorJoshua Leung <aligorith@gmail.com>
Thu, 28 Feb 2013 07:13:33 +0000 (07:13 +0000)
committerJoshua Leung <aligorith@gmail.com>
Thu, 28 Feb 2013 07:13:33 +0000 (07:13 +0000)
in the UI

This is the second part of the fixes for [#32492], making it easier to identify
which part of a driver (i.e. which of its targets) is causing problems

A number of additional/related changes needed to be made:
* Red-alert status for layouts is now propagated down to child layouts when they
are created. This is needed as otherwise some of the templates used in the Graph
Editor driver settings won't actually get the red-alert status flushed down to
them. Also, note that this status needs to be set before any widgets are added
to the layout, or else the settings aren't applied when the relevant widgets get
created.

* "Single Property" RNA-Paths resulting in out of bounds array access will now
trigger an error status and appropriate warnings

TODO:
* The error tagging doesn't get applied immediately after variables are created,
or their types changed
* There was also some other weirdness involved when a higher-value flag (1<<10)
was used for this setting, which needs further attention

source/blender/blenkernel/intern/fcurve.c
source/blender/editors/interface/interface_layout.c
source/blender/editors/space_graph/graph_buttons.c
source/blender/makesdna/DNA_anim_types.h

index 23f3a3ad3fd1f6a4ca62b0fb6531a55228e3ed95..64480980acdca6f1654a0053f5dc5b77f6bc61e7 100644 (file)
@@ -1014,7 +1014,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
        PointerRNA id_ptr, ptr;
        PropertyRNA *prop;
        ID *id;
-       int index;
+       int index = -1;
        float value = 0.0f;
        
        /* sanity check */
@@ -1029,6 +1029,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
                printf("Error: driver has an invalid target to use\n");
                if (G.debug & G_DEBUG) printf("\tpath = %s\n", dtar->rna_path);
                driver->flag |= DRIVER_FLAG_INVALID;
+               dtar->flag   |= DTAR_FLAG_INVALID;
                return 0.0f;
        }
        
@@ -1039,7 +1040,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
        if (RNA_path_resolve_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
                if (RNA_property_array_check(prop)) {
                        /* array */
-                       if (index < RNA_property_array_length(&ptr, prop)) {
+                       if ((index >= 0) && (index < RNA_property_array_length(&ptr, prop))) {
                                switch (RNA_property_type(prop)) {
                                        case PROP_BOOLEAN:
                                                value = (float)RNA_property_boolean_get_index(&ptr, prop, index);
@@ -1054,6 +1055,17 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
                                                break;
                                }
                        }
+                       else {
+                               /* out of bounds */
+                               if (G.debug & G_DEBUG) {
+                                       printf("Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)", 
+                                              id->name, dtar->rna_path, index);
+                               }
+                               
+                               driver->flag |= DRIVER_FLAG_INVALID;
+                               dtar->flag   |= DTAR_FLAG_INVALID;
+                               return 0.0f;
+                       }
                }
                else {
                        /* not an array */
@@ -1074,16 +1086,19 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
                                        break;
                        }
                }
-
        }
        else {
+               /* path couldn't be resolved */
                if (G.debug & G_DEBUG)
                        printf("Driver Evaluation Error: cannot resolve target for %s -> %s\n", id->name, dtar->rna_path);
                
                driver->flag |= DRIVER_FLAG_INVALID;
+               dtar->flag   |= DTAR_FLAG_INVALID;
                return 0.0f;
        }
        
+       /* if we're still here, we should be ok... */
+       dtar->flag &= ~DTAR_FLAG_INVALID;
        return value;
 }
 
@@ -1122,25 +1137,39 @@ static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar)
 /* evaluate 'rotation difference' driver variable */
 static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
 {
+       DriverTarget *dtar1 = &dvar->targets[0];
+       DriverTarget *dtar2 = &dvar->targets[1];
        bPoseChannel *pchan, *pchan2;
        float q1[4], q2[4], quat[4], angle;
        
        /* get pose channels, and check if we've got two */
-       pchan = dtar_get_pchan_ptr(driver, &dvar->targets[0]);
-       pchan2 = dtar_get_pchan_ptr(driver, &dvar->targets[1]);
+       pchan  = dtar_get_pchan_ptr(driver, dtar1);
+       pchan2 = dtar_get_pchan_ptr(driver, dtar2);
        
        if (ELEM(NULL, pchan, pchan2)) {
                /* disable this driver, since it doesn't work correctly... */
                driver->flag |= DRIVER_FLAG_INVALID;
                
                /* check what the error was */
-               if ((pchan == NULL) && (pchan2 == NULL))
+               if ((pchan == NULL) && (pchan2 == NULL)) {
                        printf("Driver Evaluation Error: Rotational difference failed - first 2 targets invalid\n");
-               else if (pchan == NULL)
+                       
+                       dtar1->flag |= DTAR_FLAG_INVALID;
+                       dtar2->flag |= DTAR_FLAG_INVALID;
+               }
+               else if (pchan == NULL) {
                        printf("Driver Evaluation Error: Rotational difference failed - first target not valid PoseChannel\n");
-               else if (pchan2 == NULL)
+                       
+                       dtar1->flag |=  DTAR_FLAG_INVALID;
+                       dtar2->flag &= ~DTAR_FLAG_INVALID;
+               }
+               else if (pchan2 == NULL) {
                        printf("Driver Evaluation Error: Rotational difference failed - second target not valid PoseChannel\n");
                        
+                       dtar1->flag &= ~DTAR_FLAG_INVALID;
+                       dtar2->flag |=  DTAR_FLAG_INVALID;
+               }
+               
                /* stop here... */
                return 0.0f;
        }
@@ -1177,8 +1206,13 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
                if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
                        /* invalid target, so will not have enough targets */
                        driver->flag |= DRIVER_FLAG_INVALID;
+                       dtar->flag   |= DTAR_FLAG_INVALID;
                        return 0.0f;
                }
+               else {
+                       /* target seems to be OK now... */
+                       dtar->flag &= ~DTAR_FLAG_INVALID;
+               }
                
                /* try to get posechannel */
                pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
@@ -1264,8 +1298,13 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
        if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
                /* invalid target, so will not have enough targets */
                driver->flag |= DRIVER_FLAG_INVALID;
+               dtar->flag   |= DTAR_FLAG_INVALID;
                return 0.0f;
        }
+       else {
+               /* target should be valid now */
+               dtar->flag &= ~DTAR_FLAG_INVALID;
+       }
        
        /* try to get posechannel */
        pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name);
@@ -1463,7 +1502,7 @@ void driver_change_variable_type(DriverVar *dvar, int type)
                /* store the flags */
                dtar->flag = flags;
                
-               /* object ID types only, or idtype not yet initialized*/
+               /* object ID types only, or idtype not yet initialized */
                if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0))
                        dtar->idtype = ID_OB;
        }
index c69f53a53d2509f7972d0755f982b2242fbdbe66..1e95b4df7624e1680ac33a640ce8f1608f61d0dc 100644 (file)
@@ -2263,6 +2263,7 @@ uiLayout *uiLayoutRow(uiLayout *layout, int align)
        litem->enabled = 1;
        litem->context = layout->context;
        litem->space = (align) ? 0 : layout->root->style->buttonspacex;
+       litem->redalert = layout->redalert;
        litem->w = layout->w;
        BLI_addtail(&layout->items, litem);
 
@@ -2283,6 +2284,7 @@ uiLayout *uiLayoutColumn(uiLayout *layout, int align)
        litem->enabled = 1;
        litem->context = layout->context;
        litem->space = (litem->align) ? 0 : layout->root->style->buttonspacey;
+       litem->redalert = layout->redalert;
        litem->w = layout->w;
        BLI_addtail(&layout->items, litem);
 
@@ -2303,6 +2305,7 @@ uiLayout *uiLayoutColumnFlow(uiLayout *layout, int number, int align)
        flow->litem.enabled = 1;
        flow->litem.context = layout->context;
        flow->litem.space = (flow->litem.align) ? 0 : layout->root->style->columnspace;
+       flow->litem.redalert = layout->redalert;
        flow->litem.w = layout->w;
        flow->number = number;
        BLI_addtail(&layout->items, flow);
@@ -2323,6 +2326,7 @@ static uiLayoutItemBx *ui_layout_box(uiLayout *layout, int type)
        box->litem.enabled = 1;
        box->litem.context = layout->context;
        box->litem.space = layout->root->style->columnspace;
+       box->litem.redalert = layout->redalert;
        box->litem.w = layout->w;
        BLI_addtail(&layout->items, box);
 
index ab16a9d55e66adebbc77bed646630ea6c34fca99..7da147b1575a7490fd97654c4a77ff2b9bf186cc 100644 (file)
@@ -461,6 +461,7 @@ static void graph_panel_driverVar__singleProp(uiLayout *layout, ID *id, DriverVa
        
        /* Target ID */
        row = uiLayoutRow(layout, FALSE);
+       uiLayoutSetRedAlert(row, ((dtar->flag & DTAR_FLAG_INVALID) && !dtar->id));
        uiTemplateAnyID(row, &dtar_ptr, "id", "id_type", IFACE_("Prop:"));
        
        /* Target Property */
@@ -470,8 +471,9 @@ static void graph_panel_driverVar__singleProp(uiLayout *layout, ID *id, DriverVa
                /* get pointer for resolving the property selected */
                RNA_id_pointer_create(dtar->id, &root_ptr);
                
-               col = uiLayoutColumn(layout, TRUE);
                /* rna path */
+               col = uiLayoutColumn(layout, TRUE);
+               uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID));
                uiTemplatePathBuilder(col, &dtar_ptr, "data_path", &root_ptr, IFACE_("Path"));
        }
 }
@@ -492,21 +494,23 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar *
        
        /* Bone 1 */
        col = uiLayoutColumn(layout, TRUE);
+       uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
        uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Bone 1:"));
-               
+       
        if (dtar->id && ob1->pose) {
                PointerRNA tar_ptr;
-                       
+               
                RNA_pointer_create(dtar->id, &RNA_Pose, ob1->pose, &tar_ptr);
                uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
        }
        
        col = uiLayoutColumn(layout, TRUE);
+       uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
        uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Bone 2:"));
                
        if (dtar2->id && ob2->pose) {
                PointerRNA tar_ptr;
-                       
+               
                RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr);
                uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
        }
@@ -515,7 +519,7 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar *
 /* settings for 'location difference' driver variable type */
 static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar *dvar)
 {
-       DriverTarget *dtar = &dvar->targets[0];
+       DriverTarget *dtar  = &dvar->targets[0];
        DriverTarget *dtar2 = &dvar->targets[1];
        Object *ob1 = (Object *)dtar->id;
        Object *ob2 = (Object *)dtar2->id;
@@ -523,16 +527,17 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar *
        uiLayout *col;
        
        /* initialize RNA pointer to the target */
-       RNA_pointer_create(id, &RNA_DriverTarget, dtar, &dtar_ptr); 
+       RNA_pointer_create(id, &RNA_DriverTarget, dtar,  &dtar_ptr); 
        RNA_pointer_create(id, &RNA_DriverTarget, dtar2, &dtar2_ptr); 
        
        /* Bone 1 */
        col = uiLayoutColumn(layout, TRUE);
+       uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
        uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone 1:"));
                
        if (dtar->id && ob1->pose) {
                PointerRNA tar_ptr;
-                       
+               
                RNA_pointer_create(dtar->id, &RNA_Pose, ob1->pose, &tar_ptr);
                uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
        }
@@ -540,11 +545,12 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar *
        uiItemR(col, &dtar_ptr, "transform_space", 0, NULL, ICON_NONE);
        
        col = uiLayoutColumn(layout, TRUE);
+       uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
        uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Ob/Bone 2:"));
                
        if (dtar2->id && ob2->pose) {
                PointerRNA tar_ptr;
-                       
+               
                RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr);
                uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
        }
@@ -565,11 +571,12 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar
        
        /* properties */
        col = uiLayoutColumn(layout, TRUE);
+       uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
        uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone:"));
                
        if (dtar->id && ob->pose) {
                PointerRNA tar_ptr;
-                       
+               
                RNA_pointer_create(dtar->id, &RNA_Pose, ob->pose, &tar_ptr);
                uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
        }
@@ -623,7 +630,7 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
        if (driver->type == DRIVER_TYPE_PYTHON) {
                /* expression */
                uiItemR(col, &driver_ptr, "expression", 0, IFACE_("Expr"), ICON_NONE);
-
+               
                /* errors? */
                if (driver->flag & DRIVER_FLAG_INVALID)
                        uiItemL(col, IFACE_("ERROR: Invalid Python expression"), ICON_ERROR);
@@ -668,19 +675,19 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
                box = uiLayoutBox(col);
                /* first row context info for driver */
                RNA_pointer_create(ale->id, &RNA_DriverVariable, dvar, &dvar_ptr);
-
+               
                row = uiLayoutRow(box, FALSE);
                block = uiLayoutGetBlock(row);
                /* variable name */
                uiItemR(row, &dvar_ptr, "name", 0, "", ICON_NONE);
-
+               
                /* remove button */
                uiBlockSetEmboss(block, UI_EMBOSSN);
                but = uiDefIconBut(block, BUT, B_IPO_DEPCHANGE, ICON_X, 290, 0, UI_UNIT_X, UI_UNIT_Y,
                                   NULL, 0.0, 0.0, 0.0, 0.0, IFACE_("Delete target variable"));
                uiButSetFunc(but, driver_delete_var_cb, driver, dvar);
                uiBlockSetEmboss(block, UI_EMBOSS);
-
+               
                /* variable type */
                row = uiLayoutRow(box, FALSE);
                uiItemR(row, &dvar_ptr, "type", 0, "", ICON_NONE);
@@ -721,7 +728,7 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
                        else {
                                BLI_snprintf(valBuf, sizeof(valBuf), "%.3f", dvar->curval);
                        }
-
+                       
                        uiItemL(row, valBuf, ICON_NONE);
                }
        }
index 1ac6e6db94bac8a41c8cacb859d23e57b4cb1917..73e2a9f433be1d2fd488ef5c070c55e8fd4c3561 100644 (file)
@@ -284,15 +284,18 @@ typedef enum eDriverTarget_Flag {
                /* used for targets that use the pchan_name instead of RNA path 
                 * (i.e. rotation difference) 
                 */
-       DTAR_FLAG_STRUCT_REF    = (1<<0),
+       DTAR_FLAG_STRUCT_REF    = (1 << 0),
                /* idtype can only be 'Object' */
-       DTAR_FLAG_ID_OB_ONLY    = (1<<1),
+       DTAR_FLAG_ID_OB_ONLY    = (1 << 1),
        
        /* "localspace" flags */
                /* base flag - basically "pre parent+constraints" */
-       DTAR_FLAG_LOCALSPACE    = (1<<2),
+       DTAR_FLAG_LOCALSPACE    = (1 << 2),
                /* include constraints transformed to space including parents */
-       DTAR_FLAG_LOCAL_CONSTS  = (1<<3),
+       DTAR_FLAG_LOCAL_CONSTS  = (1 << 3),
+       
+       /* error flags */
+       DTAR_FLAG_INVALID               = (1 << 4),
 } eDriverTarget_Flag;
 
 /* Transform Channels for Driver Targets */