2f4e7648a5f730315c13becd6161f9aeb23bbf4a
[blender.git] / source / blender / editors / space_node / node_select.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Blender Foundation, Nathan Letwory
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_node/node_select.c
28  *  \ingroup spnode
29  */
30
31 #include "DNA_node_types.h"
32
33 #include "BKE_context.h"
34 #include "BKE_main.h"
35 #include "BKE_node.h"
36
37 #include "BLI_rect.h"
38 #include "BLI_utildefines.h"
39
40 #include "ED_node.h"  /* own include */
41 #include "ED_screen.h"
42 #include "ED_types.h"
43
44 #include "RNA_access.h"
45 #include "RNA_define.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "UI_view2d.h"
51  
52 #include "node_intern.h"  /* own include */
53
54 /* ****** helpers ****** */
55
56 static bNode *node_under_mouse_select(bNodeTree *ntree, int mx, int my)
57 {
58         bNode *node;
59         
60         for (node = ntree->nodes.last; node; node = node->prev) {
61                 if (node->typeinfo->select_area_func) {
62                         if (node->typeinfo->select_area_func(node, mx, my))
63                                 return node;
64                 }
65         }
66         return NULL;
67 }
68
69 static bNode *node_under_mouse_tweak(bNodeTree *ntree, int mx, int my)
70 {
71         bNode *node;
72         
73         for (node = ntree->nodes.last; node; node = node->prev) {
74                 if (node->typeinfo->tweak_area_func) {
75                         if (node->typeinfo->tweak_area_func(node, mx, my))
76                                 return node;
77                 }
78         }
79         return NULL;
80 }
81
82 void node_select(bNode *node)
83 {
84         node->flag |= SELECT;
85 }
86
87 void node_deselect(bNode *node)
88 {
89         bNodeSocket *sock;
90         
91         node->flag &= ~SELECT;
92         
93         /* deselect sockets too */
94         for (sock = node->inputs.first; sock; sock = sock->next)
95                 sock->flag &= ~SELECT;
96         for (sock = node->outputs.first; sock; sock = sock->next)
97                 sock->flag &= ~SELECT;
98 }
99
100 static void node_toggle(bNode *node)
101 {
102         if (node->flag & SELECT)
103                 node_deselect(node);
104         else
105                 node_select(node);
106 }
107
108 void node_socket_select(bNode *node, bNodeSocket *sock)
109 {
110         sock->flag |= SELECT;
111         
112         /* select node too */
113         if (node)
114                 node->flag |= SELECT;
115 }
116
117 void node_socket_deselect(bNode *node, bNodeSocket *sock, int deselect_node)
118 {
119         sock->flag &= ~SELECT;
120         
121         if (node && deselect_node) {
122                 int sel = 0;
123                 
124                 /* if no selected sockets remain, also deselect the node */
125                 for (sock = node->inputs.first; sock; sock = sock->next) {
126                         if (sock->flag & SELECT) {
127                                 sel = 1;
128                                 break;
129                         }
130                 }
131                 for (sock = node->outputs.first; sock; sock = sock->next) {
132                         if (sock->flag & SELECT) {
133                                 sel = 1;
134                                 break;
135                         }
136                 }
137                 
138                 if (!sel)
139                         node->flag &= ~SELECT;
140         }
141 }
142
143 static void node_socket_toggle(bNode *node, bNodeSocket *sock, int deselect_node)
144 {
145         if (sock->flag & SELECT)
146                 node_socket_deselect(node, sock, deselect_node);
147         else
148                 node_socket_select(node, sock);
149 }
150
151 /* no undo here! */
152 void node_deselect_all(SpaceNode *snode)
153 {
154         bNode *node;
155         
156         for (node = snode->edittree->nodes.first; node; node = node->next)
157                 node_deselect(node);
158 }
159
160 void node_deselect_all_input_sockets(SpaceNode *snode, int deselect_nodes)
161 {
162         bNode *node;
163         bNodeSocket *sock;
164         
165         /* XXX not calling node_socket_deselect here each time, because this does iteration
166          * over all node sockets internally to check if the node stays selected.
167          * We can do that more efficiently here.
168          */
169         
170         for (node = snode->edittree->nodes.first; node; node = node->next) {
171                 int sel = 0;
172                 
173                 for (sock = node->inputs.first; sock; sock = sock->next)
174                         sock->flag &= ~SELECT;
175                 
176                 /* if no selected sockets remain, also deselect the node */
177                 if (deselect_nodes) {
178                         for (sock = node->outputs.first; sock; sock = sock->next) {
179                                 if (sock->flag & SELECT) {
180                                         sel = 1;
181                                         break;
182                                 }
183                         }
184                         
185                         if (!sel)
186                                 node->flag &= ~SELECT;
187                 }
188         }
189         
190         for (sock = snode->edittree->outputs.first; sock; sock = sock->next)
191                 sock->flag &= ~SELECT;
192 }
193
194 void node_deselect_all_output_sockets(SpaceNode *snode, int deselect_nodes)
195 {
196         bNode *node;
197         bNodeSocket *sock;
198         
199         /* XXX not calling node_socket_deselect here each time, because this does iteration
200          * over all node sockets internally to check if the node stays selected.
201          * We can do that more efficiently here.
202          */
203         
204         for (node = snode->edittree->nodes.first; node; node = node->next) {
205                 int sel = 0;
206                 
207                 for (sock = node->outputs.first; sock; sock = sock->next)
208                         sock->flag &= ~SELECT;
209                 
210                 /* if no selected sockets remain, also deselect the node */
211                 if (deselect_nodes) {
212                         for (sock = node->inputs.first; sock; sock = sock->next) {
213                                 if (sock->flag & SELECT) {
214                                         sel = 1;
215                                         break;
216                                 }
217                         }
218                         
219                         if (!sel)
220                                 node->flag &= ~SELECT;
221                 }
222         }
223         
224         for (sock = snode->edittree->inputs.first; sock; sock = sock->next)
225                 sock->flag &= ~SELECT;
226 }
227
228 /* return 1 if we need redraw otherwise zero. */
229 int node_select_same_type(SpaceNode *snode)
230 {
231         bNode *nac, *p;
232         int redraw;
233
234         /* search for the active node. */
235         for (nac = snode->edittree->nodes.first; nac; nac = nac->next) {
236                 if (nac->flag & SELECT)
237                         break;
238         }
239
240         /* no active node, return. */
241         if (!nac)
242                 return(0);
243
244         redraw = 0;
245         for (p = snode->edittree->nodes.first; p; p = p->next) {
246                 if (p->type != nac->type && p->flag & SELECT) {
247                         /* if it's selected but different type, unselect */
248                         redraw = 1;
249                         node_deselect(p);
250                 }
251                 else if (p->type == nac->type && (!(p->flag & SELECT))) {
252                         /* if it's the same type and is not selected, select! */
253                         redraw = 1;
254                         node_select(p);
255                 }
256         }
257         return(redraw);
258 }
259
260 /* return 1 if we need redraw, otherwise zero.
261  * dir can be 0 == next or 0 != prev.
262  */
263 int node_select_same_type_np(SpaceNode *snode, int dir)
264 {
265         bNode *nac, *p, *tnode;
266
267         /* search the active one. */
268         for (nac = snode->edittree->nodes.first; nac; nac = nac->next) {
269                 if (nac->flag & SELECT)
270                         break;
271         }
272
273         /* no active node, return. */
274         if (!nac)
275                 return(0);
276
277         if (dir == 0)
278                 p = nac->next;
279         else
280                 p = nac->prev;
281
282         while (p) {
283                 /* Now search the next with the same type. */
284                 if (p->type == nac->type)
285                         break;
286
287                 if (dir == 0)
288                         p = p->next;
289                 else
290                         p = p->prev;
291         }
292
293         if (p) {
294                 for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next)
295                         if (tnode != p)
296                                 node_deselect(tnode);
297                 node_select(p);
298                 return(1);
299         }
300         return(0);
301 }
302
303 void node_select_single(bContext *C, bNode *node)
304 {
305         Main *bmain = CTX_data_main(C);
306         SpaceNode *snode = CTX_wm_space_node(C);
307         bNode *tnode;
308         
309         for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next)
310                 if (tnode != node)
311                         node_deselect(tnode);
312         node_select(node);
313         
314         ED_node_set_active(bmain, snode->edittree, node);
315         
316         ED_node_sort(snode->edittree);
317         
318         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
319 }
320
321 /* ****** Click Select ****** */
322  
323 static int node_mouse_select(Main *bmain, SpaceNode *snode, ARegion *ar, const int mval[2], short extend)
324 {
325         bNode *node, *tnode;
326         bNodeSocket *sock, *tsock;
327         float mx, my;
328         int selected = 0;
329         
330         /* get mouse coordinates in view2d space */
331         UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &mx, &my);
332         /* node_find_indicated_socket uses snode->mx/my */
333         snode->cursor[0] = mx;
334         snode->cursor[1] = my;
335         
336         if (extend) {
337                 /* first do socket selection, these generally overlap with nodes.
338                  * socket selection only in extend mode.
339                  */
340                 if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN)) {
341                         node_socket_toggle(node, sock, 1);
342                         selected = 1;
343                 }
344                 else if (node_find_indicated_socket(snode, &node, &sock, SOCK_OUT)) {
345                         if (sock->flag & SELECT) {
346                                 node_socket_deselect(node, sock, 1);
347                         }
348                         else {
349                                 /* only allow one selected output per node, for sensible linking.
350                                  * allows selecting outputs from different nodes though.
351                                  */
352                                 if (node) {
353                                         for (tsock = node->outputs.first; tsock; tsock = tsock->next)
354                                                 node_socket_deselect(node, tsock, 1);
355                                 }
356                                 node_socket_select(node, sock);
357                         }
358                         selected = 1;
359                 }
360                 else {
361                         /* find the closest visible node */
362                         node = node_under_mouse_select(snode->edittree, mx, my);
363                         
364                         if (node) {
365                                 if ((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0) {
366                                         /* if node is selected but not active make it active
367                                          * before it'll be desleected
368                                          */
369                                         ED_node_set_active(bmain, snode->edittree, node);
370                                 }
371                                 else {
372                                         node_toggle(node);
373                                         ED_node_set_active(bmain, snode->edittree, node);
374                                 }
375
376                                 selected = 1;
377                         }
378                 }
379         }
380         else {  /* extend == 0 */
381                 
382                 /* find the closest visible node */
383                 node = node_under_mouse_select(snode->edittree, mx, my);
384                 
385                 if (node) {
386                         for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next)
387                                 node_deselect(tnode);
388                         node_select(node);
389                         ED_node_set_active(bmain, snode->edittree, node);
390                         selected = 1;
391                 }
392         }
393         
394         /* update node order */
395         if (selected)
396                 ED_node_sort(snode->edittree);
397         
398         return selected;
399 }
400
401 static int node_select_exec(bContext *C, wmOperator *op)
402 {
403         Main *bmain = CTX_data_main(C);
404         SpaceNode *snode = CTX_wm_space_node(C);
405         ARegion *ar = CTX_wm_region(C);
406         int mval[2];
407         short extend;
408         
409         /* get settings from RNA properties for operator */
410         mval[0] = RNA_int_get(op->ptr, "mouse_x");
411         mval[1] = RNA_int_get(op->ptr, "mouse_y");
412         
413         extend = RNA_boolean_get(op->ptr, "extend");
414         
415         /* perform the select */
416         if (node_mouse_select(bmain, snode, ar, mval, extend)) {
417                 /* send notifiers */
418                 WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
419                 
420                 /* allow tweak event to work too */
421                 return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
422         }
423         else {
424                 /* allow tweak event to work too */
425                 return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
426         }
427 }
428
429 static int node_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
430 {
431         RNA_int_set(op->ptr, "mouse_x", event->mval[0]);
432         RNA_int_set(op->ptr, "mouse_y", event->mval[1]);
433
434         return node_select_exec(C, op);
435 }
436
437
438 void NODE_OT_select(wmOperatorType *ot)
439 {
440         /* identifiers */
441         ot->name = "Select";
442         ot->idname = "NODE_OT_select";
443         ot->description = "Select the node under the cursor";
444         
445         /* api callbacks */
446         ot->invoke = node_select_invoke;
447         ot->poll = ED_operator_node_active;
448         
449         /* flags */
450         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
451         
452         /* properties */
453         RNA_def_int(ot->srna, "mouse_x", 0, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
454         RNA_def_int(ot->srna, "mouse_y", 0, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
455         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
456 }
457
458 /* ****** Border Select ****** */
459
460 static int node_borderselect_exec(bContext *C, wmOperator *op)
461 {
462         SpaceNode *snode = CTX_wm_space_node(C);
463         ARegion *ar = CTX_wm_region(C);
464         bNode *node;
465         rcti rect;
466         rctf rectf;
467         int gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
468         int extend = RNA_boolean_get(op->ptr, "extend");
469         
470         WM_operator_properties_border_to_rcti(op, &rect);
471
472         UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
473         UI_view2d_region_to_view(&ar->v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
474         
475         for (node = snode->edittree->nodes.first; node; node = node->next) {
476                 if (BLI_rctf_isect(&rectf, &node->totr, NULL)) {
477                         if (gesture_mode == GESTURE_MODAL_SELECT)
478                                 node_select(node);
479                         else
480                                 node_deselect(node);
481                 }
482                 else if (!extend) {
483                         node_deselect(node);
484                 }
485         }
486         
487         ED_node_sort(snode->edittree);
488         
489         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
490
491         return OPERATOR_FINISHED;
492 }
493
494 static int node_border_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
495 {
496         int tweak = RNA_boolean_get(op->ptr, "tweak");
497         
498         if (tweak) {
499                 /* prevent initiating the border select if the mouse is over a node */
500                 /* this allows border select on empty space, but drag-translate on nodes */
501                 SpaceNode *snode = CTX_wm_space_node(C);
502                 ARegion *ar = CTX_wm_region(C);
503                 float mx, my;
504
505                 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &mx, &my);
506                 
507                 if (node_under_mouse_tweak(snode->edittree, mx, my))
508                         return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
509         }
510         
511         return WM_border_select_invoke(C, op, event);
512 }
513
514 void NODE_OT_select_border(wmOperatorType *ot)
515 {
516         /* identifiers */
517         ot->name = "Border Select";
518         ot->idname = "NODE_OT_select_border";
519         ot->description = "Use box selection to select nodes";
520         
521         /* api callbacks */
522         ot->invoke = node_border_select_invoke;
523         ot->exec = node_borderselect_exec;
524         ot->modal = WM_border_select_modal;
525         ot->cancel = WM_border_select_cancel;
526         
527         ot->poll = ED_operator_node_active;
528         
529         /* flags */
530         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
531         
532         /* rna */
533         WM_operator_properties_gesture_border(ot, TRUE);
534         RNA_def_boolean(ot->srna, "tweak", 0, "Tweak", "Only activate when mouse is not over a node - useful for tweak gesture");
535 }
536
537 /* ****** Select/Deselect All ****** */
538
539 static int node_select_all_exec(bContext *C, wmOperator *op)
540 {
541         SpaceNode *snode = CTX_wm_space_node(C);
542         ListBase *node_lb = &snode->edittree->nodes;
543         bNode *node;
544         int action = RNA_enum_get(op->ptr, "action");
545
546         if (action == SEL_TOGGLE) {
547                 if (ED_node_select_check(node_lb))
548                         action = SEL_DESELECT;
549                 else
550                         action = SEL_SELECT;
551         }
552
553         for (node = node_lb->first; node; node = node->next) {
554                 switch (action) {
555                         case SEL_SELECT:
556                                 node_select(node);
557                                 break;
558                         case SEL_DESELECT:
559                                 node_deselect(node);
560                                 break;
561                         case SEL_INVERT:
562                                 ((node->flag & SELECT) ? node_deselect : node_select)(node);
563                                 break;
564                 }
565         }
566
567         ED_node_sort(snode->edittree);
568         
569         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
570         return OPERATOR_FINISHED;
571 }
572
573 void NODE_OT_select_all(wmOperatorType *ot)
574 {
575         /* identifiers */
576         ot->name = "(De)select All";
577         ot->description = "(De)select all nodes";
578         ot->idname = "NODE_OT_select_all";
579         
580         /* api callbacks */
581         ot->exec = node_select_all_exec;
582         ot->poll = ED_operator_node_active;
583         
584         /* flags */
585         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
586
587         WM_operator_properties_select_all(ot);
588 }
589
590 /* ****** Select Linked To ****** */
591
592 static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
593 {
594         SpaceNode *snode = CTX_wm_space_node(C);
595         bNodeLink *link;
596         bNode *node;
597         
598         for (node = snode->edittree->nodes.first; node; node = node->next)
599                 node->flag &= ~NODE_TEST;
600
601         for (link = snode->edittree->links.first; link; link = link->next) {
602                 if (link->fromnode && link->tonode && (link->fromnode->flag & NODE_SELECT))
603                         link->tonode->flag |= NODE_TEST;
604         }
605         
606         for (node = snode->edittree->nodes.first; node; node = node->next) {
607                 if (node->flag & NODE_TEST)
608                         node_select(node);
609         }
610         
611         ED_node_sort(snode->edittree);
612         
613         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
614         return OPERATOR_FINISHED;
615 }
616
617 void NODE_OT_select_linked_to(wmOperatorType *ot)
618 {
619         /* identifiers */
620         ot->name = "Select Linked To";
621         ot->description = "Select nodes linked to the selected ones";
622         ot->idname = "NODE_OT_select_linked_to";
623         
624         /* api callbacks */
625         ot->exec = node_select_linked_to_exec;
626         ot->poll = ED_operator_node_active;
627         
628         /* flags */
629         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
630 }
631
632 /* ****** Select Linked From ****** */
633
634 static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
635 {
636         SpaceNode *snode = CTX_wm_space_node(C);
637         bNodeLink *link;
638         bNode *node;
639         
640         for (node = snode->edittree->nodes.first; node; node = node->next)
641                 node->flag &= ~NODE_TEST;
642
643         for (link = snode->edittree->links.first; link; link = link->next) {
644                 if (link->fromnode && link->tonode && (link->tonode->flag & NODE_SELECT))
645                         link->fromnode->flag |= NODE_TEST;
646         }
647         
648         for (node = snode->edittree->nodes.first; node; node = node->next) {
649                 if (node->flag & NODE_TEST)
650                         node_select(node);
651         }
652         
653         ED_node_sort(snode->edittree);
654         
655         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
656         return OPERATOR_FINISHED;
657 }
658
659 void NODE_OT_select_linked_from(wmOperatorType *ot)
660 {
661         /* identifiers */
662         ot->name = "Select Linked From";
663         ot->description = "Select nodes linked from the selected ones";
664         ot->idname = "NODE_OT_select_linked_from";
665         
666         /* api callbacks */
667         ot->exec = node_select_linked_from_exec;
668         ot->poll = ED_operator_node_active;
669         
670         /* flags */
671         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
672 }
673
674 /* ****** Select Same Type ****** */
675
676 static int node_select_same_type_exec(bContext *C, wmOperator *UNUSED(op))
677 {
678         SpaceNode *snode = CTX_wm_space_node(C);
679
680         node_select_same_type(snode);
681
682         ED_node_sort(snode->edittree);
683
684         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
685         return OPERATOR_FINISHED;
686 }
687
688 void NODE_OT_select_same_type(wmOperatorType *ot)
689 {
690         /* identifiers */
691         ot->name = "Select Same Type";
692         ot->description = "Select all the nodes of the same type";
693         ot->idname = "NODE_OT_select_same_type";
694         
695         /* api callbacks */
696         ot->exec = node_select_same_type_exec;
697         ot->poll = ED_operator_node_active;
698         
699         /* flags */
700         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
701 }
702
703 /* ****** Select The Next/Prev Node Of The Same Type ****** */
704
705 static int node_select_same_type_next_exec(bContext *C, wmOperator *UNUSED(op))
706 {
707         SpaceNode *snode = CTX_wm_space_node(C);
708
709         node_select_same_type_np(snode, 0);
710
711         ED_node_sort(snode->edittree);
712
713         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
714
715         return OPERATOR_FINISHED;
716 }
717
718 void NODE_OT_select_same_type_next(wmOperatorType *ot)
719 {
720         /* identifiers */
721         ot->name = "Select Same Type Next";
722         ot->description = "Select the next node of the same type";
723         ot->idname = "NODE_OT_select_same_type_next";
724         
725         /* api callbacks */
726         ot->exec = node_select_same_type_next_exec;
727         ot->poll = ED_operator_node_active;
728         
729         /* flags */
730         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
731 }
732
733 static int node_select_same_type_prev_exec(bContext *C, wmOperator *UNUSED(op))
734 {
735         SpaceNode *snode = CTX_wm_space_node(C);
736
737         node_select_same_type_np(snode, 1);
738
739         ED_node_sort(snode->edittree);
740
741         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
742         return OPERATOR_FINISHED;
743 }
744
745 void NODE_OT_select_same_type_prev(wmOperatorType *ot)
746 {
747         /* identifiers */
748         ot->name = "Select Same Type Prev";
749         ot->description = "Select the prev node of the same type";
750         ot->idname = "NODE_OT_select_same_type_prev";
751         
752         /* api callbacks */
753         ot->exec = node_select_same_type_prev_exec;
754         ot->poll = ED_operator_node_active;
755         
756         /* flags */
757         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
758 }