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