Bugfix #35920
authorTon Roosendaal <ton@blender.org>
Thu, 5 Sep 2013 13:03:03 +0000 (13:03 +0000)
committerTon Roosendaal <ton@blender.org>
Thu, 5 Sep 2013 13:03:03 +0000 (13:03 +0000)
Adding a new node in Node Editor failed for "High DPI" (Only Mac retina now).

- Py script for adding nodes was doing dpi magic, which it shouldn't. It has
  been replaced with a (temporary) API call to set the correct cursor location.
  (Thanks to Lukas T for helping here)

- The SpaceNode->cursor[2] property now is *only* storing the coordinate
  in "adding new node space". Use of this has been removed from the code where
  possible, with as only exception the code to draw noodles while adding them.

Special coder note: Nodes should respect the DPI value, and draw larger with
larger buttons if you increase this size. The hack here is that this can only
work nice if also the node positions are scaled accordingly.

A better fix could be to check on scaling the node view itself for it. That
then would also remove this Python API call that was added in this commit.
However, that again might fight with how buttons layout code works now...
needs some careful checking.

release/scripts/startup/bl_operators/node.py
source/blender/editors/space_node/drawnode.c
source/blender/editors/space_node/node_add.c
source/blender/editors/space_node/node_draw.c
source/blender/editors/space_node/node_edit.c
source/blender/editors/space_node/node_intern.h
source/blender/editors/space_node/node_relationships.c
source/blender/editors/space_node/node_select.c
source/blender/editors/space_node/space_node.c
source/blender/makesrna/intern/rna_space.c

index c70b5832bfb9e853fe240a453778a5678a070305..16349c27e3965d0692d4fdb5ab1428f95e8c9232 100644 (file)
@@ -66,12 +66,8 @@ class NodeAddOperator():
 
         # convert mouse position to the View2D for later node placement
         if context.region.type == 'WINDOW':
-            # XXX, temp fix for [#35920], still fails for (U.pixelsize != 1)
-            dpi_fac = context.user_preferences.system.dpi / 72.0
-            space.cursor_location = v2d.region_to_view(event.mouse_region_x,
-                                                       event.mouse_region_y)
-            space.cursor_location /= dpi_fac
-
+            # convert mouse position to the View2D for later node placement
+            space.cursor_location_from_region(event.mouse_region_x, event.mouse_region_y) 
         else:
             space.cursor_location = tree.view_center
 
index 7576e4791e1ff8cb8a20e0a2c7695b3ae074720c..4e98c9fd8940c85e6e237d0662ee445d59f5a7b6 100644 (file)
@@ -3075,7 +3075,16 @@ int node_link_bezier_points(View2D *v2d, SpaceNode *snode, bNodeLink *link, floa
 {
        float dist, vec[4][2];
        float deltax, deltay;
+       float cursor[2] = {0.0f, 0.0f};
        int toreroute, fromreroute;
+       
+       /* this function can be called with snode null (via cut_links_intersect) */
+       /* XXX map snode->cursor back to view space */
+       if (snode) {
+               cursor[0] = snode->cursor[0] * UI_DPI_FAC;
+               cursor[1] = snode->cursor[1] * UI_DPI_FAC;
+       }
+       
        /* in v0 and v3 we put begin/end points */
        if (link->fromsock) {
                vec[0][0] = link->fromsock->locx;
@@ -3084,7 +3093,7 @@ int node_link_bezier_points(View2D *v2d, SpaceNode *snode, bNodeLink *link, floa
        }
        else {
                if (snode == NULL) return 0;
-               copy_v2_v2(vec[0], snode->cursor);
+               copy_v2_v2(vec[0], cursor);
                fromreroute = 0;
        }
        if (link->tosock) {
@@ -3094,7 +3103,7 @@ int node_link_bezier_points(View2D *v2d, SpaceNode *snode, bNodeLink *link, floa
        }
        else {
                if (snode == NULL) return 0;
-               copy_v2_v2(vec[3], snode->cursor);
+               copy_v2_v2(vec[3], cursor);
                toreroute = 0;
        }
 
index 45b0272576aa8973a675399b4e4cc04bb6b32112..007699758937e38508f87fb7140bbeecfd27556d 100644 (file)
@@ -84,10 +84,6 @@ bNode *node_add_node(const bContext *C, const char *idname, int type, float locx
        node->locy = locy + 60.0f;     /* arbitrary... so its visible, (0,0) is top of node */
        nodeSetSelected(node, TRUE);
        
-       /* node location is mapped */
-       locx /= UI_DPI_FAC;
-       locy /= UI_DPI_FAC;
-       
        node->locx = locx;
        node->locy = locy + 60.0f;
        
@@ -417,9 +413,8 @@ static int node_add_mask_poll(bContext *C)
        return ED_operator_node_editable(C) && snode->nodetree->type == NTREE_COMPOSIT;
 }
 
-static int node_add_mask_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int node_add_mask_exec(bContext *C, wmOperator *op)
 {
-       ARegion *ar = CTX_wm_region(C);
        SpaceNode *snode = CTX_wm_space_node(C);
        bNode *node;
        ID *mask = NULL;
@@ -435,9 +430,6 @@ static int node_add_mask_invoke(bContext *C, wmOperator *op, const wmEvent *even
 
        ED_preview_kill_jobs(C);
 
-       /* convert mouse coordinates to v2d space */
-       UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
-                                &snode->cursor[0], &snode->cursor[1]);
        node = node_add_node(C, NULL, CMP_NODE_MASK, snode->cursor[0], snode->cursor[1]);
 
        if (!node) {
@@ -462,7 +454,7 @@ void NODE_OT_add_mask(wmOperatorType *ot)
        ot->idname = "NODE_OT_add_mask";
 
        /* callbacks */
-       ot->invoke = node_add_mask_invoke;
+       ot->exec = node_add_mask_exec;
        ot->poll = node_add_mask_poll;
 
        /* flags */
index dce04bb8c42c6e47c6f57bcf316c5ff0002a34fd..b40a7e4702e879e55ebb34be54c5201c80ca6768 100644 (file)
@@ -1075,31 +1075,31 @@ int node_get_resize_cursor(int directions)
                return CURSOR_EDIT;
 }
 
-void node_set_cursor(wmWindow *win, SpaceNode *snode)
+void node_set_cursor(wmWindow *win, SpaceNode *snode, float cursor[2])
 {
        bNodeTree *ntree = snode->edittree;
        bNode *node;
        bNodeSocket *sock;
-       int cursor = CURSOR_STD;
+       int wmcursor = CURSOR_STD;
        
        if (ntree) {
-               if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN | SOCK_OUT)) {
+               if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN | SOCK_OUT)) {
                        /* pass */
                }
                else {
                        /* check nodes front to back */
                        for (node = ntree->nodes.last; node; node = node->prev) {
-                               if (BLI_rctf_isect_pt(&node->totr, snode->cursor[0], snode->cursor[1]))
+                               if (BLI_rctf_isect_pt(&node->totr, cursor[0], cursor[1]))
                                        break;  /* first hit on node stops */
                        }
                        if (node) {
-                               int dir = node->typeinfo->resize_area_func(node, snode->cursor[0], snode->cursor[1]);
-                               cursor = node_get_resize_cursor(dir);
+                               int dir = node->typeinfo->resize_area_func(node, cursor[0], cursor[1]);
+                               wmcursor = node_get_resize_cursor(dir);
                        }
                }
        }
        
-       WM_cursor_set(win, cursor);
+       WM_cursor_set(win, wmcursor);
 }
 
 void node_draw_default(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey key)
@@ -1251,6 +1251,7 @@ static void draw_group_overlay(const bContext *C, ARegion *ar)
 
 void drawnodespace(const bContext *C, ARegion *ar)
 {
+       wmWindow *win = CTX_wm_window(C);
        View2DScrollers *scrollers;
        SpaceNode *snode = CTX_wm_space_node(C);
        View2D *v2d = &ar->v2d;
@@ -1259,7 +1260,13 @@ void drawnodespace(const bContext *C, ARegion *ar)
        glClear(GL_COLOR_BUFFER_BIT);
 
        UI_view2d_view_ortho(v2d);
-
+       
+       /* XXX snode->cursor set in coordspace for placing new nodes, used for drawing noodles too */
+       UI_view2d_region_to_view(&ar->v2d, win->eventstate->x - ar->winrct.xmin, win->eventstate->y - ar->winrct.ymin,
+                                &snode->cursor[0], &snode->cursor[1]);
+       snode->cursor[0] /= UI_DPI_FAC;
+       snode->cursor[1] /= UI_DPI_FAC;
+       
        ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
 
        /* only set once */
index b7e9cb0268f33c2f2fdcaa2f070163b7f498331f..3ba749a8effeb5f16e91fbb22e08b922d507c25c 100644 (file)
@@ -1056,7 +1056,7 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
 
 /* checks snode->mouse position, and returns found node/socket */
 /* type is SOCK_IN and/or SOCK_OUT */
-int node_find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int in_out)
+int node_find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, float cursor[2], int in_out)
 {
        bNode *node;
        bNodeSocket *sock;
@@ -1068,10 +1068,10 @@ int node_find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **so
        /* check if we click in a socket */
        for (node = snode->edittree->nodes.first; node; node = node->next) {
                
-               rect.xmin = snode->cursor[0] - (NODE_SOCKSIZE + 4);
-               rect.ymin = snode->cursor[1] - (NODE_SOCKSIZE + 4);
-               rect.xmax = snode->cursor[0] + (NODE_SOCKSIZE + 4);
-               rect.ymax = snode->cursor[1] + (NODE_SOCKSIZE + 4);
+               rect.xmin = cursor[0] - (NODE_SOCKSIZE + 4);
+               rect.ymin = cursor[1] - (NODE_SOCKSIZE + 4);
+               rect.xmax = cursor[0] + (NODE_SOCKSIZE + 4);
+               rect.ymax = cursor[1] + (NODE_SOCKSIZE + 4);
                
                if (!(node->flag & NODE_HIDDEN)) {
                        /* extra padding inside and out - allow dragging on the text areas too */
@@ -2087,17 +2087,6 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-static int node_clipboard_paste_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
-       ARegion *ar = CTX_wm_region(C);
-       SpaceNode *snode = CTX_wm_space_node(C);
-
-       /* convert mouse coordinates to v2d space */
-       UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &snode->cursor[0], &snode->cursor[1]);
-
-       return node_clipboard_paste_exec(C, op);
-}
-
 void NODE_OT_clipboard_paste(wmOperatorType *ot)
 {
        /* identifiers */
@@ -2107,7 +2096,6 @@ void NODE_OT_clipboard_paste(wmOperatorType *ot)
 
        /* api callbacks */
        ot->exec = node_clipboard_paste_exec;
-       ot->invoke = node_clipboard_paste_invoke;
        ot->poll = ED_operator_node_editable;
 
        /* flags */
index 6ad0675eadd2727aff927561fe6d9cbfe22c37e8..9b7b00fba00d185299172b42ecbdc98534a5721d 100644 (file)
@@ -84,7 +84,7 @@ void node_draw_nodetree(const struct bContext *C, struct ARegion *ar, struct Spa
                         struct bNodeTree *ntree, bNodeInstanceKey parent_key);
 void drawnodespace(const bContext *C, ARegion *ar);
 
-void node_set_cursor(struct wmWindow *win, struct SpaceNode *snode);
+void node_set_cursor(struct wmWindow *win, struct SpaceNode *snode, float cursor[2]);
        /* DPI scaled coords */
 void node_to_view(struct bNode *node, float x, float y, float *rx, float *ry);
 void node_from_view(struct bNode *node, float x, float y, float *rx, float *ry);
@@ -183,7 +183,7 @@ int composite_node_editable(struct bContext *C);
 int node_has_hidden_sockets(bNode *node);
 void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set);
 int node_render_changed_exec(bContext *, struct wmOperator *);
-int node_find_indicated_socket(struct SpaceNode *snode, struct bNode **nodep, struct bNodeSocket **sockp, int in_out);
+int node_find_indicated_socket(struct SpaceNode *snode, struct bNode **nodep, struct bNodeSocket **sockp, float cursor[2], int in_out);
 
 void NODE_OT_duplicate(struct wmOperatorType *ot);
 void NODE_OT_delete(struct wmOperatorType *ot);
index 297c9c5c86dff92e021bb66dc6f8582eed44ff53..4b5cc9e42b6d48db4764f8842fb7b481c3ff32c9 100644 (file)
@@ -443,18 +443,19 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
        bNodeSocket *tsock = NULL;
        bNodeLink *link;
        LinkData *linkdata;
+       float cursor[2];
        int in_out;
 
        in_out = nldrag->in_out;
        
        UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
-                                &snode->cursor[0], &snode->cursor[1]);
+                                &cursor[0], &cursor[1]);
 
        switch (event->type) {
                case MOUSEMOVE:
                        
                        if (in_out == SOCK_OUT) {
-                               if (node_find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) {
+                               if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) {
                                        for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
                                                link = linkdata->data;
                                                
@@ -480,7 +481,7 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
                                }
                        }
                        else {
-                               if (node_find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) {
+                               if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_OUT)) {
                                        for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
                                                link = linkdata->data;
                                                
@@ -550,7 +551,7 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
 }
 
 /* return 1 when socket clicked */
-static bNodeLinkDrag *node_link_init(SpaceNode *snode, int detach)
+static bNodeLinkDrag *node_link_init(SpaceNode *snode, float cursor[2], int detach)
 {
        bNode *node;
        bNodeSocket *sock;
@@ -560,7 +561,7 @@ static bNodeLinkDrag *node_link_init(SpaceNode *snode, int detach)
        int num_links;
 
        /* output indicated? */
-       if (node_find_indicated_socket(snode, &node, &sock, SOCK_OUT)) {
+       if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
                nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
 
                num_links = nodeCountSocketLinks(snode->edittree, sock);
@@ -596,7 +597,7 @@ static bNodeLinkDrag *node_link_init(SpaceNode *snode, int detach)
                }
        }
        /* or an input? */
-       else if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN)) {
+       else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
                nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
 
                num_links = nodeCountSocketLinks(snode->edittree, sock);
@@ -644,14 +645,16 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
        SpaceNode *snode = CTX_wm_space_node(C);
        ARegion *ar = CTX_wm_region(C);
        bNodeLinkDrag *nldrag;
+       float cursor[2];
+       
        int detach = RNA_boolean_get(op->ptr, "detach");
 
        UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
-                                &snode->cursor[0], &snode->cursor[1]);
+                                &cursor[0], &cursor[1]);
 
        ED_preview_kill_jobs(C);
 
-       nldrag = node_link_init(snode, detach);
+       nldrag = node_link_init(snode, cursor, detach);
 
        if (nldrag) {
                op->customdata = nldrag;
@@ -1065,18 +1068,23 @@ void NODE_OT_join(wmOperatorType *ot)
 
 /* ****************** Attach ******************* */
 
-static int node_attach_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
 {
+       ARegion *ar = CTX_wm_region(C);
        SpaceNode *snode = CTX_wm_space_node(C);
        bNodeTree *ntree = snode->edittree;
        bNode *frame;
+       float cursor[2];
+       
+       /* convert mouse coordinates to v2d space */
+       UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]);
 
        /* check nodes front to back */
        for (frame = ntree->nodes.last; frame; frame = frame->prev) {
                /* skip selected, those are the nodes we want to attach */
                if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT))
                        continue;
-               if (BLI_rctf_isect_pt(&frame->totr, snode->cursor[0], snode->cursor[1]))
+               if (BLI_rctf_isect_pt(&frame->totr, cursor[0], cursor[1]))
                        break;
        }
        if (frame) {
@@ -1116,16 +1124,6 @@ static int node_attach_exec(bContext *C, wmOperator *UNUSED(op))
        return OPERATOR_FINISHED;
 }
 
-static int node_attach_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
-       ARegion *ar = CTX_wm_region(C);
-       SpaceNode *snode = CTX_wm_space_node(C);
-
-       /* convert mouse coordinates to v2d space */
-       UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &snode->cursor[0], &snode->cursor[1]);
-
-       return node_attach_exec(C, op);
-}
 
 void NODE_OT_attach(wmOperatorType *ot)
 {
@@ -1135,7 +1133,7 @@ void NODE_OT_attach(wmOperatorType *ot)
        ot->idname = "NODE_OT_attach";
 
        /* api callbacks */
-       ot->exec = node_attach_exec;
+       
        ot->invoke = node_attach_invoke;
        ot->poll = ED_operator_node_editable;
 
index e17699309ef9bf127f63557776a2427862c1c266..6ce31783bffcc17c0df15971a3b3fbef61e4ca70 100644 (file)
@@ -306,24 +306,21 @@ static int node_mouse_select(Main *bmain, SpaceNode *snode, ARegion *ar, const i
 {
        bNode *node, *tnode;
        bNodeSocket *sock, *tsock;
-       float mx, my;
+       float cursor[2];
        int selected = 0;
        
        /* get mouse coordinates in view2d space */
-       UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &mx, &my);
-       /* node_find_indicated_socket uses snode->mx/my */
-       snode->cursor[0] = mx;
-       snode->cursor[1] = my;
+       UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &cursor[0], &cursor[1]);
        
        if (extend) {
                /* first do socket selection, these generally overlap with nodes.
                 * socket selection only in extend mode.
                 */
-               if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN)) {
+               if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
                        node_socket_toggle(node, sock, 1);
                        selected = 1;
                }
-               else if (node_find_indicated_socket(snode, &node, &sock, SOCK_OUT)) {
+               else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
                        if (sock->flag & SELECT) {
                                node_socket_deselect(node, sock, 1);
                        }
@@ -341,7 +338,7 @@ static int node_mouse_select(Main *bmain, SpaceNode *snode, ARegion *ar, const i
                }
                else {
                        /* find the closest visible node */
-                       node = node_under_mouse_select(snode->edittree, mx, my);
+                       node = node_under_mouse_select(snode->edittree, cursor[0], cursor[1]);
                        
                        if (node) {
                                if ((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0) {
@@ -362,7 +359,7 @@ static int node_mouse_select(Main *bmain, SpaceNode *snode, ARegion *ar, const i
        else {  /* extend == 0 */
                
                /* find the closest visible node */
-               node = node_under_mouse_select(snode->edittree, mx, my);
+               node = node_under_mouse_select(snode->edittree, cursor[0], cursor[1]);
                
                if (node) {
                        for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
index 922912fa5407e61b32b85765bcb2778381fc98ec..a59b2fc21dfdaef60b86a7a0c2b8a9ae5810ad08 100644 (file)
@@ -602,8 +602,14 @@ static void node_cursor(wmWindow *win, ScrArea *sa, ARegion *ar)
        /* convert mouse coordinates to v2d space */
        UI_view2d_region_to_view(&ar->v2d, win->eventstate->x - ar->winrct.xmin, win->eventstate->y - ar->winrct.ymin,
                                 &snode->cursor[0], &snode->cursor[1]);
+       
+       /* here snode->cursor is used to detect the node edge for sizing */
+       node_set_cursor(win, snode, snode->cursor);
 
-       node_set_cursor(win, snode);
+       /* XXX snode->cursor is in placing new nodes space */
+       snode->cursor[0] /= UI_DPI_FAC;
+       snode->cursor[1] /= UI_DPI_FAC;
+       
 }
 
 /* Initialize main area, setting handlers. */
index 29e20c1025e775eb00b167ce739672df7868aff8..5401212261725d9758197a286c0bb458b3b319d1 100644 (file)
@@ -172,6 +172,7 @@ static EnumPropertyItem buttons_texture_context_items[] = {
 #include "DNA_mask_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_screen_types.h"
+#include "DNA_userdef_types.h"
 
 #include "BLI_math.h"
 
@@ -197,6 +198,9 @@ static EnumPropertyItem buttons_texture_context_items[] = {
 
 #include "IMB_imbuf_types.h"
 
+#include "UI_interface.h"
+#include "UI_view2d.h"
+
 static StructRNA *rna_Space_refine(struct PointerRNA *ptr)
 {
        SpaceLink *space = (SpaceLink *)ptr->data;
@@ -1263,6 +1267,15 @@ static void rna_SpaceNodeEditor_show_backdrop_update(Main *UNUSED(bmain), Scene
        WM_main_add_notifier(NC_SCENE | ND_NODES, NULL);
 }
 
+static void rna_SpaceNodeEditor_cursor_location_from_region(SpaceNode *snode, bContext *C, int x, int y)
+{
+       ARegion *ar = CTX_wm_region(C);
+       
+       UI_view2d_region_to_view(&ar->v2d, x, y, &snode->cursor[0], &snode->cursor[1]);
+       snode->cursor[0] /= UI_DPI_FAC;
+       snode->cursor[1] /= UI_DPI_FAC;
+}
+
 static void rna_SpaceClipEditor_clip_set(PointerRNA *ptr, PointerRNA value)
 {
        SpaceClip *sc = (SpaceClip *)(ptr->data);
@@ -3301,7 +3314,8 @@ static void rna_def_space_node_path_api(BlenderRNA *brna, PropertyRNA *cprop)
 static void rna_def_space_node(BlenderRNA *brna)
 {
        StructRNA *srna;
-       PropertyRNA *prop;
+       PropertyRNA *prop, *parm;
+       FunctionRNA *func;
 
        static EnumPropertyItem texture_type_items[] = {
                {SNODE_TEX_OBJECT, "OBJECT", ICON_OBJECT_DATA, "Object", "Edit texture nodes from Object"},
@@ -3442,6 +3456,14 @@ static void rna_def_space_node(BlenderRNA *brna)
        RNA_def_property_float_sdna(prop, NULL, "cursor");
        RNA_def_property_ui_text(prop, "Cursor Location", "Location for adding new nodes");
        RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
+
+       func = RNA_def_function(srna, "cursor_location_from_region", "rna_SpaceNodeEditor_cursor_location_from_region");
+       RNA_def_function_ui_description(func, "Set the cursor location using region coordinates");
+       RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+       parm = RNA_def_int(func, "x", 0, INT_MIN, INT_MAX, "x", "Region x coordinate", -10000, 10000);
+       RNA_def_property_flag(parm, PROP_REQUIRED);
+       parm = RNA_def_int(func, "y", 0, INT_MIN, INT_MAX, "y", "Region y coordinate", -10000, 10000);
+       RNA_def_property_flag(parm, PROP_REQUIRED);
 }
 
 static void rna_def_space_logic(BlenderRNA *brna)