Merge branch 'master' into blender2.8
[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 <stdlib.h>
32
33 #include "DNA_node_types.h"
34
35 #include "BLI_utildefines.h"
36 #include "BLI_rect.h"
37 #include "BLI_lasso_2d.h"
38 #include "BLI_math.h"
39 #include "BLI_string.h"
40 #include "BLI_string_utf8.h"
41
42 #include "BKE_context.h"
43 #include "BKE_main.h"
44 #include "BKE_node.h"
45
46 #include "ED_node.h"  /* own include */
47 #include "ED_screen.h"
48 #include "ED_select_utils.h"
49
50 #include "RNA_access.h"
51 #include "RNA_define.h"
52
53 #include "WM_api.h"
54 #include "WM_types.h"
55
56 #include "UI_interface.h"
57 #include "UI_resources.h"
58 #include "UI_view2d.h"
59
60 #include "MEM_guardedalloc.h"
61
62 #include "node_intern.h"  /* own include */
63
64 /* ****** helpers ****** */
65
66 static bNode *node_under_mouse_select(bNodeTree *ntree, int mx, int my)
67 {
68         bNode *node;
69
70         for (node = ntree->nodes.last; node; node = node->prev) {
71                 if (node->typeinfo->select_area_func) {
72                         if (node->typeinfo->select_area_func(node, mx, my))
73                                 return node;
74                 }
75         }
76         return NULL;
77 }
78
79 static bNode *node_under_mouse_tweak(bNodeTree *ntree, int mx, int my)
80 {
81         bNode *node;
82
83         for (node = ntree->nodes.last; node; node = node->prev) {
84                 if (node->typeinfo->tweak_area_func) {
85                         if (node->typeinfo->tweak_area_func(node, mx, my))
86                                 return node;
87                 }
88         }
89         return NULL;
90 }
91
92 static void node_toggle(bNode *node)
93 {
94         nodeSetSelected(node, !(node->flag & SELECT));
95 }
96
97 void node_socket_select(bNode *node, bNodeSocket *sock)
98 {
99         sock->flag |= SELECT;
100
101         /* select node too */
102         if (node)
103                 node->flag |= SELECT;
104 }
105
106 void node_socket_deselect(bNode *node, bNodeSocket *sock, const bool deselect_node)
107 {
108         sock->flag &= ~SELECT;
109
110         if (node && deselect_node) {
111                 bool sel = 0;
112
113                 /* if no selected sockets remain, also deselect the node */
114                 for (sock = node->inputs.first; sock; sock = sock->next) {
115                         if (sock->flag & SELECT) {
116                                 sel = 1;
117                                 break;
118                         }
119                 }
120                 for (sock = node->outputs.first; sock; sock = sock->next) {
121                         if (sock->flag & SELECT) {
122                                 sel = 1;
123                                 break;
124                         }
125                 }
126
127                 if (!sel)
128                         node->flag &= ~SELECT;
129         }
130 }
131
132 static void node_socket_toggle(bNode *node, bNodeSocket *sock, int deselect_node)
133 {
134         if (sock->flag & SELECT)
135                 node_socket_deselect(node, sock, deselect_node);
136         else
137                 node_socket_select(node, sock);
138 }
139
140 /* no undo here! */
141 void node_deselect_all(SpaceNode *snode)
142 {
143         bNode *node;
144
145         for (node = snode->edittree->nodes.first; node; node = node->next)
146                 nodeSetSelected(node, false);
147 }
148
149 void node_deselect_all_input_sockets(SpaceNode *snode, const bool deselect_nodes)
150 {
151         bNode *node;
152         bNodeSocket *sock;
153
154         /* XXX not calling node_socket_deselect here each time, because this does iteration
155          * over all node sockets internally to check if the node stays selected.
156          * We can do that more efficiently here.
157          */
158
159         for (node = snode->edittree->nodes.first; node; node = node->next) {
160                 int sel = 0;
161
162                 for (sock = node->inputs.first; sock; sock = sock->next)
163                         sock->flag &= ~SELECT;
164
165                 /* if no selected sockets remain, also deselect the node */
166                 if (deselect_nodes) {
167                         for (sock = node->outputs.first; sock; sock = sock->next) {
168                                 if (sock->flag & SELECT) {
169                                         sel = 1;
170                                         break;
171                                 }
172                         }
173
174                         if (!sel)
175                                 node->flag &= ~SELECT;
176                 }
177         }
178 }
179
180 void node_deselect_all_output_sockets(SpaceNode *snode, const bool deselect_nodes)
181 {
182         bNode *node;
183         bNodeSocket *sock;
184
185         /* XXX not calling node_socket_deselect here each time, because this does iteration
186          * over all node sockets internally to check if the node stays selected.
187          * We can do that more efficiently here.
188          */
189
190         for (node = snode->edittree->nodes.first; node; node = node->next) {
191                 bool sel = false;
192
193                 for (sock = node->outputs.first; sock; sock = sock->next)
194                         sock->flag &= ~SELECT;
195
196                 /* if no selected sockets remain, also deselect the node */
197                 if (deselect_nodes) {
198                         for (sock = node->inputs.first; sock; sock = sock->next) {
199                                 if (sock->flag & SELECT) {
200                                         sel = 1;
201                                         break;
202                                 }
203                         }
204
205                         if (!sel)
206                                 node->flag &= ~SELECT;
207                 }
208         }
209 }
210
211 /* Return true if we need redraw, otherwise false. */
212
213 static bool node_select_grouped_type(SpaceNode *snode, bNode *node_act)
214 {
215         bNode *node;
216         bool changed = false;
217
218         for (node = snode->edittree->nodes.first; node; node = node->next) {
219                 if ((node->flag & SELECT) == 0) {
220                         if (node->type == node_act->type) {
221                                 nodeSetSelected(node, true);
222                                 changed = true;
223                         }
224                 }
225         }
226
227         return changed;
228 }
229
230 static bool node_select_grouped_color(SpaceNode *snode, bNode *node_act)
231 {
232         bNode *node;
233         bool changed = false;
234
235         for (node = snode->edittree->nodes.first; node; node = node->next) {
236                 if ((node->flag & SELECT) == 0) {
237                         if (compare_v3v3(node->color, node_act->color, 0.005f)) {
238                                 nodeSetSelected(node, true);
239                                 changed = true;
240                         }
241                 }
242         }
243
244         return changed;
245 }
246
247 static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bool from_right)
248 {
249         bNode *node;
250         bool changed = false;
251         const unsigned int delims[] = {'.', '-', '_', '\0'};
252         size_t pref_len_act, pref_len_curr;
253         const char *sep, *suf_act, *suf_curr;
254
255         pref_len_act = BLI_str_partition_ex_utf8(node_act->name, NULL, delims, &sep, &suf_act, from_right);
256
257         /* Note: in case we are searching for suffix, and found none, use whole name as suffix. */
258         if (from_right && !(sep && suf_act)) {
259                 pref_len_act = 0;
260                 suf_act = node_act->name;
261         }
262
263         for (node = snode->edittree->nodes.first; node; node = node->next) {
264                 if (node->flag & SELECT) {
265                         continue;
266                 }
267                 pref_len_curr = BLI_str_partition_ex_utf8(node->name, NULL, delims, &sep, &suf_curr, from_right);
268
269                 /* Same as with active node name! */
270                 if (from_right && !(sep && suf_curr)) {
271                         pref_len_curr = 0;
272                         suf_curr = node->name;
273                 }
274
275                 if ((from_right && STREQ(suf_act, suf_curr)) ||
276                     (!from_right && (pref_len_act == pref_len_curr) && STREQLEN(node_act->name, node->name, pref_len_act)))
277                 {
278                         nodeSetSelected(node, true);
279                         changed = true;
280                 }
281         }
282
283         return changed;
284 }
285
286 enum {
287         NODE_SELECT_GROUPED_TYPE   = 0,
288         NODE_SELECT_GROUPED_COLOR  = 1,
289         NODE_SELECT_GROUPED_PREFIX = 2,
290         NODE_SELECT_GROUPED_SUFIX  = 3,
291 };
292
293 static int node_select_grouped_exec(bContext *C, wmOperator *op)
294 {
295         SpaceNode *snode = CTX_wm_space_node(C);
296         bNode *node_act = nodeGetActive(snode->edittree);
297         bNode *node;
298         bool changed = false;
299         const bool extend = RNA_boolean_get(op->ptr, "extend");
300         const int type = RNA_enum_get(op->ptr, "type");
301
302         if (!extend) {
303                 for (node = snode->edittree->nodes.first; node; node = node->next) {
304                         nodeSetSelected(node, false);
305                 }
306         }
307         nodeSetSelected(node_act, true);
308
309         switch (type) {
310                 case NODE_SELECT_GROUPED_TYPE:
311                         changed = node_select_grouped_type(snode, node_act);
312                         break;
313                 case NODE_SELECT_GROUPED_COLOR:
314                         changed = node_select_grouped_color(snode, node_act);
315                         break;
316                 case NODE_SELECT_GROUPED_PREFIX:
317                         changed = node_select_grouped_name(snode, node_act, false);
318                         break;
319                 case NODE_SELECT_GROUPED_SUFIX:
320                         changed = node_select_grouped_name(snode, node_act, true);
321                         break;
322                 default:
323                         break;
324         }
325
326         if (changed) {
327                 ED_node_sort(snode->edittree);
328                 WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
329                 return OPERATOR_FINISHED;
330         }
331
332         return OPERATOR_CANCELLED;
333 }
334
335 void NODE_OT_select_grouped(wmOperatorType *ot)
336 {
337         static const EnumPropertyItem prop_select_grouped_types[] = {
338                 {NODE_SELECT_GROUPED_TYPE, "TYPE", 0, "Type", ""},
339                 {NODE_SELECT_GROUPED_COLOR, "COLOR", 0, "Color", ""},
340                 {NODE_SELECT_GROUPED_PREFIX, "PREFIX", 0, "Prefix", ""},
341                 {NODE_SELECT_GROUPED_SUFIX, "SUFFIX", 0, "Suffix", ""},
342                 {0, NULL, 0, NULL, NULL}
343         };
344
345         /* identifiers */
346         ot->name = "Select Grouped";
347         ot->description = "Select nodes with similar properties";
348         ot->idname = "NODE_OT_select_grouped";
349
350         /* api callbacks */
351         ot->invoke = WM_menu_invoke;
352         ot->exec = node_select_grouped_exec;
353         ot->poll = ED_operator_node_active;
354
355         /* flags */
356         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
357
358         /* properties */
359         RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting everything first");
360         ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", "");
361 }
362
363 void node_select_single(bContext *C, bNode *node)
364 {
365         Main *bmain = CTX_data_main(C);
366         SpaceNode *snode = CTX_wm_space_node(C);
367         bNode *tnode;
368
369         for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next)
370                 if (tnode != node)
371                         nodeSetSelected(tnode, false);
372         nodeSetSelected(node, true);
373
374         ED_node_set_active(bmain, snode->edittree, node);
375         ED_node_set_active_viewer_key(snode);
376
377         ED_node_sort(snode->edittree);
378
379         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
380 }
381
382 /* ****** Click Select ****** */
383
384 static int node_mouse_select(Main *bmain, SpaceNode *snode, ARegion *ar, const int mval[2], short extend)
385 {
386         bNode *node, *tnode;
387         bNodeSocket *sock, *tsock;
388         float cursor[2];
389         int selected = 0;
390
391         /* get mouse coordinates in view2d space */
392         UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &cursor[0], &cursor[1]);
393
394         if (extend) {
395                 /* first do socket selection, these generally overlap with nodes.
396                  * socket selection only in extend mode.
397                  */
398                 if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
399                         node_socket_toggle(node, sock, 1);
400                         selected = 1;
401                 }
402                 else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
403                         if (sock->flag & SELECT) {
404                                 node_socket_deselect(node, sock, 1);
405                         }
406                         else {
407                                 /* only allow one selected output per node, for sensible linking.
408                                  * allows selecting outputs from different nodes though.
409                                  */
410                                 if (node) {
411                                         for (tsock = node->outputs.first; tsock; tsock = tsock->next)
412                                                 node_socket_deselect(node, tsock, 1);
413                                 }
414                                 node_socket_select(node, sock);
415                         }
416                         selected = 1;
417                 }
418                 else {
419                         /* find the closest visible node */
420                         node = node_under_mouse_select(snode->edittree, cursor[0], cursor[1]);
421
422                         if (node) {
423                                 if ((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0) {
424                                         /* if node is selected but not active make it active
425                                          * before it'll be desleected
426                                          */
427                                         ED_node_set_active(bmain, snode->edittree, node);
428                                 }
429                                 else {
430                                         node_toggle(node);
431                                         ED_node_set_active(bmain, snode->edittree, node);
432                                 }
433
434                                 selected = 1;
435                         }
436                 }
437         }
438         else {  /* extend == 0 */
439
440                 /* find the closest visible node */
441                 node = node_under_mouse_select(snode->edittree, cursor[0], cursor[1]);
442
443                 if (node) {
444                         for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
445                                 nodeSetSelected(tnode, false);
446                         }
447                         nodeSetSelected(node, true);
448                         ED_node_set_active(bmain, snode->edittree, node);
449                         selected = 1;
450                 }
451         }
452
453         /* update node order */
454         if (selected) {
455                 ED_node_set_active_viewer_key(snode);
456                 ED_node_sort(snode->edittree);
457         }
458
459         return selected;
460 }
461
462 static int node_select_exec(bContext *C, wmOperator *op)
463 {
464         Main *bmain = CTX_data_main(C);
465         SpaceNode *snode = CTX_wm_space_node(C);
466         ARegion *ar = CTX_wm_region(C);
467         int mval[2];
468         short extend;
469
470         /* get settings from RNA properties for operator */
471         mval[0] = RNA_int_get(op->ptr, "mouse_x");
472         mval[1] = RNA_int_get(op->ptr, "mouse_y");
473
474         extend = RNA_boolean_get(op->ptr, "extend");
475
476         /* perform the select */
477         if (node_mouse_select(bmain, snode, ar, mval, extend)) {
478                 /* send notifiers */
479                 WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
480
481                 /* allow tweak event to work too */
482                 return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
483         }
484         else {
485                 /* allow tweak event to work too */
486                 return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
487         }
488 }
489
490 static int node_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
491 {
492         RNA_int_set(op->ptr, "mouse_x", event->mval[0]);
493         RNA_int_set(op->ptr, "mouse_y", event->mval[1]);
494
495         return node_select_exec(C, op);
496 }
497
498
499 void NODE_OT_select(wmOperatorType *ot)
500 {
501         /* identifiers */
502         ot->name = "Select";
503         ot->idname = "NODE_OT_select";
504         ot->description = "Select the node under the cursor";
505
506         /* api callbacks */
507         ot->invoke = node_select_invoke;
508         ot->exec = node_select_exec;
509         ot->poll = ED_operator_node_active;
510
511         /* flags */
512         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
513
514         /* properties */
515         RNA_def_int(ot->srna, "mouse_x", 0, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
516         RNA_def_int(ot->srna, "mouse_y", 0, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
517         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
518 }
519
520 /* ****** Border Select ****** */
521
522 static int node_borderselect_exec(bContext *C, wmOperator *op)
523 {
524         SpaceNode *snode = CTX_wm_space_node(C);
525         ARegion *ar = CTX_wm_region(C);
526         bNode *node;
527         rctf rectf;
528         const bool select = !RNA_boolean_get(op->ptr, "deselect");
529         const bool extend = RNA_boolean_get(op->ptr, "extend");
530
531         WM_operator_properties_border_to_rctf(op, &rectf);
532         UI_view2d_region_to_view_rctf(&ar->v2d, &rectf, &rectf);
533
534         for (node = snode->edittree->nodes.first; node; node = node->next) {
535                 bool is_inside;
536                 if (node->type == NODE_FRAME) {
537                         is_inside = BLI_rctf_inside_rctf(&rectf, &node->totr);
538                 }
539                 else {
540                         is_inside = BLI_rctf_isect(&rectf, &node->totr, NULL);
541                 }
542
543                 if (is_inside) {
544                         nodeSetSelected(node, select);
545                 }
546                 else if (!extend) {
547                         nodeSetSelected(node, false);
548                 }
549         }
550
551         ED_node_sort(snode->edittree);
552
553         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
554
555         return OPERATOR_FINISHED;
556 }
557
558 static int node_border_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
559 {
560         const bool tweak = RNA_boolean_get(op->ptr, "tweak");
561
562         if (tweak) {
563                 /* prevent initiating the border select if the mouse is over a node */
564                 /* this allows border select on empty space, but drag-translate on nodes */
565                 SpaceNode *snode = CTX_wm_space_node(C);
566                 ARegion *ar = CTX_wm_region(C);
567                 float mx, my;
568
569                 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &mx, &my);
570
571                 if (node_under_mouse_tweak(snode->edittree, mx, my))
572                         return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
573         }
574
575         return WM_gesture_border_invoke(C, op, event);
576 }
577
578 void NODE_OT_select_border(wmOperatorType *ot)
579 {
580         /* identifiers */
581         ot->name = "Border Select";
582         ot->idname = "NODE_OT_select_border";
583         ot->description = "Use box selection to select nodes";
584
585         /* api callbacks */
586         ot->invoke = node_border_select_invoke;
587         ot->exec = node_borderselect_exec;
588         ot->modal = WM_gesture_border_modal;
589         ot->cancel = WM_gesture_border_cancel;
590
591         ot->poll = ED_operator_node_active;
592
593         /* flags */
594         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
595
596         /* rna */
597         WM_operator_properties_gesture_border_select(ot);
598         RNA_def_boolean(ot->srna, "tweak", 0, "Tweak", "Only activate when mouse is not over a node - useful for tweak gesture");
599 }
600
601 /* ****** Circle Select ****** */
602
603 static int node_circleselect_exec(bContext *C, wmOperator *op)
604 {
605         SpaceNode *snode = CTX_wm_space_node(C);
606         ARegion *ar = CTX_wm_region(C);
607         bNode *node;
608
609         int x, y, radius;
610         float offset[2];
611
612         float zoom  = (float)(BLI_rcti_size_x(&ar->winrct)) / (float)(BLI_rctf_size_x(&ar->v2d.cur));
613
614         const bool select = !RNA_boolean_get(op->ptr, "deselect");
615
616         /* get operator properties */
617         x = RNA_int_get(op->ptr, "x");
618         y = RNA_int_get(op->ptr, "y");
619         radius = RNA_int_get(op->ptr, "radius");
620
621         UI_view2d_region_to_view(&ar->v2d, x, y, &offset[0], &offset[1]);
622
623         for (node = snode->edittree->nodes.first; node; node = node->next) {
624                 if (BLI_rctf_isect_circle(&node->totr, offset, radius / zoom)) {
625                         nodeSetSelected(node, select);
626                 }
627         }
628
629         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
630
631         return OPERATOR_FINISHED;
632 }
633
634 void NODE_OT_select_circle(wmOperatorType *ot)
635 {
636         /* identifiers */
637         ot->name = "Circle Select";
638         ot->idname = "NODE_OT_select_circle";
639         ot->description = "Use circle selection to select nodes";
640
641         /* api callbacks */
642         ot->invoke = WM_gesture_circle_invoke;
643         ot->exec = node_circleselect_exec;
644         ot->modal = WM_gesture_circle_modal;
645
646         ot->poll = ED_operator_node_active;
647
648         /* flags */
649         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
650
651         /* properties */
652         WM_operator_properties_gesture_circle_select(ot);
653 }
654
655 /* ****** Lasso Select ****** */
656
657 static bool do_lasso_select_node(bContext *C, const int mcords[][2], short moves, bool select, bool extend)
658 {
659         SpaceNode *snode = CTX_wm_space_node(C);
660         bNode *node;
661
662         ARegion *ar = CTX_wm_region(C);
663
664         rcti rect;
665         bool changed = false;
666
667         /* get rectangle from operator */
668         BLI_lasso_boundbox(&rect, mcords, moves);
669
670         /* do actual selection */
671         for (node = snode->edittree->nodes.first; node; node = node->next) {
672
673                 if (node->flag & NODE_SELECT && select && extend) {
674                         continue;
675                 }
676
677                 int screen_co[2];
678                 const float cent[2] = {BLI_rctf_cent_x(&node->totr),
679                                        BLI_rctf_cent_y(&node->totr)};
680
681                 /* marker in screen coords */
682                 if (UI_view2d_view_to_region_clip(&ar->v2d, cent[0], cent[1], &screen_co[0], &screen_co[1]) &&
683                     BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
684                     BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX))
685                 {
686                         nodeSetSelected(node, select);
687                         changed = true;
688                 }
689                 else if (select && !extend) {
690                         nodeSetSelected(node, false);
691                         changed = true;
692                 }
693         }
694
695         if (changed) {
696                 WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
697         }
698
699         return changed;
700 }
701
702 static int node_lasso_select_exec(bContext *C, wmOperator *op)
703 {
704         int mcords_tot;
705         const int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
706
707         if (mcords) {
708                 const bool select = !RNA_boolean_get(op->ptr, "deselect");
709                 const bool extend = RNA_boolean_get(op->ptr, "extend");
710                 do_lasso_select_node(C, mcords, mcords_tot, select, extend);
711
712                 MEM_freeN((void *)mcords);
713
714                 return OPERATOR_FINISHED;
715         }
716         return OPERATOR_PASS_THROUGH;
717 }
718
719 void NODE_OT_select_lasso(wmOperatorType *ot)
720 {
721         /* identifiers */
722         ot->name = "Lasso Select";
723         ot->description = "Select nodes using lasso selection";
724         ot->idname = "NODE_OT_select_lasso";
725
726         /* api callbacks */
727         ot->invoke = WM_gesture_lasso_invoke;
728         ot->modal = WM_gesture_lasso_modal;
729         ot->exec = node_lasso_select_exec;
730         ot->poll = ED_operator_node_active;
731         ot->cancel = WM_gesture_lasso_cancel;
732
733         /* flags */
734         ot->flag = OPTYPE_UNDO;
735
736         /* properties */
737         WM_operator_properties_gesture_lasso_select(ot);
738 }
739
740 /* ****** Select/Deselect All ****** */
741
742 static int node_select_all_exec(bContext *C, wmOperator *op)
743 {
744         SpaceNode *snode = CTX_wm_space_node(C);
745         ListBase *node_lb = &snode->edittree->nodes;
746         bNode *node;
747         int action = RNA_enum_get(op->ptr, "action");
748
749         if (action == SEL_TOGGLE) {
750                 if (ED_node_select_check(node_lb))
751                         action = SEL_DESELECT;
752                 else
753                         action = SEL_SELECT;
754         }
755
756         for (node = node_lb->first; node; node = node->next) {
757                 switch (action) {
758                         case SEL_SELECT:
759                                 nodeSetSelected(node, true);
760                                 break;
761                         case SEL_DESELECT:
762                                 nodeSetSelected(node, false);
763                                 break;
764                         case SEL_INVERT:
765                                 nodeSetSelected(node, !(node->flag & SELECT));
766                                 break;
767                 }
768         }
769
770         ED_node_sort(snode->edittree);
771
772         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
773         return OPERATOR_FINISHED;
774 }
775
776 void NODE_OT_select_all(wmOperatorType *ot)
777 {
778         /* identifiers */
779         ot->name = "(De)select All";
780         ot->description = "(De)select all nodes";
781         ot->idname = "NODE_OT_select_all";
782
783         /* api callbacks */
784         ot->exec = node_select_all_exec;
785         ot->poll = ED_operator_node_active;
786
787         /* flags */
788         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
789
790         WM_operator_properties_select_all(ot);
791 }
792
793 /* ****** Select Linked To ****** */
794
795 static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
796 {
797         SpaceNode *snode = CTX_wm_space_node(C);
798         bNodeLink *link;
799         bNode *node;
800
801         for (node = snode->edittree->nodes.first; node; node = node->next)
802                 node->flag &= ~NODE_TEST;
803
804         for (link = snode->edittree->links.first; link; link = link->next) {
805                 if (nodeLinkIsHidden(link))
806                         continue;
807                 if (link->fromnode && link->tonode && (link->fromnode->flag & NODE_SELECT))
808                         link->tonode->flag |= NODE_TEST;
809         }
810
811         for (node = snode->edittree->nodes.first; node; node = node->next) {
812                 if (node->flag & NODE_TEST)
813                         nodeSetSelected(node, true);
814         }
815
816         ED_node_sort(snode->edittree);
817
818         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
819         return OPERATOR_FINISHED;
820 }
821
822 void NODE_OT_select_linked_to(wmOperatorType *ot)
823 {
824         /* identifiers */
825         ot->name = "Select Linked To";
826         ot->description = "Select nodes linked to the selected ones";
827         ot->idname = "NODE_OT_select_linked_to";
828
829         /* api callbacks */
830         ot->exec = node_select_linked_to_exec;
831         ot->poll = ED_operator_node_active;
832
833         /* flags */
834         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
835 }
836
837 /* ****** Select Linked From ****** */
838
839 static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
840 {
841         SpaceNode *snode = CTX_wm_space_node(C);
842         bNodeLink *link;
843         bNode *node;
844
845         for (node = snode->edittree->nodes.first; node; node = node->next)
846                 node->flag &= ~NODE_TEST;
847
848         for (link = snode->edittree->links.first; link; link = link->next) {
849                 if (nodeLinkIsHidden(link))
850                         continue;
851                 if (link->fromnode && link->tonode && (link->tonode->flag & NODE_SELECT))
852                         link->fromnode->flag |= NODE_TEST;
853         }
854
855         for (node = snode->edittree->nodes.first; node; node = node->next) {
856                 if (node->flag & NODE_TEST)
857                         nodeSetSelected(node, true);
858         }
859
860         ED_node_sort(snode->edittree);
861
862         WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
863         return OPERATOR_FINISHED;
864 }
865
866 void NODE_OT_select_linked_from(wmOperatorType *ot)
867 {
868         /* identifiers */
869         ot->name = "Select Linked From";
870         ot->description = "Select nodes linked from the selected ones";
871         ot->idname = "NODE_OT_select_linked_from";
872
873         /* api callbacks */
874         ot->exec = node_select_linked_from_exec;
875         ot->poll = ED_operator_node_active;
876
877         /* flags */
878         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
879 }
880
881 static int node_select_same_type_step_exec(bContext *C, wmOperator *op)
882 {
883         SpaceNode *snode = CTX_wm_space_node(C);
884         ARegion *ar = CTX_wm_region(C);
885         bNode **node_array;
886         bNode *active = nodeGetActive(snode->edittree);
887         int totnodes;
888         const bool revert = RNA_boolean_get(op->ptr, "prev");
889         const bool same_type = 1;
890
891         ntreeGetDependencyList(snode->edittree, &node_array, &totnodes);
892
893         if (totnodes > 1) {
894                 int a;
895
896                 for (a = 0; a < totnodes; a++) {
897                         if (node_array[a] == active)
898                                 break;
899                 }
900
901                 if (same_type) {
902                         bNode *node = NULL;
903
904                         while (node == NULL) {
905                                 if (revert) a--;
906                                 else a++;
907
908                                 if (a < 0 || a >= totnodes)
909                                         break;
910
911                                 node = node_array[a];
912
913                                 if (node->type == active->type)
914                                         break;
915                                 else node = NULL;
916                         }
917                         if (node)
918                                 active = node;
919                 }
920                 else {
921                         if (revert) {
922                                 if (a == 0)
923                                         active = node_array[totnodes - 1];
924                                 else
925                                         active = node_array[a - 1];
926                         }
927                         else {
928                                 if (a == totnodes - 1)
929                                         active = node_array[0];
930                                 else
931                                         active = node_array[a + 1];
932                         }
933                 }
934
935                 node_select_single(C, active);
936
937                 /* is note outside view? */
938                 if (active->totr.xmax < ar->v2d.cur.xmin || active->totr.xmin > ar->v2d.cur.xmax ||
939                     active->totr.ymax < ar->v2d.cur.ymin || active->totr.ymin > ar->v2d.cur.ymax)
940                 {
941                         const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
942                         space_node_view_flag(C, snode, ar, NODE_SELECT, smooth_viewtx);
943                 }
944         }
945
946         if (node_array)
947                 MEM_freeN(node_array);
948
949         return OPERATOR_FINISHED;
950 }
951
952 void NODE_OT_select_same_type_step(wmOperatorType *ot)
953 {
954         /* identifiers */
955         ot->name = "Activate Same Type Next/Prev";
956         ot->description = "Activate and view same node type, step by step";
957         ot->idname = "NODE_OT_select_same_type_step";
958
959         /* api callbacks */
960         ot->exec = node_select_same_type_step_exec;
961         ot->poll = ED_operator_node_active;
962
963         /* flags */
964         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
965
966         RNA_def_boolean(ot->srna, "prev", 0, "Previous", "");
967
968 }
969
970 /* *************** find a node **************** */
971
972 /* generic  search invoke */
973 static void node_find_cb(const struct bContext *C, void *UNUSED(arg), const char *str, uiSearchItems *items)
974 {
975         SpaceNode *snode = CTX_wm_space_node(C);
976         bNode *node;
977
978         for (node = snode->edittree->nodes.first; node; node = node->next) {
979
980                 if (BLI_strcasestr(node->name, str) || BLI_strcasestr(node->label, str)) {
981                         char name[256];
982
983                         if (node->label[0])
984                                 BLI_snprintf(name, 256, "%s (%s)", node->name, node->label);
985                         else
986                                 BLI_strncpy(name, node->name, 256);
987                         if (false == UI_search_item_add(items, name, node, 0))
988                                 break;
989                 }
990         }
991 }
992
993 static void node_find_call_cb(struct bContext *C, void *UNUSED(arg1), void *arg2)
994 {
995         SpaceNode *snode = CTX_wm_space_node(C);
996         bNode *active = arg2;
997
998         if (active) {
999                 ARegion *ar = CTX_wm_region(C);
1000                 node_select_single(C, active);
1001
1002                 /* is note outside view? */
1003                 if (active->totr.xmax < ar->v2d.cur.xmin || active->totr.xmin > ar->v2d.cur.xmax ||
1004                     active->totr.ymax < ar->v2d.cur.ymin || active->totr.ymin > ar->v2d.cur.ymax)
1005                 {
1006                         space_node_view_flag(C, snode, ar, NODE_SELECT, U.smooth_viewtx);
1007                 }
1008
1009         }
1010 }
1011
1012 static uiBlock *node_find_menu(bContext *C, ARegion *ar, void *arg_op)
1013 {
1014         static char search[256] = "";
1015         wmEvent event;
1016         wmWindow *win = CTX_wm_window(C);
1017         uiBlock *block;
1018         uiBut *but;
1019         wmOperator *op = (wmOperator *)arg_op;
1020
1021         block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
1022         UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
1023         UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
1024
1025         but = uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, 9 * UI_UNIT_X, UI_UNIT_Y, 0, 0, "");
1026         UI_but_func_search_set(but, NULL, node_find_cb, op->type, node_find_call_cb, NULL);
1027
1028         /* fake button, it holds space for search items */
1029         uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 10 - UI_searchbox_size_y(), UI_searchbox_size_x(), UI_searchbox_size_y(), NULL, 0, 0, 0, 0, NULL);
1030
1031         UI_block_bounds_set_popup(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
1032
1033         //      UI_but_active_only(C, ar, block, but); XXX using this here makes Blender hang - investigate
1034         wm_event_init_from_window(win, &event);
1035         event.type = EVT_BUT_OPEN;
1036         event.val = KM_PRESS;
1037         event.customdata = but;
1038         event.customdatafree = false;
1039         wm_event_add(win, &event);
1040
1041         return block;
1042 }
1043
1044
1045 static int node_find_node_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1046 {
1047         UI_popup_block_invoke(C, node_find_menu, op);
1048         return OPERATOR_CANCELLED;
1049 }
1050
1051
1052 void NODE_OT_find_node(wmOperatorType *ot)
1053 {
1054         /* identifiers */
1055         ot->name = "Find Node";
1056         ot->description = "Search for named node and allow to select and activate it";
1057         ot->idname = "NODE_OT_find_node";
1058
1059         /* api callbacks */
1060         ot->invoke = node_find_node_invoke;
1061         ot->poll = ED_operator_node_active;
1062
1063         /* flags */
1064         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1065
1066         RNA_def_boolean(ot->srna, "prev", 0, "Previous", "");
1067
1068 }