lasso select works in the node editor again, Ctrl+Alt+LMB, Ctrl+Alt+Shif+LMB to deselect
[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_lasso.h"
39 #include "BLI_utildefines.h"
40
41 #include "ED_node.h"  /* own include */
42 #include "ED_screen.h"
43 #include "ED_types.h"
44
45 #include "RNA_access.h"
46 #include "RNA_define.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "UI_view2d.h"
52
53 #include "MEM_guardedalloc.h"
54
55 #include "node_intern.h"  /* own include */
56
57 /* ****** helpers ****** */
58
59 static bNode *node_under_mouse_select(bNodeTree *ntree, int mx, int my)
60 {
61         bNode *node;
62         
63         for (node = ntree->nodes.last; node; node = node->prev) {
64                 if (node->typeinfo->select_area_func) {
65                         if (node->typeinfo->select_area_func(node, mx, my))
66                                 return node;
67                 }
68         }
69         return NULL;
70 }
71
72 static bNode *node_under_mouse_tweak(bNodeTree *ntree, int mx, int my)
73 {
74         bNode *node;
75         
76         for (node = ntree->nodes.last; node; node = node->prev) {
77                 if (node->typeinfo->tweak_area_func) {
78                         if (node->typeinfo->tweak_area_func(node, mx, my))
79                                 return node;
80                 }
81         }
82         return NULL;
83 }
84
85 void node_select(bNode *node)
86 {
87         node->flag |= SELECT;
88 }
89
90 void node_deselect(bNode *node)
91 {
92         bNodeSocket *sock;
93         
94         node->flag &= ~SELECT;
95         
96         /* deselect sockets too */
97         for (sock = node->inputs.first; sock; sock = sock->next)
98                 sock->flag &= ~SELECT;
99         for (sock = node->outputs.first; sock; sock = sock->next)
100                 sock->flag &= ~SELECT;
101 }
102
103 static void node_toggle(bNode *node)
104 {
105         if (node->flag & SELECT)
106                 node_deselect(node);
107         else
108                 node_select(node);
109 }
110
111 void node_socket_select(bNode *node, bNodeSocket *sock)
112 {
113         sock->flag |= SELECT;
114         
115         /* select node too */
116         if (node)
117                 node->flag |= SELECT;
118 }
119
120 void node_socket_deselect(bNode *node, bNodeSocket *sock, int deselect_node)
121 {
122         sock->flag &= ~SELECT;
123         
124         if (node && deselect_node) {
125                 int sel = 0;
126                 
127                 /* if no selected sockets remain, also deselect the node */
128                 for (sock = node->inputs.first; sock; sock = sock->next) {
129                         if (sock->flag & SELECT) {
130                                 sel = 1;
131                                 break;
132                         }
133                 }
134                 for (sock = node->outputs.first; sock; sock = sock->next) {
135                         if (sock->flag & SELECT) {
136                                 sel = 1;
137                                 break;
138                         }
139                 }
140                 
141                 if (!sel)
142                         node->flag &= ~SELECT;
143         }
144 }
145
146 static void node_socket_toggle(bNode *node, bNodeSocket *sock, int deselect_node)
147 {
148         if (sock->flag & SELECT)
149                 node_socket_deselect(node, sock, deselect_node);
150         else
151                 node_socket_select(node, sock);
152 }
153
154 /* no undo here! */
155 void node_deselect_all(SpaceNode *snode)
156 {
157         bNode *node;
158         
159         for (node = snode->edittree->nodes.first; node; node = node->next)
160                 node_deselect(node);
161 }
162
163 void node_deselect_all_input_sockets(SpaceNode *snode, int deselect_nodes)
164 {
165         bNode *node;
166         bNodeSocket *sock;
167         
168         /* XXX not calling node_socket_deselect here each time, because this does iteration
169          * over all node sockets internally to check if the node stays selected.
170          * We can do that more efficiently here.
171          */
172         
173         for (node = snode->edittree->nodes.first; node; node = node->next) {
174                 int sel = 0;
175                 
176                 for (sock = node->inputs.first; sock; sock = sock->next)
177                         sock->flag &= ~SELECT;
178                 
179                 /* if no selected sockets remain, also deselect the node */
180                 if (deselect_nodes) {
181                         for (sock = node->outputs.first; sock; sock = sock->next) {
182                                 if (sock->flag & SELECT) {
183                                         sel = 1;
184                                         break;
185                                 }
186                         }
187                         
188                         if (!sel)
189                                 node->flag &= ~SELECT;
190                 }
191         }
192         
193         for (sock = snode->edittree->outputs.first; sock; sock = sock->next)
194                 sock->flag &= ~SELECT;
195 }
196
197 void node_deselect_all_output_sockets(SpaceNode *snode, int deselect_nodes)
198 {
199         bNode *node;
200         bNodeSocket *sock;
201         
202         /* XXX not calling node_socket_deselect here each time, because this does iteration
203          * over all node sockets internally to check if the node stays selected.
204          * We can do that more efficiently here.
205          */
206         
207         for (node = snode->edittree->nodes.first; node; node = node->next) {
208                 int sel = 0;
209                 
210                 for (sock = node->outputs.first; sock; sock = sock->next)
211                         sock->flag &= ~SELECT;
212                 
213                 /* if no selected sockets remain, also deselect the node */
214                 if (deselect_nodes) {
215                         for (sock = node->inputs.first; sock; sock = sock->next) {
216                                 if (sock->flag & SELECT) {
217                                         sel = 1;
218                                         break;
219                                 }
220                         }
221                         
222                         if (!sel)
223                                 node->flag &= ~SELECT;
224                 }
225         }
226         
227         for (sock = snode->edittree->inputs.first; sock; sock = sock->next)
228                 sock->flag &= ~SELECT;
229 }
230
231 /* return 1 if we need redraw otherwise zero. */
232 int node_select_same_type(SpaceNode *snode)
233 {
234         bNode *nac, *p;
235         int redraw;
236
237         /* search for the active node. */
238         for (nac = snode->edittree->nodes.first; nac; nac = nac->next) {
239                 if (nac->flag & SELECT)
240                         break;
241         }
242
243         /* no active node, return. */
244         if (!nac)
245                 return(0);
246
247         redraw = 0;
248         for (p = snode->edittree->nodes.first; p; p = p->next) {
249                 if (p->type != nac->type && p->flag & SELECT) {
250                         /* if it's selected but different type, unselect */
251                         redraw = 1;
252                         node_deselect(p);
253                 }
254                 else if (p->type == nac->type && (!(p->flag & SELECT))) {
255                         /* if it's the same type and is not selected, select! */
256                         redraw = 1;
257                         node_select(p);
258                 }
259         }
260         return(redraw);
261 }
262
263 /* return 1 if we need redraw, otherwise zero.
264  * dir can be 0 == next or 0 != prev.
265  */
266 int node_select_same_type_np(SpaceNode *snode, int dir)
267 {
268         bNode *nac, *p, *tnode;
269
270         /* search the active one. */
271         for (nac = snode->edittree->nodes.first; nac; nac = nac->next) {
272                 if (nac->flag & SELECT)
273                         break;
274         }
275
276         /* no active node, return. */
277         if (!nac)
278                 return(0);
279
280         if (dir == 0)
281                 p = nac->next;
282         else
283                 p = nac->prev;
284
285         while (p) {
286                 /* Now search the next with the same type. */
287                 if (p->type == nac->type)
288                         break;
289
290                 if (dir == 0)
291                         p = p->next;
292                 else
293                         p = p->prev;
294         }
295
296         if (p) {
297                 for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next)
298                         if (tnode != p)
299                                 node_deselect(tnode);
300                 node_select(p);
301                 return(1);
302         }
303         return(0);
304 }
305
306 void node_select_single(bContext *C, bNode *node)
307 {
308         Main *bmain = CTX_data_main(C);
309         SpaceNode *snode = CTX_wm_space_node(C);
310         bNode *tnode;
311         
312         for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next)
313                 if (tnode != node)
314                         node_deselect(tnode);
315         node_select(node);
316         
317         ED_node_set_active(bmain, snode->edittree, node);
318         
319         ED_node_sort(snode->edittree);
320         
321         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
322 }
323
324 /* ****** Click Select ****** */
325  
326 static int node_mouse_select(Main *bmain, SpaceNode *snode, ARegion *ar, const int mval[2], short extend)
327 {
328         bNode *node, *tnode;
329         bNodeSocket *sock, *tsock;
330         float mx, my;
331         int selected = 0;
332         
333         /* get mouse coordinates in view2d space */
334         UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &mx, &my);
335         /* node_find_indicated_socket uses snode->mx/my */
336         snode->cursor[0] = mx;
337         snode->cursor[1] = my;
338         
339         if (extend) {
340                 /* first do socket selection, these generally overlap with nodes.
341                  * socket selection only in extend mode.
342                  */
343                 if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN)) {
344                         node_socket_toggle(node, sock, 1);
345                         selected = 1;
346                 }
347                 else if (node_find_indicated_socket(snode, &node, &sock, SOCK_OUT)) {
348                         if (sock->flag & SELECT) {
349                                 node_socket_deselect(node, sock, 1);
350                         }
351                         else {
352                                 /* only allow one selected output per node, for sensible linking.
353                                  * allows selecting outputs from different nodes though.
354                                  */
355                                 if (node) {
356                                         for (tsock = node->outputs.first; tsock; tsock = tsock->next)
357                                                 node_socket_deselect(node, tsock, 1);
358                                 }
359                                 node_socket_select(node, sock);
360                         }
361                         selected = 1;
362                 }
363                 else {
364                         /* find the closest visible node */
365                         node = node_under_mouse_select(snode->edittree, mx, my);
366                         
367                         if (node) {
368                                 if ((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0) {
369                                         /* if node is selected but not active make it active
370                                          * before it'll be desleected
371                                          */
372                                         ED_node_set_active(bmain, snode->edittree, node);
373                                 }
374                                 else {
375                                         node_toggle(node);
376                                         ED_node_set_active(bmain, snode->edittree, node);
377                                 }
378
379                                 selected = 1;
380                         }
381                 }
382         }
383         else {  /* extend == 0 */
384                 
385                 /* find the closest visible node */
386                 node = node_under_mouse_select(snode->edittree, mx, my);
387                 
388                 if (node) {
389                         for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next)
390                                 node_deselect(tnode);
391                         node_select(node);
392                         ED_node_set_active(bmain, snode->edittree, node);
393                         selected = 1;
394                 }
395         }
396         
397         /* update node order */
398         if (selected)
399                 ED_node_sort(snode->edittree);
400         
401         return selected;
402 }
403
404 static int node_select_exec(bContext *C, wmOperator *op)
405 {
406         Main *bmain = CTX_data_main(C);
407         SpaceNode *snode = CTX_wm_space_node(C);
408         ARegion *ar = CTX_wm_region(C);
409         int mval[2];
410         short extend;
411         
412         /* get settings from RNA properties for operator */
413         mval[0] = RNA_int_get(op->ptr, "mouse_x");
414         mval[1] = RNA_int_get(op->ptr, "mouse_y");
415         
416         extend = RNA_boolean_get(op->ptr, "extend");
417         
418         /* perform the select */
419         if (node_mouse_select(bmain, snode, ar, mval, extend)) {
420                 /* send notifiers */
421                 WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
422                 
423                 /* allow tweak event to work too */
424                 return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
425         }
426         else {
427                 /* allow tweak event to work too */
428                 return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
429         }
430 }
431
432 static int node_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
433 {
434         RNA_int_set(op->ptr, "mouse_x", event->mval[0]);
435         RNA_int_set(op->ptr, "mouse_y", event->mval[1]);
436
437         return node_select_exec(C, op);
438 }
439
440
441 void NODE_OT_select(wmOperatorType *ot)
442 {
443         /* identifiers */
444         ot->name = "Select";
445         ot->idname = "NODE_OT_select";
446         ot->description = "Select the node under the cursor";
447         
448         /* api callbacks */
449         ot->invoke = node_select_invoke;
450         ot->poll = ED_operator_node_active;
451         
452         /* flags */
453         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
454         
455         /* properties */
456         RNA_def_int(ot->srna, "mouse_x", 0, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
457         RNA_def_int(ot->srna, "mouse_y", 0, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
458         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
459 }
460
461 /* ****** Border Select ****** */
462
463 static int node_borderselect_exec(bContext *C, wmOperator *op)
464 {
465         SpaceNode *snode = CTX_wm_space_node(C);
466         ARegion *ar = CTX_wm_region(C);
467         bNode *node;
468         rcti rect;
469         rctf rectf;
470         int gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
471         int extend = RNA_boolean_get(op->ptr, "extend");
472         
473         WM_operator_properties_border_to_rcti(op, &rect);
474
475         UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
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 /* ****** Lasso Select ****** */
541
542 static int do_lasso_select_node(bContext *C, int mcords[][2], short moves, short select)
543 {
544         SpaceNode *snode = CTX_wm_space_node(C);
545         bNode *node;
546
547         ARegion *ar = CTX_wm_region(C);
548
549         rcti rect;
550         int change = FALSE;
551
552         /* get rectangle from operator */
553         BLI_lasso_boundbox(&rect, mcords, moves);
554
555         /* do actual selection */
556         for (node = snode->edittree->nodes.first; node; node = node->next) {
557                 int screen_co[2];
558                 const float cent[2] = {BLI_RCT_CENTER_X(&node->totr),
559                                        BLI_RCT_CENTER_Y(&node->totr)};
560
561                 /* marker in screen coords */
562                 UI_view2d_view_to_region(&ar->v2d,
563                                          cent[0], cent[1],
564                                          &screen_co[0], &screen_co[1]);
565
566                 if (BLI_in_rcti(&rect, screen_co[0], screen_co[1]) &&
567                         BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX))
568                 {
569                         if (select)
570                                 node_select(node);
571                         else
572                                 node_deselect(node);
573
574                         change = TRUE;
575                 }
576         }
577
578         if (change) {
579                 WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
580         }
581
582         return change;
583 }
584
585 static int node_lasso_select_exec(bContext *C, wmOperator *op)
586 {
587         int mcords_tot;
588         int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
589
590         if (mcords) {
591                 short select;
592
593                 select = !RNA_boolean_get(op->ptr, "deselect");
594                 do_lasso_select_node(C, mcords, mcords_tot, select);
595
596                 MEM_freeN(mcords);
597
598                 return OPERATOR_FINISHED;
599         }
600         return OPERATOR_PASS_THROUGH;
601 }
602
603 void NODE_OT_select_lasso(wmOperatorType *ot)
604 {
605         /* identifiers */
606         ot->name = "Lasso Select";
607         ot->description = "Select nodes using lasso selection";
608         ot->idname = "NODE_OT_select_lasso";
609
610         /* api callbacks */
611         ot->invoke = WM_gesture_lasso_invoke;
612         ot->modal = WM_gesture_lasso_modal;
613         ot->exec = node_lasso_select_exec;
614         ot->poll = ED_operator_node_active;
615         ot->cancel = WM_gesture_lasso_cancel;
616
617         /* flags */
618         ot->flag = OPTYPE_UNDO;
619
620         /* properties */
621         RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
622         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items");
623         RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
624 }
625
626 /* ****** Select/Deselect All ****** */
627
628 static int node_select_all_exec(bContext *C, wmOperator *op)
629 {
630         SpaceNode *snode = CTX_wm_space_node(C);
631         ListBase *node_lb = &snode->edittree->nodes;
632         bNode *node;
633         int action = RNA_enum_get(op->ptr, "action");
634
635         if (action == SEL_TOGGLE) {
636                 if (ED_node_select_check(node_lb))
637                         action = SEL_DESELECT;
638                 else
639                         action = SEL_SELECT;
640         }
641
642         for (node = node_lb->first; node; node = node->next) {
643                 switch (action) {
644                         case SEL_SELECT:
645                                 node_select(node);
646                                 break;
647                         case SEL_DESELECT:
648                                 node_deselect(node);
649                                 break;
650                         case SEL_INVERT:
651                                 ((node->flag & SELECT) ? node_deselect : node_select)(node);
652                                 break;
653                 }
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_all(wmOperatorType *ot)
663 {
664         /* identifiers */
665         ot->name = "(De)select All";
666         ot->description = "(De)select all nodes";
667         ot->idname = "NODE_OT_select_all";
668         
669         /* api callbacks */
670         ot->exec = node_select_all_exec;
671         ot->poll = ED_operator_node_active;
672         
673         /* flags */
674         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
675
676         WM_operator_properties_select_all(ot);
677 }
678
679 /* ****** Select Linked To ****** */
680
681 static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
682 {
683         SpaceNode *snode = CTX_wm_space_node(C);
684         bNodeLink *link;
685         bNode *node;
686         
687         for (node = snode->edittree->nodes.first; node; node = node->next)
688                 node->flag &= ~NODE_TEST;
689
690         for (link = snode->edittree->links.first; link; link = link->next) {
691                 if (link->fromnode && link->tonode && (link->fromnode->flag & NODE_SELECT))
692                         link->tonode->flag |= NODE_TEST;
693         }
694         
695         for (node = snode->edittree->nodes.first; node; node = node->next) {
696                 if (node->flag & NODE_TEST)
697                         node_select(node);
698         }
699         
700         ED_node_sort(snode->edittree);
701         
702         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
703         return OPERATOR_FINISHED;
704 }
705
706 void NODE_OT_select_linked_to(wmOperatorType *ot)
707 {
708         /* identifiers */
709         ot->name = "Select Linked To";
710         ot->description = "Select nodes linked to the selected ones";
711         ot->idname = "NODE_OT_select_linked_to";
712         
713         /* api callbacks */
714         ot->exec = node_select_linked_to_exec;
715         ot->poll = ED_operator_node_active;
716         
717         /* flags */
718         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
719 }
720
721 /* ****** Select Linked From ****** */
722
723 static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
724 {
725         SpaceNode *snode = CTX_wm_space_node(C);
726         bNodeLink *link;
727         bNode *node;
728         
729         for (node = snode->edittree->nodes.first; node; node = node->next)
730                 node->flag &= ~NODE_TEST;
731
732         for (link = snode->edittree->links.first; link; link = link->next) {
733                 if (link->fromnode && link->tonode && (link->tonode->flag & NODE_SELECT))
734                         link->fromnode->flag |= NODE_TEST;
735         }
736         
737         for (node = snode->edittree->nodes.first; node; node = node->next) {
738                 if (node->flag & NODE_TEST)
739                         node_select(node);
740         }
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_linked_from(wmOperatorType *ot)
749 {
750         /* identifiers */
751         ot->name = "Select Linked From";
752         ot->description = "Select nodes linked from the selected ones";
753         ot->idname = "NODE_OT_select_linked_from";
754         
755         /* api callbacks */
756         ot->exec = node_select_linked_from_exec;
757         ot->poll = ED_operator_node_active;
758         
759         /* flags */
760         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
761 }
762
763 /* ****** Select Same Type ****** */
764
765 static int node_select_same_type_exec(bContext *C, wmOperator *UNUSED(op))
766 {
767         SpaceNode *snode = CTX_wm_space_node(C);
768
769         node_select_same_type(snode);
770
771         ED_node_sort(snode->edittree);
772
773         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
774         return OPERATOR_FINISHED;
775 }
776
777 void NODE_OT_select_same_type(wmOperatorType *ot)
778 {
779         /* identifiers */
780         ot->name = "Select Same Type";
781         ot->description = "Select all the nodes of the same type";
782         ot->idname = "NODE_OT_select_same_type";
783         
784         /* api callbacks */
785         ot->exec = node_select_same_type_exec;
786         ot->poll = ED_operator_node_active;
787         
788         /* flags */
789         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
790 }
791
792 /* ****** Select The Next/Prev Node Of The Same Type ****** */
793
794 static int node_select_same_type_next_exec(bContext *C, wmOperator *UNUSED(op))
795 {
796         SpaceNode *snode = CTX_wm_space_node(C);
797
798         node_select_same_type_np(snode, 0);
799
800         ED_node_sort(snode->edittree);
801
802         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
803
804         return OPERATOR_FINISHED;
805 }
806
807 void NODE_OT_select_same_type_next(wmOperatorType *ot)
808 {
809         /* identifiers */
810         ot->name = "Select Same Type Next";
811         ot->description = "Select the next node of the same type";
812         ot->idname = "NODE_OT_select_same_type_next";
813         
814         /* api callbacks */
815         ot->exec = node_select_same_type_next_exec;
816         ot->poll = ED_operator_node_active;
817         
818         /* flags */
819         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
820 }
821
822 static int node_select_same_type_prev_exec(bContext *C, wmOperator *UNUSED(op))
823 {
824         SpaceNode *snode = CTX_wm_space_node(C);
825
826         node_select_same_type_np(snode, 1);
827
828         ED_node_sort(snode->edittree);
829
830         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
831         return OPERATOR_FINISHED;
832 }
833
834 void NODE_OT_select_same_type_prev(wmOperatorType *ot)
835 {
836         /* identifiers */
837         ot->name = "Select Same Type Prev";
838         ot->description = "Select the prev node of the same type";
839         ot->idname = "NODE_OT_select_same_type_prev";
840         
841         /* api callbacks */
842         ot->exec = node_select_same_type_prev_exec;
843         ot->poll = ED_operator_node_active;
844         
845         /* flags */
846         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
847 }