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