Blender V2.61 - r43446

node_select.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version. 
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2008 Blender Foundation.
00019  * All rights reserved.
00020  *
00021  * 
00022  * Contributor(s): Blender Foundation, Nathan Letwory
00023  *
00024  * ***** END GPL LICENSE BLOCK *****
00025  */
00026 
00032 #include <stdio.h>
00033 
00034 #include "BLI_listbase.h"
00035 
00036 #include "DNA_node_types.h"
00037 #include "DNA_scene_types.h"
00038 
00039 #include "BKE_context.h"
00040 #include "BKE_main.h"
00041 
00042 #include "BLI_rect.h"
00043 #include "BLI_utildefines.h"
00044 
00045 #include "ED_node.h"
00046 #include "ED_screen.h"
00047 #include "ED_types.h"
00048 
00049 #include "RNA_access.h"
00050 #include "RNA_define.h"
00051 
00052 #include "WM_api.h"
00053 #include "WM_types.h"
00054 
00055 #include "UI_view2d.h"
00056  
00057 #include "node_intern.h"
00058 
00059 /* ****** helpers ****** */
00060 
00061 static bNode *node_under_mouse(bNodeTree *ntree, int mx, int my)
00062 {
00063     bNode *node;
00064     
00065     for(node=ntree->nodes.last; node; node=node->prev) {
00066         /* node body (header and scale are in other operators) */
00067         if (BLI_in_rctf(&node->totr, mx, my))
00068             return node;
00069     }
00070     return NULL;
00071 }
00072 
00073 static int compare_nodes(bNode *a, bNode *b)
00074 {
00075     bNode *parent;
00076     /* These tell if either the node or any of the parent nodes is selected.
00077      * A selected parent means an unselected node is also in foreground!
00078      */
00079     int a_select=(a->flag & NODE_SELECT), b_select=(b->flag & NODE_SELECT);
00080     int a_active=(a->flag & NODE_ACTIVE), b_active=(b->flag & NODE_ACTIVE);
00081     
00082     /* if one is an ancestor of the other */
00083     /* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */
00084     for (parent = a->parent; parent; parent=parent->parent) {
00085         /* if b is an ancestor, it is always behind a */
00086         if (parent==b)
00087             return 1;
00088         /* any selected ancestor moves the node forward */
00089         if (parent->flag & NODE_ACTIVE)
00090             a_active = 1;
00091         if (parent->flag & NODE_SELECT)
00092             a_select = 1;
00093     }
00094     for (parent = b->parent; parent; parent=parent->parent) {
00095         /* if a is an ancestor, it is always behind b */
00096         if (parent==a)
00097             return 0;
00098         /* any selected ancestor moves the node forward */
00099         if (parent->flag & NODE_ACTIVE)
00100             b_active = 1;
00101         if (parent->flag & NODE_SELECT)
00102             b_select = 1;
00103     }
00104 
00105     /* if one of the nodes is in the background and the other not */
00106     if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND))
00107         return 0;
00108     else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND))
00109         return 1;
00110     
00111     /* if one has a higher selection state (active > selected > nothing) */
00112     if (!b_active && a_active)
00113         return 1;
00114     else if (!b_select && (a_active || a_select))
00115         return 1;
00116     
00117     return 0;
00118 }
00119 
00120 /* Sorts nodes by selection: unselected nodes first, then selected,
00121  * then the active node at the very end. Relative order is kept intact!
00122  */
00123 static void node_sort(bNodeTree *ntree)
00124 {
00125     /* merge sort is the algorithm of choice here */
00126     bNode *first_a, *first_b, *node_a, *node_b, *tmp;
00127     int totnodes= BLI_countlist(&ntree->nodes);
00128     int k, a, b;
00129     
00130     k = 1;
00131     while (k < totnodes) {
00132         first_a = first_b = ntree->nodes.first;
00133         
00134         do {
00135             /* setup first_b pointer */
00136             for (b=0; b < k && first_b; ++b) {
00137                 first_b = first_b->next;
00138             }
00139             /* all batches merged? */
00140             if (first_b==NULL)
00141                 break;
00142             
00143             /* merge batches */
00144             node_a = first_a;
00145             node_b = first_b;
00146             a = b = 0;
00147             while (a < k && b < k && node_b) {
00148                 if (compare_nodes(node_a, node_b)==0) {
00149                     node_a = node_a->next;
00150                     ++a;
00151                 }
00152                 else {
00153                     tmp = node_b;
00154                     node_b = node_b->next;
00155                     ++b;
00156                     BLI_remlink(&ntree->nodes, tmp);
00157                     BLI_insertlinkbefore(&ntree->nodes, node_a, tmp);
00158                 }
00159             }
00160 
00161             /* setup first pointers for next batch */
00162             first_b = node_b;
00163             for (; b < k; ++b) {
00164                 /* all nodes sorted? */
00165                 if (first_b==NULL)
00166                     break;
00167                 first_b = first_b->next;
00168             }
00169             first_a = first_b;
00170         } while (first_b);
00171         
00172         k = k << 1;
00173     }
00174 }
00175 
00176 /* no undo here! */
00177 void node_deselect_all(SpaceNode *snode)
00178 {
00179     bNode *node;
00180     
00181     for(node= snode->edittree->nodes.first; node; node= node->next)
00182         node->flag &= ~SELECT;
00183 }
00184 
00185 /* return 1 if we need redraw otherwise zero. */
00186 int node_select_same_type(SpaceNode *snode)
00187 {
00188     bNode *nac, *p;
00189     int redraw;
00190 
00191     /* search for the active node. */
00192     for (nac= snode->edittree->nodes.first; nac; nac= nac->next) {
00193         if (nac->flag & SELECT)
00194             break;
00195     }
00196 
00197     /* no active node, return. */
00198     if (!nac)
00199         return(0);
00200 
00201     redraw= 0;
00202     for (p= snode->edittree->nodes.first; p; p= p->next) {
00203         if (p->type != nac->type && p->flag & SELECT) {
00204             /* if it's selected but different type, unselect */
00205             redraw= 1;
00206             p->flag &= ~SELECT;
00207         }
00208         else if (p->type == nac->type && (!(p->flag & SELECT))) {
00209             /* if it's the same type and is not selected, select! */
00210             redraw= 1;
00211             p->flag |= SELECT;
00212         }
00213     }
00214     return(redraw);
00215 }
00216 
00217 /* return 1 if we need redraw, otherwise zero.
00218  * dir can be 0 == next or 0 != prev.
00219  */
00220 int node_select_same_type_np(SpaceNode *snode, int dir)
00221 {
00222     bNode *nac, *p;
00223 
00224     /* search the active one. */
00225     for (nac= snode->edittree->nodes.first; nac; nac= nac->next) {
00226         if (nac->flag & SELECT)
00227             break;
00228     }
00229 
00230     /* no active node, return. */
00231     if (!nac)
00232         return(0);
00233 
00234     if (dir == 0)
00235         p= nac->next;
00236     else
00237         p= nac->prev;
00238 
00239     while (p) {
00240         /* Now search the next with the same type. */
00241         if (p->type == nac->type)
00242             break;
00243 
00244         if (dir == 0)
00245             p= p->next;
00246         else
00247             p= p->prev;
00248     }
00249 
00250     if (p) {
00251         node_deselect_all(snode);
00252         p->flag |= SELECT;
00253         return(1);
00254     }
00255     return(0);
00256 }
00257 
00258 void node_select_single(bContext *C, bNode *node)
00259 {
00260     Main *bmain= CTX_data_main(C);
00261     SpaceNode *snode= CTX_wm_space_node(C);
00262     
00263     node_deselect_all(snode);
00264     node->flag |= SELECT;
00265     
00266     ED_node_set_active(bmain, snode->edittree, node);
00267     
00268     node_sort(snode->edittree);
00269     
00270     WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
00271 }
00272 
00273 /* ****** Click Select ****** */
00274  
00275 static bNode *node_mouse_select(Main *bmain, SpaceNode *snode, ARegion *ar, const int mval[2], short extend)
00276 {
00277     bNode *node;
00278     float mx, my;
00279     
00280     /* get mouse coordinates in view2d space */
00281     mx= (float)mval[0];
00282     my= (float)mval[1];
00283     
00284     UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &mx, &my);
00285     
00286     /* find the closest visible node */
00287     node = node_under_mouse(snode->edittree, mx, my);
00288     
00289     if (node) {
00290         if (extend == 0) {
00291             node_deselect_all(snode);
00292             node->flag |= SELECT;
00293         }
00294         else
00295             node->flag ^= SELECT;
00296         
00297         ED_node_set_active(bmain, snode->edittree, node);
00298         
00299         node_sort(snode->edittree);
00300     }
00301 
00302     return node;
00303 }
00304 
00305 static int node_select_exec(bContext *C, wmOperator *op)
00306 {
00307     Main *bmain= CTX_data_main(C);
00308     SpaceNode *snode= CTX_wm_space_node(C);
00309     ARegion *ar= CTX_wm_region(C);
00310     int mval[2];
00311     short extend;
00312     /* bNode *node= NULL; */ /* UNUSED */
00313     
00314     /* get settings from RNA properties for operator */
00315     mval[0] = RNA_int_get(op->ptr, "mouse_x");
00316     mval[1] = RNA_int_get(op->ptr, "mouse_y");
00317     
00318     extend = RNA_boolean_get(op->ptr, "extend");
00319     
00320     /* perform the select */
00321     /* node= */ /* UNUSED*/ node_mouse_select(bmain, snode, ar, mval, extend);
00322     
00323     /* send notifiers */
00324     WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
00325     
00326     /* allow tweak event to work too */
00327     return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
00328 }
00329 
00330 static int node_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
00331 {
00332     RNA_int_set(op->ptr, "mouse_x", event->mval[0]);
00333     RNA_int_set(op->ptr, "mouse_y", event->mval[1]);
00334 
00335     return node_select_exec(C,op);
00336 }
00337 
00338 
00339 void NODE_OT_select(wmOperatorType *ot)
00340 {
00341     /* identifiers */
00342     ot->name= "Select";
00343     ot->idname= "NODE_OT_select";
00344     ot->description= "Select the node under the cursor";
00345     
00346     /* api callbacks */
00347     ot->invoke= node_select_invoke;
00348     ot->poll= ED_operator_node_active;
00349     
00350     /* flags */
00351     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00352     
00353     /* properties */
00354     RNA_def_int(ot->srna, "mouse_x", 0, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
00355     RNA_def_int(ot->srna, "mouse_y", 0, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
00356     RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
00357 }
00358 
00359 /* ****** Border Select ****** */
00360 
00361 static int node_borderselect_exec(bContext *C, wmOperator *op)
00362 {
00363     SpaceNode *snode= CTX_wm_space_node(C);
00364     ARegion *ar= CTX_wm_region(C);
00365     bNode *node;
00366     rcti rect;
00367     rctf rectf;
00368     int gesture_mode= RNA_int_get(op->ptr, "gesture_mode");
00369     int extend= RNA_boolean_get(op->ptr, "extend");
00370     
00371     rect.xmin= RNA_int_get(op->ptr, "xmin");
00372     rect.ymin= RNA_int_get(op->ptr, "ymin");
00373     UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
00374     
00375     rect.xmax= RNA_int_get(op->ptr, "xmax");
00376     rect.ymax= RNA_int_get(op->ptr, "ymax");
00377     UI_view2d_region_to_view(&ar->v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
00378     
00379     for(node= snode->edittree->nodes.first; node; node= node->next) {
00380         if(BLI_isect_rctf(&rectf, &node->totr, NULL)) {
00381             if(gesture_mode==GESTURE_MODAL_SELECT)
00382                 node->flag |= SELECT;
00383             else
00384                 node->flag &= ~SELECT;
00385         }
00386         else if(!extend) {
00387             node->flag &= ~SELECT;
00388         }
00389     }
00390     
00391     node_sort(snode->edittree);
00392     
00393     WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
00394 
00395     return OPERATOR_FINISHED;
00396 }
00397 
00398 static int node_border_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
00399 {
00400     int tweak = RNA_boolean_get(op->ptr, "tweak");
00401     
00402     if (tweak) {
00403         /* prevent initiating the border select if the mouse is over a node */
00404         /* this allows border select on empty space, but drag-translate on nodes */
00405         SpaceNode *snode= CTX_wm_space_node(C);
00406         ARegion *ar= CTX_wm_region(C);
00407         float mx, my;
00408 
00409         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &mx, &my);
00410         
00411         if (node_under_mouse(snode->edittree, mx, my))
00412             return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH;
00413     }
00414     
00415     return WM_border_select_invoke(C, op, event);
00416 }
00417 
00418 void NODE_OT_select_border(wmOperatorType *ot)
00419 {
00420     /* identifiers */
00421     ot->name= "Border Select";
00422     ot->idname= "NODE_OT_select_border";
00423     ot->description= "Use box selection to select nodes";
00424     
00425     /* api callbacks */
00426     ot->invoke= node_border_select_invoke;
00427     ot->exec= node_borderselect_exec;
00428     ot->modal= WM_border_select_modal;
00429     ot->cancel= WM_border_select_cancel;
00430     
00431     ot->poll= ED_operator_node_active;
00432     
00433     /* flags */
00434     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00435     
00436     /* rna */
00437     WM_operator_properties_gesture_border(ot, TRUE);
00438     RNA_def_boolean(ot->srna, "tweak", 0, "Tweak", "Only activate when mouse is not over a node - useful for tweak gesture");
00439 }
00440 
00441 /* ****** Select/Deselect All ****** */
00442 
00443 static int node_select_all_exec(bContext *C, wmOperator *UNUSED(op))
00444 {
00445     SpaceNode *snode = CTX_wm_space_node(C);
00446     bNode *first = snode->edittree->nodes.first;
00447     bNode *node;
00448     int count= 0;
00449 
00450     for(node=first; node; node=node->next)
00451         if(node->flag & NODE_SELECT)
00452             count++;
00453 
00454     if(count) {
00455         for(node=first; node; node=node->next)
00456             node->flag &= ~NODE_SELECT;
00457     }
00458     else {
00459         for(node=first; node; node=node->next)
00460             node->flag |= NODE_SELECT;
00461     }
00462     
00463     node_sort(snode->edittree);
00464     
00465     WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
00466     return OPERATOR_FINISHED;
00467 }
00468 
00469 void NODE_OT_select_all(wmOperatorType *ot)
00470 {
00471     /* identifiers */
00472     ot->name = "Select or Deselect All";
00473     ot->description = "(De)select all nodes";
00474     ot->idname = "NODE_OT_select_all";
00475     
00476     /* api callbacks */
00477     ot->exec = node_select_all_exec;
00478     ot->poll = ED_operator_node_active;
00479     
00480     /* flags */
00481     ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
00482 }
00483 
00484 /* ****** Select Linked To ****** */
00485 
00486 static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
00487 {
00488     SpaceNode *snode = CTX_wm_space_node(C);
00489     bNodeLink *link;
00490     bNode *node;
00491     
00492     for (node=snode->edittree->nodes.first; node; node=node->next)
00493         node->flag &= ~NODE_TEST;
00494 
00495     for (link=snode->edittree->links.first; link; link=link->next) {
00496         if (link->fromnode && link->tonode && (link->fromnode->flag & NODE_SELECT))
00497             link->tonode->flag |= NODE_TEST;
00498     }
00499     
00500     for (node=snode->edittree->nodes.first; node; node=node->next) {
00501         if (node->flag & NODE_TEST)
00502             node->flag |= NODE_SELECT;
00503     }
00504     
00505     node_sort(snode->edittree);
00506     
00507     WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
00508     return OPERATOR_FINISHED;
00509 }
00510 
00511 void NODE_OT_select_linked_to(wmOperatorType *ot)
00512 {
00513     /* identifiers */
00514     ot->name = "Select Linked To";
00515     ot->description = "Select nodes linked to the selected ones";
00516     ot->idname = "NODE_OT_select_linked_to";
00517     
00518     /* api callbacks */
00519     ot->exec = node_select_linked_to_exec;
00520     ot->poll = ED_operator_node_active;
00521     
00522     /* flags */
00523     ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
00524 }
00525 
00526 /* ****** Select Linked From ****** */
00527 
00528 static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
00529 {
00530     SpaceNode *snode = CTX_wm_space_node(C);
00531     bNodeLink *link;
00532     bNode *node;
00533     
00534     for(node=snode->edittree->nodes.first; node; node=node->next)
00535         node->flag &= ~NODE_TEST;
00536 
00537     for(link=snode->edittree->links.first; link; link=link->next) {
00538         if(link->fromnode && link->tonode && (link->tonode->flag & NODE_SELECT))
00539             link->fromnode->flag |= NODE_TEST;
00540     }
00541     
00542     for(node=snode->edittree->nodes.first; node; node=node->next) {
00543         if(node->flag & NODE_TEST)
00544             node->flag |= NODE_SELECT;
00545     }
00546     
00547     node_sort(snode->edittree);
00548     
00549     WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
00550     return OPERATOR_FINISHED;
00551 }
00552 
00553 void NODE_OT_select_linked_from(wmOperatorType *ot)
00554 {
00555     /* identifiers */
00556     ot->name = "Select Linked From";
00557     ot->description = "Select nodes linked from the selected ones";
00558     ot->idname = "NODE_OT_select_linked_from";
00559     
00560     /* api callbacks */
00561     ot->exec = node_select_linked_from_exec;
00562     ot->poll = ED_operator_node_active;
00563     
00564     /* flags */
00565     ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
00566 }
00567 
00568 /* ****** Select Same Type ****** */
00569 
00570 static int node_select_same_type_exec(bContext *C, wmOperator *UNUSED(op))
00571 {
00572     SpaceNode *snode = CTX_wm_space_node(C);
00573 
00574     node_select_same_type(snode);
00575 
00576     node_sort(snode->edittree);
00577 
00578     WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
00579     return OPERATOR_FINISHED;
00580 }
00581 
00582 void NODE_OT_select_same_type(wmOperatorType *ot)
00583 {
00584     /* identifiers */
00585     ot->name = "Select Same Type";
00586     ot->description = "Select all the nodes of the same type";
00587     ot->idname = "NODE_OT_select_same_type";
00588     
00589     /* api callbacks */
00590     ot->exec = node_select_same_type_exec;
00591     ot->poll = ED_operator_node_active;
00592     
00593     /* flags */
00594     ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
00595 }
00596 
00597 /* ****** Select The Next/Prev Node Of The Same Type ****** */
00598 
00599 static int node_select_same_type_next_exec(bContext *C, wmOperator *UNUSED(op))
00600 {
00601     SpaceNode *snode = CTX_wm_space_node(C);
00602 
00603     node_select_same_type_np(snode, 0);
00604 
00605     node_sort(snode->edittree);
00606 
00607     WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
00608 
00609     return OPERATOR_FINISHED;
00610 }
00611 
00612 void NODE_OT_select_same_type_next(wmOperatorType *ot)
00613 {
00614     /* identifiers */
00615     ot->name = "Select Same Type Next";
00616     ot->description = "Select the next node of the same type";
00617     ot->idname = "NODE_OT_select_same_type_next";
00618     
00619     /* api callbacks */
00620     ot->exec = node_select_same_type_next_exec;
00621     ot->poll = ED_operator_node_active;
00622     
00623     /* flags */
00624     ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
00625 }
00626 
00627 static int node_select_same_type_prev_exec(bContext *C, wmOperator *UNUSED(op))
00628 {
00629     SpaceNode *snode = CTX_wm_space_node(C);
00630 
00631     node_select_same_type_np(snode, 1);
00632 
00633     node_sort(snode->edittree);
00634 
00635     WM_event_add_notifier(C, NC_NODE|NA_SELECTED, NULL);
00636     return OPERATOR_FINISHED;
00637 }
00638 
00639 void NODE_OT_select_same_type_prev(wmOperatorType *ot)
00640 {
00641     /* identifiers */
00642     ot->name = "Select Same Type Prev";
00643     ot->description = "Select the prev node of the same type";
00644     ot->idname = "NODE_OT_select_same_type_prev";
00645     
00646     /* api callbacks */
00647     ot->exec = node_select_same_type_prev_exec;
00648     ot->poll = ED_operator_node_active;
00649     
00650     /* flags */
00651     ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
00652 }