Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / space_outliner / space_outliner.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
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_outliner/space_outliner.c
28  *  \ingroup spoutliner
29  */
30
31
32 #include <string.h>
33 #include <stdio.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_utildefines.h"
39 #include "BLI_mempool.h"
40
41 #include "BKE_context.h"
42 #include "BKE_layer.h"
43 #include "BKE_screen.h"
44 #include "BKE_scene.h"
45 #include "BKE_outliner_treehash.h"
46
47 #include "ED_space_api.h"
48 #include "ED_screen.h"
49
50 #include "WM_api.h"
51 #include "WM_message.h"
52 #include "WM_types.h"
53
54 #include "BIF_gl.h"
55
56 #include "RNA_access.h"
57
58 #include "DNA_scene_types.h"
59 #include "DNA_object_types.h"
60
61 #include "UI_resources.h"
62 #include "UI_view2d.h"
63
64
65 #include "outliner_intern.h"
66
67 static void outliner_main_region_init(wmWindowManager *wm, ARegion *ar)
68 {
69         ListBase *lb;
70         wmKeyMap *keymap;
71         
72         /* make sure we keep the hide flags */
73         ar->v2d.scroll |= (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM);
74         ar->v2d.scroll &= ~(V2D_SCROLL_LEFT | V2D_SCROLL_TOP);  /* prevent any noise of past */
75         ar->v2d.scroll |= V2D_SCROLL_HORIZONTAL_HIDE;
76         ar->v2d.scroll |= V2D_SCROLL_VERTICAL_HIDE;
77
78         ar->v2d.align = (V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_POS_Y);
79         ar->v2d.keepzoom = (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_LIMITZOOM | V2D_KEEPASPECT);
80         ar->v2d.keeptot = V2D_KEEPTOT_STRICT;
81         ar->v2d.minzoom = ar->v2d.maxzoom = 1.0f;
82
83         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy);
84         
85         /* own keymap */
86         keymap = WM_keymap_find(wm->defaultconf, "Outliner", SPACE_OUTLINER, 0);
87         /* don't pass on view2d mask, it's always set with scrollbar space, hide fails */
88         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, NULL, &ar->winrct);
89
90         /* Add dropboxes */
91         lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW);
92         WM_event_add_dropbox_handler(&ar->handlers, lb);
93 }
94
95 static int outliner_parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
96 {
97         ARegion *ar = CTX_wm_region(C);
98         SpaceOops *soops = CTX_wm_space_outliner(C);
99         float fmval[2];
100         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
101
102         if (drag->type == WM_DRAG_ID) {
103                 ID *id = drag->poin;
104                 if (GS(id->name) == ID_OB) {
105                         /* Ensure item under cursor is valid drop target */
106                         TreeElement *te = outliner_dropzone_find(soops, fmval, true);
107                         TreeStoreElem *tselem = te ? TREESTORE(te) : NULL;
108
109                         if (!te) {
110                                 /* pass */
111                         }
112                         else if (te->idcode == ID_OB && tselem->type == 0) {
113                                 Scene *scene;
114                                 ID *te_id = tselem->id;
115
116                                 /* check if dropping self or parent */
117                                 if (te_id == id || (Object *)te_id == ((Object *)id)->parent)
118                                         return 0;
119
120                                 /* check that parent/child are both in the same scene */
121                                 scene = (Scene *)outliner_search_back(soops, te, ID_SCE);
122
123                                 /* currently outliner organized in a way that if there's no parent scene
124                                  * element for object it means that all displayed objects belong to
125                                  * active scene and parenting them is allowed (sergey)
126                                  */
127                                 if (!scene) {
128                                         return 1;
129                                 }
130                                 else {
131                                         for (ViewLayer *view_layer = scene->view_layers.first;
132                                              view_layer;
133                                              view_layer = view_layer->next)
134                                         {
135                                                 if (BKE_view_layer_base_find(view_layer, (Object *)id)) {
136                                                         return 1;
137                                                 }
138                                         }
139                                 }
140                         }
141                         else if (ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION)) {
142                                 /* support adding object from different scene to collection */
143                                 return 1;
144                         }
145                 }
146         }
147         return 0;
148 }
149
150 static void outliner_parent_drop_copy(wmDrag *drag, wmDropBox *drop)
151 {
152         ID *id = drag->poin;
153
154         RNA_string_set(drop->ptr, "child", id->name + 2);
155 }
156
157 static int outliner_parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event)
158 {
159         ARegion *ar = CTX_wm_region(C);
160         SpaceOops *soops = CTX_wm_space_outliner(C);
161         TreeElement *te = NULL;
162         float fmval[2];
163
164         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
165
166         if (!ELEM(soops->outlinevis, SO_SCENES, SO_GROUPS, SO_COLLECTIONS)) {
167                 return false;
168         }
169
170         if (drag->type == WM_DRAG_ID) {
171                 ID *id = drag->poin;
172                 if (GS(id->name) == ID_OB) {
173                         if (((Object *)id)->parent) {
174                                 if ((te = outliner_dropzone_find(soops, fmval, true))) {
175                                         TreeStoreElem *tselem = TREESTORE(te);
176
177                                         switch (te->idcode) {
178                                                 case ID_SCE:
179                                                         return (ELEM(tselem->type, TSE_R_LAYER_BASE, TSE_R_LAYER));
180                                                 case ID_OB:
181                                                         return (ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE));
182                                                 /* Other codes to ignore? */
183                                         }
184                                 }
185                                 return (te == NULL);
186                         }
187                 }
188         }
189         return 0;
190 }
191
192 static void outliner_parent_clear_copy(wmDrag *drag, wmDropBox *drop)
193 {
194         ID *id = drag->poin;
195         RNA_string_set(drop->ptr, "dragged_obj", id->name + 2);
196
197         /* Set to simple parent clear type. Avoid menus for drag and drop if possible.
198          * If desired, user can toggle the different "Clear Parent" types in the operator
199          * menu on tool shelf. */
200         RNA_enum_set(drop->ptr, "type", 0);
201 }
202
203 static int outliner_scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
204 {
205         ARegion *ar = CTX_wm_region(C);
206         SpaceOops *soops = CTX_wm_space_outliner(C);
207         float fmval[2];
208         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
209
210         if (drag->type == WM_DRAG_ID) {
211                 ID *id = drag->poin;
212                 if (GS(id->name) == ID_OB) {
213                         /* Ensure item under cursor is valid drop target */
214                         TreeElement *te = outliner_dropzone_find(soops, fmval, false);
215                         return (te && te->idcode == ID_SCE && TREESTORE(te)->type == 0);
216                 }
217         }
218         return 0;
219 }
220
221 static void outliner_scene_drop_copy(wmDrag *drag, wmDropBox *drop)
222 {
223         ID *id = drag->poin;
224
225         RNA_string_set(drop->ptr, "object", id->name + 2);
226 }
227
228 static int outliner_material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
229 {
230         ARegion *ar = CTX_wm_region(C);
231         SpaceOops *soops = CTX_wm_space_outliner(C);
232         float fmval[2];
233         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
234
235         if (drag->type == WM_DRAG_ID) {
236                 ID *id = drag->poin;
237                 if (GS(id->name) == ID_MA) {
238                         /* Ensure item under cursor is valid drop target */
239                         TreeElement *te = outliner_dropzone_find(soops, fmval, true);
240                         return (te && te->idcode == ID_OB && TREESTORE(te)->type == 0);
241                 }
242         }
243         return 0;
244 }
245
246 static void outliner_material_drop_copy(wmDrag *drag, wmDropBox *drop)
247 {
248         ID *id = drag->poin;
249
250         RNA_string_set(drop->ptr, "material", id->name + 2);
251 }
252
253 static int outliner_group_link_poll(bContext *C, wmDrag *drag, const wmEvent *event)
254 {
255         ARegion *ar = CTX_wm_region(C);
256         SpaceOops *soops = CTX_wm_space_outliner(C);
257         float fmval[2];
258         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
259
260         if (drag->type == WM_DRAG_ID) {
261                 ID *id = drag->poin;
262                 if (GS(id->name) == ID_OB) {
263                         /* Ensure item under cursor is valid drop target */
264                         TreeElement *te = outliner_dropzone_find(soops, fmval, true);
265                         return (te && te->idcode == ID_GR && TREESTORE(te)->type == 0);
266                 }
267         }
268         return 0;
269 }
270
271 static void outliner_group_link_copy(wmDrag *drag, wmDropBox *drop)
272 {
273         ID *id = drag->poin;
274         RNA_string_set(drop->ptr, "object", id->name + 2);
275 }
276
277 /* region dropbox definition */
278 static void outliner_dropboxes(void)
279 {
280         ListBase *lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW);
281
282         WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", outliner_parent_drop_poll, outliner_parent_drop_copy);
283         WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", outliner_parent_clear_poll, outliner_parent_clear_copy);
284         WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", outliner_scene_drop_poll, outliner_scene_drop_copy);
285         WM_dropbox_add(lb, "OUTLINER_OT_material_drop", outliner_material_drop_poll, outliner_material_drop_copy);
286         WM_dropbox_add(lb, "OUTLINER_OT_group_link", outliner_group_link_poll, outliner_group_link_copy);
287 }
288
289 static void outliner_main_region_draw(const bContext *C, ARegion *ar)
290 {
291         View2D *v2d = &ar->v2d;
292         View2DScrollers *scrollers;
293         
294         /* clear */
295         UI_ThemeClearColor(TH_BACK);
296         glClear(GL_COLOR_BUFFER_BIT);
297         
298         draw_outliner(C);
299         
300         /* reset view matrix */
301         UI_view2d_view_restore(C);
302         
303         /* scrollers */
304         scrollers = UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
305         UI_view2d_scrollers_draw(C, v2d, scrollers);
306         UI_view2d_scrollers_free(scrollers);
307 }
308
309
310 static void outliner_main_region_free(ARegion *UNUSED(ar))
311 {
312         
313 }
314
315 static void outliner_main_region_listener(
316         bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar,
317         wmNotifier *wmn, const Scene *UNUSED(scene))
318 {
319         /* context changes */
320         switch (wmn->category) {
321                 case NC_SCENE:
322                         switch (wmn->data) {
323                                 case ND_OB_ACTIVE:
324                                 case ND_OB_SELECT:
325                                 case ND_OB_VISIBLE:
326                                 case ND_OB_RENDER:
327                                 case ND_MODE:
328                                 case ND_KEYINGSET:
329                                 case ND_FRAME:
330                                 case ND_RENDER_OPTIONS:
331                                 case ND_SEQUENCER:
332                                 case ND_LAYER:
333                                 case ND_LAYER_CONTENT:
334                                 case ND_WORLD:
335                                 case ND_SCENEBROWSE:
336                                         ED_region_tag_redraw(ar);
337                                         break;
338                         }
339                         break;
340                 case NC_OBJECT:
341                         switch (wmn->data) {
342                                 case ND_TRANSFORM:
343                                         /* transform doesn't change outliner data */
344                                         break;
345                                 case ND_BONE_ACTIVE:
346                                 case ND_BONE_SELECT:
347                                 case ND_DRAW:
348                                 case ND_PARENT:
349                                 case ND_OB_SHADING:
350                                         ED_region_tag_redraw(ar);
351                                         break;
352                                 case ND_CONSTRAINT:
353                                         switch (wmn->action) {
354                                                 case NA_ADDED:
355                                                 case NA_REMOVED:
356                                                 case NA_RENAME:
357                                                         ED_region_tag_redraw(ar);
358                                                         break;
359                                         }
360                                         break;
361                                 case ND_MODIFIER:
362                                         /* all modifier actions now */
363                                         ED_region_tag_redraw(ar);
364                                         break;
365                                 default:
366                                         /* Trigger update for NC_OBJECT itself */
367                                         ED_region_tag_redraw(ar);
368                                         break;
369                         }
370                         break;
371                 case NC_GROUP:
372                         /* all actions now, todo: check outliner view mode? */
373                         ED_region_tag_redraw(ar);
374                         break;
375                 case NC_LAMP:
376                         /* For updating lamp icons, when changing lamp type */
377                         if (wmn->data == ND_LIGHTING_DRAW)
378                                 ED_region_tag_redraw(ar);
379                         break;
380                 case NC_SPACE:
381                         if (wmn->data == ND_SPACE_OUTLINER)
382                                 ED_region_tag_redraw(ar);
383                         break;
384                 case NC_ID:
385                         if (wmn->action == NA_RENAME)
386                                 ED_region_tag_redraw(ar);
387                         break;
388                 case NC_MATERIAL:
389                         switch (wmn->data) {
390                                 case ND_SHADING_LINKS:
391                                         ED_region_tag_redraw(ar);
392                                         break;
393                         }
394                         break;
395                 case NC_GEOM:
396                         switch (wmn->data) {
397                                 case ND_VERTEX_GROUP:
398                                 case ND_DATA:
399                                         ED_region_tag_redraw(ar);
400                                         break;
401                         }
402                         break;
403                 case NC_ANIMATION:
404                         switch (wmn->data) {
405                                 case ND_NLA_ACTCHANGE:
406                                 case ND_KEYFRAME:
407                                         ED_region_tag_redraw(ar);
408                                         break;
409                                 case ND_ANIMCHAN:
410                                         if (wmn->action == NA_SELECTED)
411                                                 ED_region_tag_redraw(ar);
412                                         break;
413                         }
414                         break;
415                 case NC_GPENCIL:
416                         if (ELEM(wmn->action, NA_EDITED, NA_SELECTED))
417                                 ED_region_tag_redraw(ar);
418                         break;
419                 case NC_SCREEN:
420                         if (ELEM(wmn->data, ND_LAYER)) {
421                                 ED_region_tag_redraw(ar);
422                         }
423                         break;
424         }
425         
426 }
427
428 static void outliner_main_region_message_subscribe(
429         const struct bContext *UNUSED(C),
430         struct WorkSpace *UNUSED(workspace), struct Scene *UNUSED(scene),
431         struct bScreen *UNUSED(screen), struct ScrArea *sa, struct ARegion *ar,
432         struct wmMsgBus *mbus)
433 {
434         SpaceOops *soops = sa->spacedata.first;
435         wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
436                 .owner = ar,
437                 .user_data = ar,
438                 .notify = ED_region_do_msg_notify_tag_redraw,
439         };
440
441         if (soops->outlinevis == SO_COLLECTIONS) {
442                 WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw);
443         }
444 }
445
446
447 /* ************************ header outliner area region *********************** */
448
449 /* add handlers, stuff you only do once or on area/region changes */
450 static void outliner_header_region_init(wmWindowManager *UNUSED(wm), ARegion *ar)
451 {
452         ED_region_header_init(ar);
453 }
454
455 static void outliner_header_region_draw(const bContext *C, ARegion *ar)
456 {
457         ED_region_header(C, ar);
458 }
459
460 static void outliner_header_region_free(ARegion *UNUSED(ar))
461 {
462 }
463
464 static void outliner_header_region_listener(
465         bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar,
466         wmNotifier *wmn, const Scene *UNUSED(scene))
467 {
468         /* context changes */
469         switch (wmn->category) {
470                 case NC_SCENE:
471                         if (wmn->data == ND_KEYINGSET)
472                                 ED_region_tag_redraw(ar);
473                         break;
474                 case NC_SPACE:
475                         if (wmn->data == ND_SPACE_OUTLINER)
476                                 ED_region_tag_redraw(ar);
477                         break;
478         }
479 }
480
481 /* ******************** default callbacks for outliner space ***************** */
482
483 static SpaceLink *outliner_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene))
484 {
485         ARegion *ar;
486         SpaceOops *soutliner;
487
488         soutliner = MEM_callocN(sizeof(SpaceOops), "initoutliner");
489         soutliner->spacetype = SPACE_OUTLINER;
490         
491         /* header */
492         ar = MEM_callocN(sizeof(ARegion), "header for outliner");
493         
494         BLI_addtail(&soutliner->regionbase, ar);
495         ar->regiontype = RGN_TYPE_HEADER;
496         ar->alignment = RGN_ALIGN_BOTTOM;
497         
498         /* main region */
499         ar = MEM_callocN(sizeof(ARegion), "main region for outliner");
500         
501         BLI_addtail(&soutliner->regionbase, ar);
502         ar->regiontype = RGN_TYPE_WINDOW;
503         
504         return (SpaceLink *)soutliner;
505 }
506
507 /* not spacelink itself */
508 static void outliner_free(SpaceLink *sl)
509 {
510         SpaceOops *soutliner = (SpaceOops *)sl;
511         
512         outliner_free_tree(&soutliner->tree);
513         if (soutliner->treestore) {
514                 BLI_mempool_destroy(soutliner->treestore);
515         }
516         if (soutliner->treehash) {
517                 BKE_outliner_treehash_free(soutliner->treehash);
518         }
519 }
520
521 /* spacetype; init callback */
522 static void outliner_init(wmWindowManager *UNUSED(wm), ScrArea *UNUSED(sa))
523 {
524         
525 }
526
527 static SpaceLink *outliner_duplicate(SpaceLink *sl)
528 {
529         SpaceOops *soutliner = (SpaceOops *)sl;
530         SpaceOops *soutlinern = MEM_dupallocN(soutliner);
531
532         BLI_listbase_clear(&soutlinern->tree);
533         soutlinern->treestore = NULL;
534         soutlinern->treehash = NULL;
535         
536         return (SpaceLink *)soutlinern;
537 }
538
539 static void outliner_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id)
540 {
541         SpaceOops *so = (SpaceOops *)slink;
542
543         /* Some early out checks. */
544         if (!TREESTORE_ID_TYPE(old_id)) {
545                 return;  /* ID type is not used by outilner... */
546         }
547
548         if (so->search_tse.id == old_id) {
549                 so->search_tse.id = new_id;
550         }
551
552         if (so->treestore) {
553                 TreeStoreElem *tselem;
554                 BLI_mempool_iter iter;
555                 bool changed = false;
556
557                 BLI_mempool_iternew(so->treestore, &iter);
558                 while ((tselem = BLI_mempool_iterstep(&iter))) {
559                         if (tselem->id == old_id) {
560                                 tselem->id = new_id;
561                                 changed = true;
562                         }
563                 }
564                 if (so->treehash && changed) {
565                         /* rebuild hash table, because it depends on ids too */
566                         /* postpone a full rebuild because this can be called many times on-free */
567                         so->storeflag |= SO_TREESTORE_REBUILD;
568                 }
569         }
570 }
571
572 /* only called once, from space_api/spacetypes.c */
573 void ED_spacetype_outliner(void)
574 {
575         SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype time");
576         ARegionType *art;
577         
578         st->spaceid = SPACE_OUTLINER;
579         strncpy(st->name, "Outliner", BKE_ST_MAXNAME);
580         
581         st->new = outliner_new;
582         st->free = outliner_free;
583         st->init = outliner_init;
584         st->duplicate = outliner_duplicate;
585         st->operatortypes = outliner_operatortypes;
586         st->keymap = outliner_keymap;
587         st->dropboxes = outliner_dropboxes;
588         st->id_remap = outliner_id_remap;
589
590         /* regions: main window */
591         art = MEM_callocN(sizeof(ARegionType), "spacetype outliner region");
592         art->regionid = RGN_TYPE_WINDOW;
593         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES;
594         
595         art->init = outliner_main_region_init;
596         art->draw = outliner_main_region_draw;
597         art->free = outliner_main_region_free;
598         art->listener = outliner_main_region_listener;
599         art->message_subscribe = outliner_main_region_message_subscribe;
600         BLI_addhead(&st->regiontypes, art);
601         
602         /* regions: header */
603         art = MEM_callocN(sizeof(ARegionType), "spacetype outliner header region");
604         art->regionid = RGN_TYPE_HEADER;
605         art->prefsizey = HEADERY;
606         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
607         
608         art->init = outliner_header_region_init;
609         art->draw = outliner_header_region_draw;
610         art->free = outliner_header_region_free;
611         art->listener = outliner_header_region_listener;
612         BLI_addhead(&st->regiontypes, art);
613         
614         BKE_spacetype_register(st);
615 }
616