code cleanup: rename editmode functions so we have ED_object_editmode_load/enter...
[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_rand.h"
40 #include "BLI_utildefines.h"
41
42 #include "BKE_context.h"
43 #include "BKE_screen.h"
44 #include "BKE_scene.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_area_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         TreeElement *te = NULL;
98         float fmval[2];
99         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
100
101         if (drag->type == WM_DRAG_ID) {
102                 ID *id = (ID *)drag->poin;
103                 if (GS(id->name) == ID_OB) {
104                         /* Ensure item under cursor is valid drop target */
105                         /* Find object hovered over */
106                         for (te = soops->tree.first; te; te = te->next) {
107                                 TreeElement *te_valid;
108                                 te_valid = outliner_dropzone_parent(C, event, te, fmval);
109                                 if (te_valid) {
110                                         /* check that parent/child are both in the same scene */
111                                         Scene *scene = (Scene *)outliner_search_back(soops, te_valid, ID_SCE);
112
113                                         if (!scene) {
114                                                 /* currently outlier organized in a way, that if there's no parent scene
115                                                  * element for object it means that all displayed objects belong to
116                                                  * active scene and parenting them is allowed (sergey)
117                                                  */
118                                                 return 1;
119                                         }
120
121                                         if (scene && BKE_scene_base_find(scene, (Object *)id)) {
122                                                 return 1;
123                                         }
124                                 }
125                         }
126                 }
127         }
128         return 0;
129 }
130
131 static void outliner_parent_drop_copy(wmDrag *drag, wmDropBox *drop)
132 {
133         ID *id = (ID *)drag->poin;
134
135         RNA_string_set(drop->ptr, "child", id->name + 2);
136 }
137
138 static int outliner_parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event)
139 {
140         ARegion *ar = CTX_wm_region(C);
141         SpaceOops *soops = CTX_wm_space_outliner(C);
142         TreeElement *te = NULL;
143         float fmval[2];
144
145         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
146
147         if (drag->type == WM_DRAG_ID) {
148                 ID *id = (ID *)drag->poin;
149                 if (GS(id->name) == ID_OB) {
150                         if (((Object *)id)->parent == NULL) {
151                                 return 0;
152                         }
153                         /* Ensure location under cursor is valid dropzone */
154                         for (te = soops->tree.first; te; te = te->next) {
155                                 if (outliner_dropzone_parent_clear(C, event, te, fmval)) return 1;
156                         }
157                         /* Check if mouse cursor is below the tree */
158                         te = soops->tree.last;
159                         while (((te->flag & TE_LAZY_CLOSED) == 0) && (te->subtree.last)) {
160                                 te = te->subtree.last;
161                         }
162                         if (fmval[1] < te->ys) return 1;
163                 }
164         }
165         return 0;
166 }
167
168 static void outliner_parent_clear_copy(wmDrag *drag, wmDropBox *drop)
169 {
170         ID *id = (ID *)drag->poin;
171         RNA_string_set(drop->ptr, "dragged_obj", id->name + 2);
172
173         /* Set to simple parent clear type. Avoid menus for drag and drop if possible.
174          * If desired, user can toggle the different "Clear Parent" types in the operator
175          * menu on tool shelf. */
176         RNA_enum_set(drop->ptr, "type", 0);
177 }
178
179 static int outliner_scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
180 {
181         ARegion *ar = CTX_wm_region(C);
182         SpaceOops *soops = CTX_wm_space_outliner(C);
183         TreeElement *te = NULL;
184         float fmval[2];
185         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
186
187         if (drag->type == WM_DRAG_ID) {
188                 ID *id = (ID *)drag->poin;
189                 if (GS(id->name) == ID_OB) {
190                         /* Ensure item under cursor is valid drop target */
191                         /* Find object hovered over */
192                         for (te = soops->tree.first; te; te = te->next) {
193                                 if (outliner_dropzone_scene(C, event, te, fmval))
194                                         return 1;
195                         }
196                 }
197         }
198         return 0;
199 }
200
201 static void outliner_scene_drop_copy(wmDrag *drag, wmDropBox *drop)
202 {
203         ID *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         TreeElement *te = NULL;
213         float fmval[2];
214         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
215
216         if (drag->type == WM_DRAG_ID) {
217                 ID *id = (ID *)drag->poin;
218                 if (GS(id->name) == ID_MA) {
219                         /* Ensure item under cursor is valid drop target */
220                         /* Find object hovered over */
221                         for (te = soops->tree.first; te; te = te->next) {
222                                 if (outliner_dropzone_parent(C, event, te, fmval))
223                                         return 1;
224                         }
225                 }
226         }
227         return 0;
228 }
229
230 static void outliner_material_drop_copy(wmDrag *drag, wmDropBox *drop)
231 {
232         ID *id = (ID *)drag->poin;
233
234         RNA_string_set(drop->ptr, "material", id->name + 2);
235 }
236
237 /* region dropbox definition */
238 static void outliner_dropboxes(void)
239 {
240         ListBase *lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW);
241
242         WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", outliner_parent_drop_poll, outliner_parent_drop_copy);
243         WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", outliner_parent_clear_poll, outliner_parent_clear_copy);
244         WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", outliner_scene_drop_poll, outliner_scene_drop_copy);
245         WM_dropbox_add(lb, "OUTLINER_OT_material_drop", outliner_material_drop_poll, outliner_material_drop_copy);
246 }
247
248 static void outliner_main_area_draw(const bContext *C, ARegion *ar)
249 {
250         View2D *v2d = &ar->v2d;
251         View2DScrollers *scrollers;
252         
253         /* clear */
254         UI_ThemeClearColor(TH_BACK);
255         glClear(GL_COLOR_BUFFER_BIT);
256         
257         draw_outliner(C);
258         
259         /* reset view matrix */
260         UI_view2d_view_restore(C);
261         
262         /* scrollers */
263         scrollers = UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
264         UI_view2d_scrollers_draw(C, v2d, scrollers);
265         UI_view2d_scrollers_free(scrollers);
266 }
267
268
269 static void outliner_main_area_free(ARegion *UNUSED(ar))
270 {
271         
272 }
273
274 static void outliner_main_area_listener(ARegion *ar, wmNotifier *wmn)
275 {
276         /* context changes */
277         switch (wmn->category) {
278                 case NC_SCENE:
279                         switch (wmn->data) {
280                                 case ND_OB_ACTIVE:
281                                 case ND_OB_SELECT:
282                                 case ND_OB_VISIBLE:
283                                 case ND_OB_RENDER:
284                                 case ND_MODE:
285                                 case ND_KEYINGSET:
286                                 case ND_FRAME:
287                                 case ND_RENDER_OPTIONS:
288                                 case ND_LAYER:
289                                 case ND_WORLD:
290                                         ED_region_tag_redraw(ar);
291                                         break;
292                         }
293                         break;
294                 case NC_OBJECT:
295                         switch (wmn->data) {
296                                 case ND_TRANSFORM:
297                                         /* transform doesn't change outliner data */
298                                         break;
299                                 case ND_BONE_ACTIVE:
300                                 case ND_BONE_SELECT:
301                                 case ND_DRAW:
302                                 case ND_PARENT:
303                                 case ND_OB_SHADING:
304                                         ED_region_tag_redraw(ar);
305                                         break;
306                                 case ND_CONSTRAINT:
307                                         switch (wmn->action) {
308                                                 case NA_ADDED:
309                                                 case NA_REMOVED:
310                                                 case NA_RENAME:
311                                                         ED_region_tag_redraw(ar);
312                                                         break;
313                                         }
314                                         break;
315                                 case ND_MODIFIER:
316                                         /* all modifier actions now */
317                                         ED_region_tag_redraw(ar);
318                                         break;
319                                 default:
320                                         /* Trigger update for NC_OBJECT itself */
321                                         ED_region_tag_redraw(ar);
322                                         break;
323                         }
324                         break;
325                 case NC_GROUP:
326                         /* all actions now, todo: check outliner view mode? */
327                         ED_region_tag_redraw(ar);
328                         break;
329                 case NC_LAMP:
330                         /* For updating lamp icons, when changing lamp type */
331                         if (wmn->data == ND_LIGHTING_DRAW)
332                                 ED_region_tag_redraw(ar);
333                         break;
334                 case NC_SPACE:
335                         if (wmn->data == ND_SPACE_OUTLINER)
336                                 ED_region_tag_redraw(ar);
337                         break;
338                 case NC_ID:
339                         if (wmn->action == NA_RENAME)
340                                 ED_region_tag_redraw(ar);
341                         break;
342                 case NC_MATERIAL:
343                         switch (wmn->data) {
344                                 case ND_SHADING_LINKS:
345                                         ED_region_tag_redraw(ar);
346                                         break;
347                         }
348                         break;
349                 case NC_GEOM:
350                         switch (wmn->data) {
351                                 case ND_VERTEX_GROUP:
352                                         ED_region_tag_redraw(ar);
353                                         break;
354                         }
355                         break;
356                 case NC_ANIMATION:
357                         switch (wmn->data) {
358                                 case ND_NLA_ACTCHANGE:
359                                 case ND_KEYFRAME:
360                                         ED_region_tag_redraw(ar);
361                                         break;
362                                 case ND_ANIMCHAN:
363                                         if (wmn->action == NA_SELECTED)
364                                                 ED_region_tag_redraw(ar);
365                                         break;
366                         }
367                         break;
368         }
369         
370 }
371
372
373 /* ************************ header outliner area region *********************** */
374
375 /* add handlers, stuff you only do once or on area/region changes */
376 static void outliner_header_area_init(wmWindowManager *UNUSED(wm), ARegion *ar)
377 {
378         ED_region_header_init(ar);
379 }
380
381 static void outliner_header_area_draw(const bContext *C, ARegion *ar)
382 {
383         ED_region_header(C, ar);
384 }
385
386 static void outliner_header_area_free(ARegion *UNUSED(ar))
387 {
388 }
389
390 static void outliner_header_area_listener(ARegion *ar, wmNotifier *wmn)
391 {
392         /* context changes */
393         switch (wmn->category) {
394                 case NC_SCENE:
395                         if (wmn->data == ND_KEYINGSET)
396                                 ED_region_tag_redraw(ar);
397                         break;
398                 case NC_SPACE:
399                         if (wmn->data == ND_SPACE_OUTLINER)
400                                 ED_region_tag_redraw(ar);
401                         break;
402         }
403 }
404
405 /* ******************** default callbacks for outliner space ***************** */
406
407 static SpaceLink *outliner_new(const bContext *UNUSED(C))
408 {
409         ARegion *ar;
410         SpaceOops *soutliner;
411
412         soutliner = MEM_callocN(sizeof(SpaceOops), "initoutliner");
413         soutliner->spacetype = SPACE_OUTLINER;
414         
415         /* header */
416         ar = MEM_callocN(sizeof(ARegion), "header for outliner");
417         
418         BLI_addtail(&soutliner->regionbase, ar);
419         ar->regiontype = RGN_TYPE_HEADER;
420         ar->alignment = RGN_ALIGN_BOTTOM;
421         
422         /* main area */
423         ar = MEM_callocN(sizeof(ARegion), "main area for outliner");
424         
425         BLI_addtail(&soutliner->regionbase, ar);
426         ar->regiontype = RGN_TYPE_WINDOW;
427         
428         return (SpaceLink *)soutliner;
429 }
430
431 /* not spacelink itself */
432 static void outliner_free(SpaceLink *sl)
433 {
434         SpaceOops *soutliner = (SpaceOops *)sl;
435         
436         outliner_free_tree(&soutliner->tree);
437         if (soutliner->treestore) {
438                 if (soutliner->treestore->data) MEM_freeN(soutliner->treestore->data);
439                 MEM_freeN(soutliner->treestore);
440         }
441         
442 }
443
444 /* spacetype; init callback */
445 static void outliner_init(wmWindowManager *UNUSED(wm), ScrArea *UNUSED(sa))
446 {
447         
448 }
449
450 static SpaceLink *outliner_duplicate(SpaceLink *sl)
451 {
452         SpaceOops *soutliner = (SpaceOops *)sl;
453         SpaceOops *soutlinern = MEM_dupallocN(soutliner);
454
455         soutlinern->tree.first = soutlinern->tree.last = NULL;
456         soutlinern->treestore = 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