Fix state losses for recursive outliner trees (e.g. datablocks editor)
[blender-staging.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_math.h"
39 #include "BLI_utildefines.h"
40 #include "BLI_mempool.h"
41 #include "BLI_ghash.h"
42
43 #include "BKE_context.h"
44 #include "BKE_screen.h"
45 #include "BKE_scene.h"
46 #include "BKE_treehash.h"
47
48 #include "ED_space_api.h"
49 #include "ED_screen.h"
50
51 #include "WM_api.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_area_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 = (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, 1);
107
108                         if (te && te->idcode == ID_OB && TREESTORE(te)->type == 0) {
109                                 Scene *scene;
110                                 ID *te_id = TREESTORE(te)->id;
111
112                                 /* check if dropping self or parent */
113                                 if (te_id == id || (Object *)te_id == ((Object *)id)->parent)
114                                         return 0;
115
116                                 /* check that parent/child are both in the same scene */
117                                 scene = (Scene *)outliner_search_back(soops, te, ID_SCE);
118
119                                 /* currently outliner organized in a way that if there's no parent scene
120                                  * element for object it means that all displayed objects belong to
121                                  * active scene and parenting them is allowed (sergey)
122                                  */
123                                 if (!scene || BKE_scene_base_find(scene, (Object *)id)) {
124                                         return 1;
125                                 }
126                         }
127                 }
128         }
129         return 0;
130 }
131
132 static void outliner_parent_drop_copy(wmDrag *drag, wmDropBox *drop)
133 {
134         ID *id = (ID *)drag->poin;
135
136         RNA_string_set(drop->ptr, "child", id->name + 2);
137 }
138
139 static int outliner_parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event)
140 {
141         ARegion *ar = CTX_wm_region(C);
142         SpaceOops *soops = CTX_wm_space_outliner(C);
143         TreeElement *te = NULL;
144         float fmval[2];
145
146         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
147
148         if (!ELEM4(soops->outlinevis, SO_ALL_SCENES, SO_CUR_SCENE, SO_VISIBLE, SO_GROUPS)) {
149                 return FALSE;
150         }
151
152         if (drag->type == WM_DRAG_ID) {
153                 ID *id = (ID *)drag->poin;
154                 if (GS(id->name) == ID_OB) {
155                         if (((Object *)id)->parent) {
156                                 if ((te = outliner_dropzone_find(soops, fmval, 1))) {
157                                         TreeStoreElem *tselem = TREESTORE(te);
158
159                                         switch (te->idcode) {
160                                                 case ID_SCE:
161                                                         return (ELEM3(tselem->type, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS));
162                                                 case ID_OB:
163                                                         return (ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE));
164                                                 /* Other codes to ignore? */
165                                         }
166                                 }
167                                 return (te == NULL);
168                         }
169                 }
170         }
171         return 0;
172 }
173
174 static void outliner_parent_clear_copy(wmDrag *drag, wmDropBox *drop)
175 {
176         ID *id = (ID *)drag->poin;
177         RNA_string_set(drop->ptr, "dragged_obj", id->name + 2);
178
179         /* Set to simple parent clear type. Avoid menus for drag and drop if possible.
180          * If desired, user can toggle the different "Clear Parent" types in the operator
181          * menu on tool shelf. */
182         RNA_enum_set(drop->ptr, "type", 0);
183 }
184
185 static int outliner_scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
186 {
187         ARegion *ar = CTX_wm_region(C);
188         SpaceOops *soops = CTX_wm_space_outliner(C);
189         float fmval[2];
190         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
191
192         if (drag->type == WM_DRAG_ID) {
193                 ID *id = (ID *)drag->poin;
194                 if (GS(id->name) == ID_OB) {
195                         /* Ensure item under cursor is valid drop target */
196                         TreeElement *te = outliner_dropzone_find(soops, fmval, 0);
197                         return (te && te->idcode == ID_SCE && TREESTORE(te)->type == 0);
198                 }
199         }
200         return 0;
201 }
202
203 static void outliner_scene_drop_copy(wmDrag *drag, wmDropBox *drop)
204 {
205         ID *id = (ID *)drag->poin;
206
207         RNA_string_set(drop->ptr, "object", id->name + 2);
208 }
209
210 static int outliner_material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
211 {
212         ARegion *ar = CTX_wm_region(C);
213         SpaceOops *soops = CTX_wm_space_outliner(C);
214         float fmval[2];
215         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
216
217         if (drag->type == WM_DRAG_ID) {
218                 ID *id = (ID *)drag->poin;
219                 if (GS(id->name) == ID_MA) {
220                         /* Ensure item under cursor is valid drop target */
221                         TreeElement *te = outliner_dropzone_find(soops, fmval, 1);
222                         return (te && te->idcode == ID_OB && TREESTORE(te)->type == 0);
223                 }
224         }
225         return 0;
226 }
227
228 static void outliner_material_drop_copy(wmDrag *drag, wmDropBox *drop)
229 {
230         ID *id = (ID *)drag->poin;
231
232         RNA_string_set(drop->ptr, "material", id->name + 2);
233 }
234
235 /* region dropbox definition */
236 static void outliner_dropboxes(void)
237 {
238         ListBase *lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW);
239
240         WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", outliner_parent_drop_poll, outliner_parent_drop_copy);
241         WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", outliner_parent_clear_poll, outliner_parent_clear_copy);
242         WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", outliner_scene_drop_poll, outliner_scene_drop_copy);
243         WM_dropbox_add(lb, "OUTLINER_OT_material_drop", outliner_material_drop_poll, outliner_material_drop_copy);
244 }
245
246 static void outliner_main_area_draw(const bContext *C, ARegion *ar)
247 {
248         View2D *v2d = &ar->v2d;
249         View2DScrollers *scrollers;
250         
251         /* clear */
252         UI_ThemeClearColor(TH_BACK);
253         glClear(GL_COLOR_BUFFER_BIT);
254         
255         draw_outliner(C);
256         
257         /* reset view matrix */
258         UI_view2d_view_restore(C);
259         
260         /* scrollers */
261         scrollers = UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
262         UI_view2d_scrollers_draw(C, v2d, scrollers);
263         UI_view2d_scrollers_free(scrollers);
264 }
265
266
267 static void outliner_main_area_free(ARegion *UNUSED(ar))
268 {
269         
270 }
271
272 static void outliner_main_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn)
273 {
274         /* context changes */
275         switch (wmn->category) {
276                 case NC_SCENE:
277                         switch (wmn->data) {
278                                 case ND_OB_ACTIVE:
279                                 case ND_OB_SELECT:
280                                 case ND_OB_VISIBLE:
281                                 case ND_OB_RENDER:
282                                 case ND_MODE:
283                                 case ND_KEYINGSET:
284                                 case ND_FRAME:
285                                 case ND_RENDER_OPTIONS:
286                                 case ND_LAYER:
287                                 case ND_WORLD:
288                                         ED_region_tag_redraw(ar);
289                                         break;
290                         }
291                         break;
292                 case NC_OBJECT:
293                         switch (wmn->data) {
294                                 case ND_TRANSFORM:
295                                         /* transform doesn't change outliner data */
296                                         break;
297                                 case ND_BONE_ACTIVE:
298                                 case ND_BONE_SELECT:
299                                 case ND_DRAW:
300                                 case ND_PARENT:
301                                 case ND_OB_SHADING:
302                                         ED_region_tag_redraw(ar);
303                                         break;
304                                 case ND_CONSTRAINT:
305                                         switch (wmn->action) {
306                                                 case NA_ADDED:
307                                                 case NA_REMOVED:
308                                                 case NA_RENAME:
309                                                         ED_region_tag_redraw(ar);
310                                                         break;
311                                         }
312                                         break;
313                                 case ND_MODIFIER:
314                                         /* all modifier actions now */
315                                         ED_region_tag_redraw(ar);
316                                         break;
317                                 default:
318                                         /* Trigger update for NC_OBJECT itself */
319                                         ED_region_tag_redraw(ar);
320                                         break;
321                         }
322                         break;
323                 case NC_GROUP:
324                         /* all actions now, todo: check outliner view mode? */
325                         ED_region_tag_redraw(ar);
326                         break;
327                 case NC_LAMP:
328                         /* For updating lamp icons, when changing lamp type */
329                         if (wmn->data == ND_LIGHTING_DRAW)
330                                 ED_region_tag_redraw(ar);
331                         break;
332                 case NC_SPACE:
333                         if (wmn->data == ND_SPACE_OUTLINER)
334                                 ED_region_tag_redraw(ar);
335                         break;
336                 case NC_ID:
337                         if (wmn->action == NA_RENAME)
338                                 ED_region_tag_redraw(ar);
339                         break;
340                 case NC_MATERIAL:
341                         switch (wmn->data) {
342                                 case ND_SHADING_LINKS:
343                                         ED_region_tag_redraw(ar);
344                                         break;
345                         }
346                         break;
347                 case NC_GEOM:
348                         switch (wmn->data) {
349                                 case ND_VERTEX_GROUP:
350                                         ED_region_tag_redraw(ar);
351                                         break;
352                         }
353                         break;
354                 case NC_ANIMATION:
355                         switch (wmn->data) {
356                                 case ND_NLA_ACTCHANGE:
357                                 case ND_KEYFRAME:
358                                         ED_region_tag_redraw(ar);
359                                         break;
360                                 case ND_ANIMCHAN:
361                                         if (wmn->action == NA_SELECTED)
362                                                 ED_region_tag_redraw(ar);
363                                         break;
364                         }
365                         break;
366         }
367         
368 }
369
370
371 /* ************************ header outliner area region *********************** */
372
373 /* add handlers, stuff you only do once or on area/region changes */
374 static void outliner_header_area_init(wmWindowManager *UNUSED(wm), ARegion *ar)
375 {
376         ED_region_header_init(ar);
377 }
378
379 static void outliner_header_area_draw(const bContext *C, ARegion *ar)
380 {
381         ED_region_header(C, ar);
382 }
383
384 static void outliner_header_area_free(ARegion *UNUSED(ar))
385 {
386 }
387
388 static void outliner_header_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn)
389 {
390         /* context changes */
391         switch (wmn->category) {
392                 case NC_SCENE:
393                         if (wmn->data == ND_KEYINGSET)
394                                 ED_region_tag_redraw(ar);
395                         break;
396                 case NC_SPACE:
397                         if (wmn->data == ND_SPACE_OUTLINER)
398                                 ED_region_tag_redraw(ar);
399                         break;
400         }
401 }
402
403 /* ******************** default callbacks for outliner space ***************** */
404
405 static SpaceLink *outliner_new(const bContext *UNUSED(C))
406 {
407         ARegion *ar;
408         SpaceOops *soutliner;
409
410         soutliner = MEM_callocN(sizeof(SpaceOops), "initoutliner");
411         soutliner->spacetype = SPACE_OUTLINER;
412         
413         /* header */
414         ar = MEM_callocN(sizeof(ARegion), "header for outliner");
415         
416         BLI_addtail(&soutliner->regionbase, ar);
417         ar->regiontype = RGN_TYPE_HEADER;
418         ar->alignment = RGN_ALIGN_BOTTOM;
419         
420         /* main area */
421         ar = MEM_callocN(sizeof(ARegion), "main area for outliner");
422         
423         BLI_addtail(&soutliner->regionbase, ar);
424         ar->regiontype = RGN_TYPE_WINDOW;
425         
426         return (SpaceLink *)soutliner;
427 }
428
429 /* not spacelink itself */
430 static void outliner_free(SpaceLink *sl)
431 {
432         SpaceOops *soutliner = (SpaceOops *)sl;
433         
434         outliner_free_tree(&soutliner->tree);
435         if (soutliner->treestore) {
436                 BLI_mempool_destroy(soutliner->treestore);
437         }
438         if (soutliner->treehash) {
439                 BKE_treehash_free(soutliner->treehash);
440         }
441 }
442
443 /* spacetype; init callback */
444 static void outliner_init(wmWindowManager *UNUSED(wm), ScrArea *UNUSED(sa))
445 {
446         
447 }
448
449 static SpaceLink *outliner_duplicate(SpaceLink *sl)
450 {
451         SpaceOops *soutliner = (SpaceOops *)sl;
452         SpaceOops *soutlinern = MEM_dupallocN(soutliner);
453
454         soutlinern->tree.first = soutlinern->tree.last = NULL;
455         soutlinern->treestore = NULL;
456         soutlinern->treehash = NULL;
457         
458         return (SpaceLink *)soutlinern;
459 }
460
461 /* only called once, from space_api/spacetypes.c */
462 void ED_spacetype_outliner(void)
463 {
464         SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype time");
465         ARegionType *art;
466         
467         st->spaceid = SPACE_OUTLINER;
468         strncpy(st->name, "Outliner", BKE_ST_MAXNAME);
469         
470         st->new = outliner_new;
471         st->free = outliner_free;
472         st->init = outliner_init;
473         st->duplicate = outliner_duplicate;
474         st->operatortypes = outliner_operatortypes;
475         st->keymap = outliner_keymap;
476         st->dropboxes = outliner_dropboxes;
477         
478         /* regions: main window */
479         art = MEM_callocN(sizeof(ARegionType), "spacetype time region");
480         art->regionid = RGN_TYPE_WINDOW;
481         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D;
482         
483         art->init = outliner_main_area_init;
484         art->draw = outliner_main_area_draw;
485         art->free = outliner_main_area_free;
486         art->listener = outliner_main_area_listener;
487         BLI_addhead(&st->regiontypes, art);
488         
489         /* regions: header */
490         art = MEM_callocN(sizeof(ARegionType), "spacetype time header region");
491         art->regionid = RGN_TYPE_HEADER;
492         art->prefsizey = HEADERY;
493         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER;
494         
495         art->init = outliner_header_area_init;
496         art->draw = outliner_header_area_draw;
497         art->free = outliner_header_area_free;
498         art->listener = outliner_header_area_listener;
499         BLI_addhead(&st->regiontypes, art);
500         
501         BKE_spacetype_register(st);
502 }
503