- Bugfix: NLA "duplicate" operator now lets strips get moved between
[blender.git] / source / blender / editors / space_node / node_select.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation, Nathan Letwory
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <stdio.h>
30
31 #include "DNA_node_types.h"
32 #include "DNA_scene_types.h"
33
34 #include "BKE_context.h"
35
36 #include "BLI_rect.h"
37 #include "BLI_utildefines.h"
38
39 #include "ED_screen.h"
40 #include "ED_types.h"
41
42 #include "RNA_access.h"
43 #include "RNA_define.h"
44
45 #include "WM_api.h"
46 #include "WM_types.h"
47
48 #include "UI_view2d.h"
49  
50 #include "node_intern.h"
51
52 /* ****** helpers ****** */
53
54 static bNode *node_under_mouse(bNodeTree *ntree, int mx, int my)
55 {
56         bNode *node;
57         
58         for(next_node(ntree); (node=next_node(NULL));) {
59                 /* node body (header and scale are in other operators) */
60                 if (BLI_in_rctf(&node->totr, mx, my))
61                         return node;
62         }
63         return NULL;
64 }
65
66 /* ****** Click Select ****** */
67  
68 static bNode *node_mouse_select(SpaceNode *snode, ARegion *ar, short *mval, short extend)
69 {
70         bNode *node;
71         float mx, my;
72         
73         /* get mouse coordinates in view2d space */
74         mx= (float)mval[0];
75         my= (float)mval[1];
76         
77         UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &mx, &my);
78         
79         /* find the closest visible node */
80         node = node_under_mouse(snode->edittree, mx, my);
81         
82         if (node) {
83                 if (extend == 0) {
84                         node_deselectall(snode);
85                         node->flag |= SELECT;
86                 }
87                 else
88                         node->flag ^= SELECT;
89                         
90                 node_set_active(snode, node);
91         }
92
93         return node;
94 }
95
96 static int node_select_exec(bContext *C, wmOperator *op)
97 {
98         SpaceNode *snode= CTX_wm_space_node(C);
99         ARegion *ar= CTX_wm_region(C);
100         short mval[2];
101         short extend;
102         bNode *node= NULL;
103         
104         /* get settings from RNA properties for operator */
105         mval[0] = RNA_int_get(op->ptr, "mouse_x");
106         mval[1] = RNA_int_get(op->ptr, "mouse_y");
107         
108         extend = RNA_boolean_get(op->ptr, "extend");
109         
110         /* perform the select */
111         node= node_mouse_select(snode, ar, mval, extend);
112         
113         /* send notifiers */
114         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
115         
116         /* allow tweak event to work too */
117         return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
118 }
119
120 static int node_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
121 {
122         ARegion *ar= CTX_wm_region(C);
123         short mval[2];  
124         
125         mval[0]= event->x - ar->winrct.xmin;
126         mval[1]= event->y - ar->winrct.ymin;
127         
128         RNA_int_set(op->ptr, "mouse_x", mval[0]);
129         RNA_int_set(op->ptr, "mouse_y", mval[1]);
130
131         return node_select_exec(C,op);
132 }
133
134
135 void NODE_OT_select(wmOperatorType *ot)
136 {
137         /* identifiers */
138         ot->name= "Select";
139         ot->idname= "NODE_OT_select";
140         ot->description= "Select node under cursor";
141         
142         /* api callbacks */
143         ot->invoke= node_select_invoke;
144         ot->poll= ED_operator_node_active;
145         
146         /* flags */
147         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
148         
149         /* properties */
150         RNA_def_int(ot->srna, "mouse_x", 0, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
151         RNA_def_int(ot->srna, "mouse_y", 0, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
152         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
153 }
154
155 /* ****** Border Select ****** */
156
157 static int node_borderselect_exec(bContext *C, wmOperator *op)
158 {
159         SpaceNode *snode= CTX_wm_space_node(C);
160         ARegion *ar= CTX_wm_region(C);
161         bNode *node;
162         rcti rect;
163         rctf rectf;
164         int gesture_mode= RNA_int_get(op->ptr, "gesture_mode");
165         
166         rect.xmin= RNA_int_get(op->ptr, "xmin");
167         rect.ymin= RNA_int_get(op->ptr, "ymin");
168         UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
169         
170         rect.xmax= RNA_int_get(op->ptr, "xmax");
171         rect.ymax= RNA_int_get(op->ptr, "ymax");
172         UI_view2d_region_to_view(&ar->v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
173         
174         for(node= snode->edittree->nodes.first; node; node= node->next) {
175                 if(BLI_isect_rctf(&rectf, &node->totr, NULL)) {
176                         if(gesture_mode==GESTURE_MODAL_SELECT)
177                                 node->flag |= SELECT;
178                         else
179                                 node->flag &= ~SELECT;
180                 }
181         }
182         
183         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
184
185         return OPERATOR_FINISHED;
186 }
187
188 static int node_border_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
189 {
190         int tweak = RNA_boolean_get(op->ptr, "tweak");
191         
192         if (tweak) {
193                 /* prevent initiating the border select if the mouse is over a node */
194                 /* this allows border select on empty space, but drag-translate on nodes */
195                 SpaceNode *snode= CTX_wm_space_node(C);
196                 ARegion *ar= CTX_wm_region(C);
197                 short mval[2];
198                 float mx, my;
199                 
200                 mval[0]= event->x - ar->winrct.xmin;
201                 mval[1]= event->y - ar->winrct.ymin;
202                 
203                 /* get mouse coordinates in view2d space */
204                 mx= (float)mval[0];
205                 my= (float)mval[1];
206                 
207                 UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &mx, &my);
208                 
209                 if (node_under_mouse(snode->edittree, mx, my))
210                         return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
211         }
212         
213         return WM_border_select_invoke(C, op, event);
214 }
215
216 void NODE_OT_select_border(wmOperatorType *ot)
217 {
218         /* identifiers */
219         ot->name= "Border Select";
220         ot->idname= "NODE_OT_select_border";
221         ot->description= "Use box selection to select nodes";
222         
223         /* api callbacks */
224         ot->invoke= node_border_select_invoke;
225         ot->exec= node_borderselect_exec;
226         ot->modal= WM_border_select_modal;
227         
228         ot->poll= ED_operator_node_active;
229         
230         /* flags */
231         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
232         
233         /* rna */
234         WM_operator_properties_gesture_border(ot, FALSE);
235         RNA_def_boolean(ot->srna, "tweak", 0, "Tweak", "Only activate when mouse is not over a node - useful for tweak gesture");
236 }
237
238 /* ****** Select/Deselect All ****** */
239
240 static int node_select_all_exec(bContext *C, wmOperator *UNUSED(op))
241 {
242         SpaceNode *snode = CTX_wm_space_node(C);
243         bNode *first = snode->edittree->nodes.first;
244         bNode *node;
245         int count= 0;
246
247         for(node=first; node; node=node->next)
248                 if(node->flag & NODE_SELECT)
249                         count++;
250
251         if(count) {
252                 for(node=first; node; node=node->next)
253                         node->flag &= ~NODE_SELECT;
254         }
255         else {
256                 for(node=first; node; node=node->next)
257                         node->flag |= NODE_SELECT;
258         }
259         
260         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
261         return OPERATOR_FINISHED;
262 }
263
264 void NODE_OT_select_all(wmOperatorType *ot)
265 {
266         /* identifiers */
267         ot->name = "Select or Deselect All";
268         ot->description = "(De)select all nodes";
269         ot->idname = "NODE_OT_select_all";
270         
271         /* api callbacks */
272         ot->exec = node_select_all_exec;
273         ot->poll = ED_operator_node_active;
274         
275         /* flags */
276         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
277 }
278
279 /* ****** Select Linked To ****** */
280
281 static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
282 {
283         SpaceNode *snode = CTX_wm_space_node(C);
284         bNodeLink *link;
285         bNode *node;
286         
287         for (node=snode->edittree->nodes.first; node; node=node->next)
288                 node->flag &= ~NODE_TEST;
289
290         for (link=snode->edittree->links.first; link; link=link->next) {
291                 if (link->fromnode->flag & NODE_SELECT)
292                         link->tonode->flag |= NODE_TEST;
293         }
294         
295         for (node=snode->edittree->nodes.first; node; node=node->next) {
296                 if (node->flag & NODE_TEST)
297                         node->flag |= NODE_SELECT;
298         }
299         
300         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
301         return OPERATOR_FINISHED;
302 }
303
304 void NODE_OT_select_linked_to(wmOperatorType *ot)
305 {
306         /* identifiers */
307         ot->name = "Select Linked To";
308         ot->description = "Select nodes linked to the selected ones";
309         ot->idname = "NODE_OT_select_linked_to";
310         
311         /* api callbacks */
312         ot->exec = node_select_linked_to_exec;
313         ot->poll = ED_operator_node_active;
314         
315         /* flags */
316         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
317 }
318
319 /* ****** Select Linked From ****** */
320
321 static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
322 {
323         SpaceNode *snode = CTX_wm_space_node(C);
324         bNodeLink *link;
325         bNode *node;
326         
327         for(node=snode->edittree->nodes.first; node; node=node->next)
328                 node->flag &= ~NODE_TEST;
329
330         for(link=snode->edittree->links.first; link; link=link->next) {
331                 if(link->tonode->flag & NODE_SELECT)
332                         link->fromnode->flag |= NODE_TEST;
333         }
334         
335         for(node=snode->edittree->nodes.first; node; node=node->next) {
336                 if(node->flag & NODE_TEST)
337                         node->flag |= NODE_SELECT;
338         }
339         
340         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
341         return OPERATOR_FINISHED;
342 }
343
344 void NODE_OT_select_linked_from(wmOperatorType *ot)
345 {
346         /* identifiers */
347         ot->name = "Select Linked From";
348         ot->description = "Select nodes linked from the selected ones";
349         ot->idname = "NODE_OT_select_linked_from";
350         
351         /* api callbacks */
352         ot->exec = node_select_linked_from_exec;
353         ot->poll = ED_operator_node_active;
354         
355         /* flags */
356         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
357 }
358
359 /* ****** Select Same Type ****** */
360
361 static int node_select_same_type_exec(bContext *C, wmOperator *UNUSED(op))
362 {
363         SpaceNode *snode = CTX_wm_space_node(C);
364
365         node_select_same_type(snode);
366         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
367         return OPERATOR_FINISHED;
368 }
369
370 void NODE_OT_select_same_type(wmOperatorType *ot)
371 {
372         /* identifiers */
373         ot->name = "Select Same Type";
374         ot->description = "Select all the same type";
375         ot->idname = "NODE_OT_select_same_type";
376         
377         /* api callbacks */
378         ot->exec = node_select_same_type_exec;
379         ot->poll = ED_operator_node_active;
380         
381         /* flags */
382         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
383 }
384
385 /* ****** Select The Next/Prev Node Of The Same Type ****** */
386
387 static int node_select_same_type_next_exec(bContext *C, wmOperator *UNUSED(op))
388 {
389         SpaceNode *snode = CTX_wm_space_node(C);
390
391         node_select_same_type_np(snode, 0);
392         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
393         return OPERATOR_FINISHED;
394 }
395
396 void NODE_OT_select_same_type_next(wmOperatorType *ot)
397 {
398         /* identifiers */
399         ot->name = "Select Same Type Next";
400         ot->description = "Select the next node of the same type.";
401         ot->idname = "NODE_OT_select_same_type_next";
402         
403         /* api callbacks */
404         ot->exec = node_select_same_type_next_exec;
405         ot->poll = ED_operator_node_active;
406         
407         /* flags */
408         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
409 }
410
411 static int node_select_same_type_prev_exec(bContext *C, wmOperator *UNUSED(op))
412 {
413         SpaceNode *snode = CTX_wm_space_node(C);
414
415         node_select_same_type_np(snode, 1);
416         WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
417         return OPERATOR_FINISHED;
418 }
419
420 void NODE_OT_select_same_type_prev(wmOperatorType *ot)
421 {
422         /* identifiers */
423         ot->name = "Select Same Type Prev";
424         ot->description = "Select the prev node of the same type.";
425         ot->idname = "NODE_OT_select_same_type_prev";
426         
427         /* api callbacks */
428         ot->exec = node_select_same_type_prev_exec;
429         ot->poll = ED_operator_node_active;
430         
431         /* flags */
432         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
433 }