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