code cleanup: lazy init enum for node search
[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->mx = mx;
334         snode->my = 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         rect.xmin = RNA_int_get(op->ptr, "xmin");
471         rect.ymin = RNA_int_get(op->ptr, "ymin");
472         UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
473         
474         rect.xmax = RNA_int_get(op->ptr, "xmax");
475         rect.ymax = RNA_int_get(op->ptr, "ymax");
476         UI_view2d_region_to_view(&ar->v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
477         
478         for (node = snode->edittree->nodes.first; node; node = node->next) {
479                 if (BLI_rctf_isect(&rectf, &node->totr, NULL)) {
480                         if (gesture_mode == GESTURE_MODAL_SELECT)
481                                 node_select(node);
482                         else
483                                 node_deselect(node);
484                 }
485                 else if (!extend) {
486                         node_deselect(node);
487                 }
488         }
489         
490         ED_node_sort(snode->edittree);
491         
492         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
493
494         return OPERATOR_FINISHED;
495 }
496
497 static int node_border_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
498 {
499         int tweak = RNA_boolean_get(op->ptr, "tweak");
500         
501         if (tweak) {
502                 /* prevent initiating the border select if the mouse is over a node */
503                 /* this allows border select on empty space, but drag-translate on nodes */
504                 SpaceNode *snode = CTX_wm_space_node(C);
505                 ARegion *ar = CTX_wm_region(C);
506                 float mx, my;
507
508                 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &mx, &my);
509                 
510                 if (node_under_mouse_tweak(snode->edittree, mx, my))
511                         return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
512         }
513         
514         return WM_border_select_invoke(C, op, event);
515 }
516
517 void NODE_OT_select_border(wmOperatorType *ot)
518 {
519         /* identifiers */
520         ot->name = "Border Select";
521         ot->idname = "NODE_OT_select_border";
522         ot->description = "Use box selection to select nodes";
523         
524         /* api callbacks */
525         ot->invoke = node_border_select_invoke;
526         ot->exec = node_borderselect_exec;
527         ot->modal = WM_border_select_modal;
528         ot->cancel = WM_border_select_cancel;
529         
530         ot->poll = ED_operator_node_active;
531         
532         /* flags */
533         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
534         
535         /* rna */
536         WM_operator_properties_gesture_border(ot, TRUE);
537         RNA_def_boolean(ot->srna, "tweak", 0, "Tweak", "Only activate when mouse is not over a node - useful for tweak gesture");
538 }
539
540 /* ****** Select/Deselect All ****** */
541
542 static int node_select_all_exec(bContext *C, wmOperator *op)
543 {
544         SpaceNode *snode = CTX_wm_space_node(C);
545         ListBase *node_lb = &snode->edittree->nodes;
546         bNode *node;
547         int action = RNA_enum_get(op->ptr, "action");
548
549         if (action == SEL_TOGGLE) {
550                 if (ED_node_select_check(node_lb))
551                         action = SEL_DESELECT;
552                 else
553                         action = SEL_SELECT;
554         }
555
556         for (node = node_lb->first; node; node = node->next) {
557                 switch (action) {
558                         case SEL_SELECT:
559                                 node_select(node);
560                                 break;
561                         case SEL_DESELECT:
562                                 node_deselect(node);
563                                 break;
564                         case SEL_INVERT:
565                                 ((node->flag & SELECT) ? node_deselect : node_select)(node);
566                                 break;
567                 }
568         }
569
570         ED_node_sort(snode->edittree);
571         
572         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
573         return OPERATOR_FINISHED;
574 }
575
576 void NODE_OT_select_all(wmOperatorType *ot)
577 {
578         /* identifiers */
579         ot->name = "(De)select All";
580         ot->description = "(De)select all nodes";
581         ot->idname = "NODE_OT_select_all";
582         
583         /* api callbacks */
584         ot->exec = node_select_all_exec;
585         ot->poll = ED_operator_node_active;
586         
587         /* flags */
588         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
589
590         WM_operator_properties_select_all(ot);
591 }
592
593 /* ****** Select Linked To ****** */
594
595 static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
596 {
597         SpaceNode *snode = CTX_wm_space_node(C);
598         bNodeLink *link;
599         bNode *node;
600         
601         for (node = snode->edittree->nodes.first; node; node = node->next)
602                 node->flag &= ~NODE_TEST;
603
604         for (link = snode->edittree->links.first; link; link = link->next) {
605                 if (link->fromnode && link->tonode && (link->fromnode->flag & NODE_SELECT))
606                         link->tonode->flag |= NODE_TEST;
607         }
608         
609         for (node = snode->edittree->nodes.first; node; node = node->next) {
610                 if (node->flag & NODE_TEST)
611                         node_select(node);
612         }
613         
614         ED_node_sort(snode->edittree);
615         
616         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
617         return OPERATOR_FINISHED;
618 }
619
620 void NODE_OT_select_linked_to(wmOperatorType *ot)
621 {
622         /* identifiers */
623         ot->name = "Select Linked To";
624         ot->description = "Select nodes linked to the selected ones";
625         ot->idname = "NODE_OT_select_linked_to";
626         
627         /* api callbacks */
628         ot->exec = node_select_linked_to_exec;
629         ot->poll = ED_operator_node_active;
630         
631         /* flags */
632         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
633 }
634
635 /* ****** Select Linked From ****** */
636
637 static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
638 {
639         SpaceNode *snode = CTX_wm_space_node(C);
640         bNodeLink *link;
641         bNode *node;
642         
643         for (node = snode->edittree->nodes.first; node; node = node->next)
644                 node->flag &= ~NODE_TEST;
645
646         for (link = snode->edittree->links.first; link; link = link->next) {
647                 if (link->fromnode && link->tonode && (link->tonode->flag & NODE_SELECT))
648                         link->fromnode->flag |= NODE_TEST;
649         }
650         
651         for (node = snode->edittree->nodes.first; node; node = node->next) {
652                 if (node->flag & NODE_TEST)
653                         node_select(node);
654         }
655         
656         ED_node_sort(snode->edittree);
657         
658         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
659         return OPERATOR_FINISHED;
660 }
661
662 void NODE_OT_select_linked_from(wmOperatorType *ot)
663 {
664         /* identifiers */
665         ot->name = "Select Linked From";
666         ot->description = "Select nodes linked from the selected ones";
667         ot->idname = "NODE_OT_select_linked_from";
668         
669         /* api callbacks */
670         ot->exec = node_select_linked_from_exec;
671         ot->poll = ED_operator_node_active;
672         
673         /* flags */
674         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
675 }
676
677 /* ****** Select Same Type ****** */
678
679 static int node_select_same_type_exec(bContext *C, wmOperator *UNUSED(op))
680 {
681         SpaceNode *snode = CTX_wm_space_node(C);
682
683         node_select_same_type(snode);
684
685         ED_node_sort(snode->edittree);
686
687         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
688         return OPERATOR_FINISHED;
689 }
690
691 void NODE_OT_select_same_type(wmOperatorType *ot)
692 {
693         /* identifiers */
694         ot->name = "Select Same Type";
695         ot->description = "Select all the nodes of the same type";
696         ot->idname = "NODE_OT_select_same_type";
697         
698         /* api callbacks */
699         ot->exec = node_select_same_type_exec;
700         ot->poll = ED_operator_node_active;
701         
702         /* flags */
703         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
704 }
705
706 /* ****** Select The Next/Prev Node Of The Same Type ****** */
707
708 static int node_select_same_type_next_exec(bContext *C, wmOperator *UNUSED(op))
709 {
710         SpaceNode *snode = CTX_wm_space_node(C);
711
712         node_select_same_type_np(snode, 0);
713
714         ED_node_sort(snode->edittree);
715
716         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
717
718         return OPERATOR_FINISHED;
719 }
720
721 void NODE_OT_select_same_type_next(wmOperatorType *ot)
722 {
723         /* identifiers */
724         ot->name = "Select Same Type Next";
725         ot->description = "Select the next node of the same type";
726         ot->idname = "NODE_OT_select_same_type_next";
727         
728         /* api callbacks */
729         ot->exec = node_select_same_type_next_exec;
730         ot->poll = ED_operator_node_active;
731         
732         /* flags */
733         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
734 }
735
736 static int node_select_same_type_prev_exec(bContext *C, wmOperator *UNUSED(op))
737 {
738         SpaceNode *snode = CTX_wm_space_node(C);
739
740         node_select_same_type_np(snode, 1);
741
742         ED_node_sort(snode->edittree);
743
744         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
745         return OPERATOR_FINISHED;
746 }
747
748 void NODE_OT_select_same_type_prev(wmOperatorType *ot)
749 {
750         /* identifiers */
751         ot->name = "Select Same Type Prev";
752         ot->description = "Select the prev node of the same type";
753         ot->idname = "NODE_OT_select_same_type_prev";
754         
755         /* api callbacks */
756         ot->exec = node_select_same_type_prev_exec;
757         ot->poll = ED_operator_node_active;
758         
759         /* flags */
760         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
761 }