/**
 * Set up data for defining a new pie menu level and add button that invokes it.
 */
void ui_pie_menu_level_create(
        uiBlock *block, wmOperatorType *ot, const char *propname, IDProperty *properties,
        const EnumPropertyItem *items, int totitem, int context, int flag)
{
	const int totitem_parent = PIE_MAX_ITEMS - 1;
	const int totitem_remain = totitem - totitem_parent;
	size_t array_size = sizeof(EnumPropertyItem) * totitem_remain;

	/* used as but->func_argN so freeing is handled elsewhere */
	EnumPropertyItem *remaining = MEM_mallocN(array_size + sizeof(EnumPropertyItem), "pie_level_item_array");
	memcpy(remaining, items + totitem_parent, array_size);
	/* a NULL terminating sentinal element is required */
	memset(&remaining[totitem_remain], 0, sizeof(EnumPropertyItem));


	/* yuk, static... issue is we can't reliably free this without doing dangerous changes */
	static PieMenuLevelData lvl;
	BLI_strncpy(lvl.title, block->pie_data.title, UI_MAX_NAME_STR);
	lvl.totitem    = totitem_remain;
	lvl.ot         = ot;
	lvl.propname   = propname;
	lvl.properties = properties;
	lvl.context    = context;
	lvl.flag       = flag;

	/* add a 'more' menu entry */
	uiBut *but = uiDefIconTextBut(block, UI_BTYPE_BUT, 0, ICON_PLUS, "More", 0, 0, UI_UNIT_X * 3, UI_UNIT_Y, NULL,
	                              0.0f, 0.0f, 0.0f, 0.0f, "Show more items of this menu");
	UI_but_funcN_set(but, ui_pie_menu_level_invoke, remaining, &lvl);
}
Esempio n. 2
0
static void template_texture_user_menu(bContext *C, uiLayout *layout, void *UNUSED(arg))
{
  /* callback when opening texture user selection menu, to create buttons. */
  SpaceProperties *sbuts = CTX_wm_space_properties(C);
  ButsContextTexture *ct = sbuts->texuser;
  ButsTextureUser *user;
  uiBlock *block = uiLayoutGetBlock(layout);
  const char *last_category = NULL;

  for (user = ct->users.first; user; user = user->next) {
    uiBut *but;
    char name[UI_MAX_NAME_STR];

    /* add label per category */
    if (!last_category || !STREQ(last_category, user->category)) {
      uiItemL(layout, IFACE_(user->category), ICON_NONE);
      but = block->buttons.last;
      but->drawflag = UI_BUT_TEXT_LEFT;
    }

    /* create button */
    if (user->prop) {
      PointerRNA texptr = RNA_property_pointer_get(&user->ptr, user->prop);
      Tex *tex = texptr.data;

      if (tex) {
        BLI_snprintf(name, UI_MAX_NAME_STR, "  %s - %s", user->name, tex->id.name + 2);
      }
      else {
        BLI_snprintf(name, UI_MAX_NAME_STR, "  %s", user->name);
      }
    }
    else {
      BLI_snprintf(name, UI_MAX_NAME_STR, "  %s", user->name);
    }

    but = uiDefIconTextBut(block,
                           UI_BTYPE_BUT,
                           0,
                           user->icon,
                           name,
                           0,
                           0,
                           UI_UNIT_X * 4,
                           UI_UNIT_Y,
                           NULL,
                           0.0,
                           0.0,
                           0.0,
                           0.0,
                           "");
    UI_but_funcN_set(but, template_texture_select, MEM_dupallocN(user), NULL);

    last_category = user->category;
  }

  UI_block_flag_enable(block, UI_BLOCK_NO_FLIP);
}
static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p)
{
	Main *bmain = CTX_data_main(C);
	Scene *scene = CTX_data_scene(C);
	uiBlock *block = uiLayoutGetBlock(layout);
	uiBut *but = (uiBut *)but_p;
	uiLayout *split, *column;
	NodeLinkArg *arg = (NodeLinkArg *)but->func_argN;
	bNodeSocket *sock = arg->sock;
	bNodeTreeType *ntreetype = arg->ntree->typeinfo;

	UI_block_flag_enable(block, UI_BLOCK_NO_FLIP);
	UI_block_layout_set_current(block, layout);
	split = uiLayoutSplit(layout, 0.0f, false);

	arg->bmain = bmain;
	arg->scene = scene;
	arg->layout = split;

	if (ntreetype && ntreetype->foreach_nodeclass)
		ntreetype->foreach_nodeclass(scene, arg, node_menu_column_foreach_cb);

	column = uiLayoutColumn(split, false);
	UI_block_layout_set_current(block, column);

	if (sock->link) {
		uiItemL(column, IFACE_("Link"), ICON_NONE);
		but = block->buttons.last;
		but->drawflag = UI_BUT_TEXT_LEFT;

		but = uiDefBut(block, UI_BTYPE_BUT, 0, IFACE_("Remove"), 0, 0, UI_UNIT_X * 4, UI_UNIT_Y,
		               NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Remove nodes connected to the input"));
		UI_but_funcN_set(but, ui_node_link, MEM_dupallocN(arg), SET_INT_IN_POINTER(UI_NODE_LINK_REMOVE));

		but = uiDefBut(block, UI_BTYPE_BUT, 0, IFACE_("Disconnect"), 0, 0, UI_UNIT_X * 4, UI_UNIT_Y,
		               NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Disconnect nodes connected to the input"));
		UI_but_funcN_set(but, ui_node_link, MEM_dupallocN(arg), SET_INT_IN_POINTER(UI_NODE_LINK_DISCONNECT));
	}

	ui_node_menu_column(arg, NODE_CLASS_GROUP, N_("Group"));
}
static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
{
	bNodeTree *ntree = arg->ntree;
	bNodeSocket *sock = arg->sock;
	uiLayout *layout = arg->layout;
	uiLayout *column = NULL;
	uiBlock *block = uiLayoutGetBlock(layout);
	uiBut *but;
	NodeLinkArg *argN;
	int first = 1;
	int compatibility = 0;
	
	if (ntree->type == NTREE_SHADER) {
		if (BKE_scene_use_new_shading_nodes(arg->scene))
			compatibility = NODE_NEW_SHADING;
		else
			compatibility = NODE_OLD_SHADING;
	}

	NODE_TYPES_BEGIN(ntype) {
		NodeLinkItem *items;
		int totitems;
		char name[UI_MAX_NAME_STR];
		const char *cur_node_name = NULL;
		int i, num = 0;
		int icon = ICON_NONE;
		
		if (compatibility && !(ntype->compatibility & compatibility))
			continue;
		
		if (ntype->nclass != nclass)
			continue;
		
		arg->node_type = ntype;
		
		ui_node_link_items(arg, SOCK_OUT, &items, &totitems);
		
		for (i = 0; i < totitems; ++i)
			if (ui_compatible_sockets(items[i].socket_type, sock->type))
				num++;
		
		for (i = 0; i < totitems; ++i) {
			if (!ui_compatible_sockets(items[i].socket_type, sock->type))
				continue;
			
			if (first) {
				column = uiLayoutColumn(layout, 0);
				UI_block_layout_set_current(block, column);
				
				uiItemL(column, IFACE_(cname), ICON_NODE);
				but = block->buttons.last;
				
				first = 0;
			}
			
			if (num > 1) {
				if (!cur_node_name || !STREQ(cur_node_name, items[i].node_name)) {
					cur_node_name = items[i].node_name;
					/* XXX Do not use uiItemL here, it would add an empty icon as we are in a menu! */
					uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_(cur_node_name), 0, 0, UI_UNIT_X * 4, UI_UNIT_Y,
					         NULL, 0.0, 0.0, 0.0, 0.0, "");
				}

				BLI_snprintf(name, UI_MAX_NAME_STR, "%s", IFACE_(items[i].socket_name));
				icon = ICON_BLANK1;
			}
			else {
				BLI_strncpy(name, IFACE_(items[i].node_name), UI_MAX_NAME_STR);
				icon = ICON_NONE;
			}
			
			but = uiDefIconTextBut(block, UI_BTYPE_BUT, 0, icon, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y,
			                       NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Add node to input"));
			
			argN = MEM_dupallocN(arg);
			argN->item = items[i];
			UI_but_funcN_set(but, ui_node_link, argN, NULL);
		}
		
		if (items)
			MEM_freeN(items);
	}
	NODE_TYPES_END
}
Esempio n. 5
0
/* Note: This function uses pixelspace (0, 0, winx, winy), not view2d. 
 * The controls are laid out as follows:
 *
 * -------------------------------------------
 * | Directory input               | execute |
 * -------------------------------------------
 * | Filename input        | + | - | cancel  |
 * -------------------------------------------
 *
 * The input widgets will stretch to fill any excess space.
 * When there isn't enough space for all controls to be shown, they are
 * hidden in this order: x/-, execute/cancel, input widgets.
 */
void file_draw_buttons(const bContext *C, ARegion *ar)
{
	/* Button layout. */
	const int max_x      = ar->winx - 10;
	const int line1_y    = ar->winy - (IMASEL_BUTTONS_HEIGHT / 2 + IMASEL_BUTTONS_MARGIN);
	const int line2_y    = line1_y - (IMASEL_BUTTONS_HEIGHT / 2 + IMASEL_BUTTONS_MARGIN);
	const int input_minw = 20;
	const int btn_h      = UI_UNIT_Y;
	const int btn_fn_w   = UI_UNIT_X;
	const int btn_minw   = 80;
	const int btn_margin = 20;
	const int separator  = 4;

	/* Additional locals. */
	char uiblockstr[32];
	int loadbutton;
	int fnumbuttons;
	int min_x       = 10;
	int chan_offs   = 0;
	int available_w = max_x - min_x;
	int line1_w     = available_w;
	int line2_w     = available_w;
	
	uiBut *but;
	uiBlock *block;
	SpaceFile *sfile  = CTX_wm_space_file(C);
	FileSelectParams *params = ED_fileselect_get_params(sfile);
	ARegion *artmp;
	const bool is_browse_only = (sfile->op == NULL);
	
	/* Initialize UI block. */
	BLI_snprintf(uiblockstr, sizeof(uiblockstr), "win %p", (void *)ar);
	block = UI_block_begin(C, ar, uiblockstr, UI_EMBOSS);

	/* exception to make space for collapsed region icon */
	for (artmp = CTX_wm_area(C)->regionbase.first; artmp; artmp = artmp->next) {
		if (artmp->regiontype == RGN_TYPE_TOOLS && artmp->flag & RGN_FLAG_HIDDEN) {
			chan_offs = 16;
			min_x += chan_offs;
			available_w -= chan_offs;
		}
	}

	/* Is there enough space for the execute / cancel buttons? */


	if (is_browse_only) {
		loadbutton = 0;
	}
	else {
		const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
		loadbutton = UI_fontstyle_string_width(fstyle, params->title) + btn_margin;
		CLAMP_MIN(loadbutton, btn_minw);
		if (available_w <= loadbutton + separator + input_minw) {
			loadbutton = 0;
		}
	}

	if (loadbutton) {
		line1_w -= (loadbutton + separator);
		line2_w  = line1_w;
	}

	/* Is there enough space for file number increment/decrement buttons? */
	fnumbuttons = 2 * btn_fn_w;
	if (!loadbutton || line2_w <= fnumbuttons + separator + input_minw) {
		fnumbuttons = 0;
	}
	else {
		line2_w -= (fnumbuttons + separator);
	}

	/* Text input fields for directory and file. */
	if (available_w > 0) {
		const struct direntry *file = sfile->files ? filelist_file(sfile->files, params->active_file) : NULL;
		int overwrite_alert = file_draw_check_exists(sfile);
		const bool is_active_dir = file && file->path && BLI_is_dir(file->path);

		/* callbacks for operator check functions */
		UI_block_func_set(block, file_draw_check_cb, NULL, NULL);

		but = uiDefBut(block, UI_BTYPE_TEXT, -1, "",
		               min_x, line1_y, line1_w - chan_offs, btn_h,
		               params->dir, 0.0, (float)FILE_MAX, 0, 0,
		               TIP_("File path"));
		UI_but_func_complete_set(but, autocomplete_directory, NULL);
		UI_but_flag_enable(but, UI_BUT_NO_UTF8);
		UI_but_flag_disable(but, UI_BUT_UNDO);
		UI_but_funcN_set(but, file_directory_enter_handle, NULL, but);

		/* TODO, directory editing is non-functional while a library is loaded
		 * until this is properly supported just disable it. */
		if (sfile->files && filelist_lib(sfile->files))
			UI_but_flag_enable(but, UI_BUT_DISABLED);

		if ((params->flag & FILE_DIRSEL_ONLY) == 0) {
			but = uiDefBut(block, UI_BTYPE_TEXT, -1, "",
			               min_x, line2_y, line2_w - chan_offs, btn_h,
			               is_active_dir ? (char *)"" : params->file,
			               0.0, (float)FILE_MAXFILE, 0, 0,
			               TIP_(overwrite_alert ? N_("File name, overwrite existing") : N_("File name")));
			UI_but_func_complete_set(but, autocomplete_file, NULL);
			UI_but_flag_enable(but, UI_BUT_NO_UTF8);
			UI_but_flag_disable(but, UI_BUT_UNDO);
			/* silly workaround calling NFunc to ensure this does not get called
			 * immediate ui_apply_but_func but only after button deactivates */
			UI_but_funcN_set(but, file_filename_enter_handle, NULL, but);

			/* check if this overrides a file and if the operator option is used */
			if (overwrite_alert) {
				UI_but_flag_enable(but, UI_BUT_REDALERT);
			}
		}
		
		/* clear func */
		UI_block_func_set(block, NULL, NULL, NULL);
	}
	
	/* Filename number increment / decrement buttons. */
	if (fnumbuttons && (params->flag & FILE_DIRSEL_ONLY) == 0) {
		UI_block_align_begin(block);
		but = uiDefIconButO(block, UI_BTYPE_BUT, "FILE_OT_filenum", 0, ICON_ZOOMOUT,
		                    min_x + line2_w + separator - chan_offs, line2_y,
		                    btn_fn_w, btn_h,
		                    TIP_("Decrement the filename number"));
		RNA_int_set(UI_but_operator_ptr_get(but), "increment", -1);

		but = uiDefIconButO(block, UI_BTYPE_BUT, "FILE_OT_filenum", 0, ICON_ZOOMIN,
		                    min_x + line2_w + separator + btn_fn_w - chan_offs, line2_y,
		                    btn_fn_w, btn_h,
		                    TIP_("Increment the filename number"));
		RNA_int_set(UI_but_operator_ptr_get(but), "increment", 1);
		UI_block_align_end(block);
	}
	
	/* Execute / cancel buttons. */
	if (loadbutton) {
		const struct direntry *file = filelist_file(sfile->files, params->active_file);
		const char *str_exec = (file && file->path && BLI_is_dir(file->path)) ?
		                        /* params->title is already translated! */
		                        IFACE_("Open Directory") : params->title;

		uiDefButO(block, UI_BTYPE_BUT, "FILE_OT_execute", WM_OP_EXEC_REGION_WIN, str_exec,
		          max_x - loadbutton, line1_y, loadbutton, btn_h, "");
		uiDefButO(block, UI_BTYPE_BUT, "FILE_OT_cancel", WM_OP_EXEC_REGION_WIN, IFACE_("Cancel"),
		          max_x - loadbutton, line2_y, loadbutton, btn_h, "");
	}
	
	UI_block_end(C, block);
	UI_block_draw(C, block);
}
Esempio n. 6
0
/* driver settings for active F-Curve (only for 'Drivers' mode) */
static void graph_panel_drivers(const bContext *C, Panel *pa)
{
	bAnimListElem *ale;
	FCurve *fcu;
	ChannelDriver *driver;
	DriverVar *dvar;
	
	PointerRNA driver_ptr;
	uiLayout *col;
	uiBlock *block;
	uiBut *but;
	
	/* Get settings from context */
	if (!graph_panel_context(C, &ale, &fcu))
		return;
	driver = fcu->driver;
	
	/* set event handler for panel */
	block = uiLayoutGetBlock(pa->layout); // xxx?
	UI_block_func_handle_set(block, do_graph_region_driver_buttons, NULL);
	
	/* general actions - management */
	col = uiLayoutColumn(pa->layout, false);
	block = uiLayoutGetBlock(col);
	but = uiDefIconTextBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_FILE_REFRESH, IFACE_("Update Dependencies"),
	               0, 0, 10 * UI_UNIT_X, UI_UNIT_Y,
	               NULL, 0.0, 0.0, 0, 0,
	               TIP_("Force updates of dependencies"));
	UI_but_func_set(but, driver_update_flags_cb, fcu, NULL);

	but = uiDefIconTextBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_ZOOMOUT, IFACE_("Remove Driver"),
	               0, 0, 10 * UI_UNIT_X, UI_UNIT_Y,
	               NULL, 0.0, 0.0, 0, 0,
	               TIP_("Remove this driver"));
	UI_but_funcN_set(but, driver_remove_cb, MEM_dupallocN(ale), NULL);
		
	/* driver-level settings - type, expressions, and errors */
	RNA_pointer_create(ale->id, &RNA_Driver, driver, &driver_ptr);
	
	col = uiLayoutColumn(pa->layout, true);
	block = uiLayoutGetBlock(col);
	uiItemR(col, &driver_ptr, "type", 0, NULL, ICON_NONE);

	/* show expression box if doing scripted drivers, and/or error messages when invalid drivers exist */
	if (driver->type == DRIVER_TYPE_PYTHON) {
		bool bpy_data_expr_error = (strstr(driver->expression, "bpy.data.") != NULL);
		bool bpy_ctx_expr_error  = (strstr(driver->expression, "bpy.context.") != NULL);
		
		/* expression */
		uiItemR(col, &driver_ptr, "expression", 0, IFACE_("Expr"), ICON_NONE);
		
		/* errors? */
		if ((G.f & G_SCRIPT_AUTOEXEC) == 0) {
			uiItemL(col, IFACE_("ERROR: Python auto-execution disabled"), ICON_CANCEL);
		}
		else if (driver->flag & DRIVER_FLAG_INVALID) {
			uiItemL(col, IFACE_("ERROR: Invalid Python expression"), ICON_CANCEL);
		}
		
		/* Explicit bpy-references are evil. Warn about these to prevent errors */
		/* TODO: put these in a box? */
		if (bpy_data_expr_error || bpy_ctx_expr_error) {
			uiItemL(col, IFACE_("WARNING: Driver expression may not work correctly"), ICON_HELP);
			
			if (bpy_data_expr_error) {
				uiItemL(col, IFACE_("TIP: Use variables instead of bpy.data paths (see below)"), ICON_ERROR);
			}
			if (bpy_ctx_expr_error) {
				uiItemL(col, IFACE_("TIP: bpy.context is not safe for renderfarm usage"), ICON_ERROR);
			}
		}
	}
	else {
		/* errors? */
		if (driver->flag & DRIVER_FLAG_INVALID)
			uiItemL(col, IFACE_("ERROR: Invalid target channel(s)"), ICON_ERROR);
			
		/* Warnings about a lack of variables
		 * NOTE: The lack of variables is generally a bad thing, since it indicates
		 *       that the driver doesn't work at all. This particular scenario arises
		 *       primarily when users mistakenly try to use drivers for procedural
		 *       property animation
		 */
		if (BLI_listbase_is_empty(&driver->variables)) {
			uiItemL(col, IFACE_("ERROR: Driver is useless without any inputs"), ICON_ERROR);
			
			if (!BLI_listbase_is_empty(&fcu->modifiers)) {
				uiItemL(col, IFACE_("TIP: Use F-Curves for procedural animation instead"), ICON_INFO);
				uiItemL(col, IFACE_("F-Modifiers can generate curves for those too"), ICON_INFO);
			}
		}
	}
		
	col = uiLayoutColumn(pa->layout, true);

	if (driver->type == DRIVER_TYPE_PYTHON) {
		uiItemR(col, &driver_ptr, "use_self", 0, NULL, ICON_NONE);
	}

	/* debug setting */
	uiItemR(col, &driver_ptr, "show_debug_info", 0, NULL, ICON_NONE);
		
	/* value of driver */
	if (driver->flag & DRIVER_FLAG_SHOWDEBUG) {
		uiLayout *row = uiLayoutRow(col, true);
		char valBuf[32];
			
		uiItemL(row, IFACE_("Driver Value:"), ICON_NONE);
			
		BLI_snprintf(valBuf, sizeof(valBuf), "%.3f", driver->curval);
		uiItemL(row, valBuf, ICON_NONE);
	}
	
	/* add/copy/paste driver variables */
	{
		uiLayout *row;
		
		/* add driver variable */
		row = uiLayoutRow(pa->layout, false);
		block = uiLayoutGetBlock(row);
		but = uiDefIconTextBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_ZOOMIN, IFACE_("Add Variable"),
	                           0, 0, 10 * UI_UNIT_X, UI_UNIT_Y,
	                           NULL, 0.0, 0.0, 0, 0,
	                           TIP_("Driver variables ensure that all dependencies will be accounted for and that drivers will update correctly"));
		UI_but_func_set(but, driver_add_var_cb, driver, NULL);
		
		/* copy/paste (as sub-row) */
		row = uiLayoutRow(row, true);
		block = uiLayoutGetBlock(row);
		
		uiItemO(row, "", ICON_COPYDOWN, "GRAPH_OT_driver_variables_copy");
		uiItemO(row, "", ICON_PASTEDOWN, "GRAPH_OT_driver_variables_paste");
	}
	
	/* loop over targets, drawing them */
	for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
		PointerRNA dvar_ptr;
		uiLayout *box, *row;
		uiLayout *subrow, *sub;
		
		/* sub-layout column for this variable's settings */
		col = uiLayoutColumn(pa->layout, true);
		
		/* 1) header panel */
		box = uiLayoutBox(col);
		RNA_pointer_create(ale->id, &RNA_DriverVariable, dvar, &dvar_ptr);
		
		row = uiLayoutRow(box, false);
		block = uiLayoutGetBlock(row);
		
		/* 1.1) variable type and name */
		subrow = uiLayoutRow(row, true);
		
		/* 1.1.1) variable type */
		sub = uiLayoutRow(subrow, true);                     /* HACK: special group just for the enum, otherwise we */
		uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT);  /*       we get ugly layout with text included too...  */
		
		uiItemR(sub, &dvar_ptr, "type", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
		
		/* 1.1.2) variable name */
		sub = uiLayoutRow(subrow, true);                       /* HACK: special group to counteract the effects of the previous */
		uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_EXPAND);  /*       enum, which now pushes everything too far right         */
		
		uiItemR(sub, &dvar_ptr, "name", 0, "", ICON_NONE);
		
		/* 1.2) invalid name? */
		UI_block_emboss_set(block, UI_EMBOSS_NONE);
		
		if (dvar->flag & DVAR_FLAG_INVALID_NAME) {
			but = uiDefIconBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_ERROR, 290, 0, UI_UNIT_X, UI_UNIT_Y,
			                   NULL, 0.0, 0.0, 0.0, 0.0, IFACE_("Invalid variable name, click here for details"));
			UI_but_func_set(but, driver_dvar_invalid_name_query_cb, dvar, NULL); // XXX: reports?
		}
		
		/* 1.3) remove button */
		but = uiDefIconBut(block, UI_BTYPE_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"));
		UI_but_func_set(but, driver_delete_var_cb, driver, dvar);
		UI_block_emboss_set(block, UI_EMBOSS);
		
		
		/* 2) variable type settings */
		box = uiLayoutBox(col);
		/* controls to draw depends on the type of variable */
		switch (dvar->type) {
			case DVAR_TYPE_SINGLE_PROP:     /* single property */
				graph_panel_driverVar__singleProp(box, ale->id, dvar);
				break;
			case DVAR_TYPE_ROT_DIFF:     /* rotational difference */
				graph_panel_driverVar__rotDiff(box, ale->id, dvar);
				break;
			case DVAR_TYPE_LOC_DIFF:     /* location difference */
				graph_panel_driverVar__locDiff(box, ale->id, dvar);
				break;
			case DVAR_TYPE_TRANSFORM_CHAN:     /* transform channel */
				graph_panel_driverVar__transChan(box, ale->id, dvar);
				break;
		}
		
		/* 3) value of variable */
		if (driver->flag & DRIVER_FLAG_SHOWDEBUG) {
			char valBuf[32];
			
			box = uiLayoutBox(col);
			row = uiLayoutRow(box, true);
			uiItemL(row, IFACE_("Value:"), ICON_NONE);
			
			if ((dvar->type == DVAR_TYPE_ROT_DIFF) ||
			    (dvar->type == DVAR_TYPE_TRANSFORM_CHAN &&
			     dvar->targets[0].transChan >= DTAR_TRANSCHAN_ROTX &&
			     dvar->targets[0].transChan < DTAR_TRANSCHAN_SCALEX))
			{
				BLI_snprintf(valBuf, sizeof(valBuf), "%.3f (%4.1f°)", dvar->curval, RAD2DEGF(dvar->curval));
			}
			else {
				BLI_snprintf(valBuf, sizeof(valBuf), "%.3f", dvar->curval);
			}
			
			uiItemL(row, valBuf, ICON_NONE);
		}
	}
	
	/* cleanup */
	MEM_freeN(ale);
}