VSE: Better handling of effect strip splitting
[blender.git] / source / blender / editors / space_outliner / space_outliner.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spoutliner
22  */
23
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "MEM_guardedalloc.h"
28
29 #include "BLI_blenlib.h"
30 #include "BLI_mempool.h"
31 #include "BLI_utildefines.h"
32
33 #include "BKE_context.h"
34 #include "BKE_outliner_treehash.h"
35 #include "BKE_screen.h"
36
37 #include "ED_screen.h"
38 #include "ED_space_api.h"
39
40 #include "WM_api.h"
41 #include "WM_message.h"
42 #include "WM_types.h"
43
44 #include "RNA_access.h"
45
46 #include "DNA_object_types.h"
47 #include "DNA_scene_types.h"
48
49 #include "UI_resources.h"
50 #include "UI_view2d.h"
51
52 #include "outliner_intern.h"
53 #include "tree/tree_display.h"
54
55 static void outliner_main_region_init(wmWindowManager *wm, ARegion *region)
56 {
57   ListBase *lb;
58   wmKeyMap *keymap;
59
60   /* make sure we keep the hide flags */
61   region->v2d.scroll |= (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM);
62   region->v2d.scroll &= ~(V2D_SCROLL_LEFT | V2D_SCROLL_TOP); /* prevent any noise of past */
63   region->v2d.scroll |= V2D_SCROLL_HORIZONTAL_HIDE;
64   region->v2d.scroll |= V2D_SCROLL_VERTICAL_HIDE;
65
66   region->v2d.align = (V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_POS_Y);
67   region->v2d.keepzoom = (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_LIMITZOOM | V2D_KEEPASPECT);
68   region->v2d.keeptot = V2D_KEEPTOT_STRICT;
69   region->v2d.minzoom = region->v2d.maxzoom = 1.0f;
70
71   UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_LIST, region->winx, region->winy);
72
73   /* own keymap */
74   keymap = WM_keymap_ensure(wm->defaultconf, "Outliner", SPACE_OUTLINER, 0);
75   WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
76
77   /* Add dropboxes */
78   lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW);
79   WM_event_add_dropbox_handler(&region->handlers, lb);
80 }
81
82 static void outliner_main_region_draw(const bContext *C, ARegion *region)
83 {
84   View2D *v2d = &region->v2d;
85
86   /* clear */
87   UI_ThemeClearColor(TH_BACK);
88
89   draw_outliner(C);
90
91   /* reset view matrix */
92   UI_view2d_view_restore(C);
93
94   /* scrollers */
95   UI_view2d_scrollers_draw(v2d, NULL);
96 }
97
98 static void outliner_main_region_free(ARegion *UNUSED(region))
99 {
100 }
101
102 static void outliner_main_region_listener(const wmRegionListenerParams *params)
103 {
104   ScrArea *area = params->area;
105   ARegion *region = params->region;
106   wmNotifier *wmn = params->notifier;
107   SpaceOutliner *space_outliner = area->spacedata.first;
108
109   /* context changes */
110   switch (wmn->category) {
111     case NC_WM:
112       switch (wmn->data) {
113         case ND_LIB_OVERRIDE_CHANGED:
114           ED_region_tag_redraw(region);
115           break;
116       }
117       break;
118     case NC_SCENE:
119       switch (wmn->data) {
120         case ND_OB_ACTIVE:
121         case ND_OB_SELECT:
122           if (outliner_requires_rebuild_on_select_or_active_change(space_outliner)) {
123             ED_region_tag_redraw(region);
124           }
125           else {
126             ED_region_tag_redraw_no_rebuild(region);
127           }
128           break;
129         case ND_OB_VISIBLE:
130         case ND_OB_RENDER:
131         case ND_MODE:
132         case ND_KEYINGSET:
133         case ND_FRAME:
134         case ND_RENDER_OPTIONS:
135         case ND_SEQUENCER:
136         case ND_LAYER_CONTENT:
137         case ND_WORLD:
138         case ND_SCENEBROWSE:
139           ED_region_tag_redraw(region);
140           break;
141         case ND_LAYER:
142           /* Avoid rebuild if only the active collection changes */
143           if ((wmn->subtype == NS_LAYER_COLLECTION) && (wmn->action == NA_ACTIVATED)) {
144             ED_region_tag_redraw_no_rebuild(region);
145             break;
146           }
147
148           ED_region_tag_redraw(region);
149           break;
150       }
151       if (wmn->action == NA_EDITED) {
152         ED_region_tag_redraw_no_rebuild(region);
153       }
154       break;
155     case NC_OBJECT:
156       switch (wmn->data) {
157         case ND_TRANSFORM:
158           ED_region_tag_redraw_no_rebuild(region);
159           break;
160         case ND_BONE_ACTIVE:
161         case ND_BONE_SELECT:
162         case ND_DRAW:
163         case ND_PARENT:
164         case ND_OB_SHADING:
165           ED_region_tag_redraw(region);
166           break;
167         case ND_CONSTRAINT:
168           /* all constraint actions now, for reordering */
169           ED_region_tag_redraw(region);
170           break;
171         case ND_MODIFIER:
172           /* all modifier actions now */
173           ED_region_tag_redraw(region);
174           break;
175         default:
176           /* Trigger update for NC_OBJECT itself */
177           ED_region_tag_redraw(region);
178           break;
179       }
180       break;
181     case NC_GROUP:
182       /* all actions now, todo: check outliner view mode? */
183       ED_region_tag_redraw(region);
184       break;
185     case NC_LAMP:
186       /* For updating light icons, when changing light type */
187       if (wmn->data == ND_LIGHTING_DRAW) {
188         ED_region_tag_redraw(region);
189       }
190       break;
191     case NC_SPACE:
192       if (wmn->data == ND_SPACE_OUTLINER) {
193         ED_region_tag_redraw(region);
194       }
195       break;
196     case NC_ID:
197       if (ELEM(wmn->action, NA_RENAME, NA_ADDED)) {
198         ED_region_tag_redraw(region);
199       }
200       break;
201     case NC_MATERIAL:
202       switch (wmn->data) {
203         case ND_SHADING_LINKS:
204           ED_region_tag_redraw_no_rebuild(region);
205           break;
206       }
207       break;
208     case NC_GEOM:
209       switch (wmn->data) {
210         case ND_VERTEX_GROUP:
211         case ND_DATA:
212           ED_region_tag_redraw(region);
213           break;
214       }
215       break;
216     case NC_ANIMATION:
217       switch (wmn->data) {
218         case ND_NLA_ACTCHANGE:
219         case ND_KEYFRAME:
220           ED_region_tag_redraw(region);
221           break;
222         case ND_ANIMCHAN:
223           if (ELEM(wmn->action, NA_SELECTED, NA_RENAME)) {
224             ED_region_tag_redraw(region);
225           }
226           break;
227         case ND_NLA:
228           if (ELEM(wmn->action, NA_ADDED, NA_REMOVED)) {
229             ED_region_tag_redraw(region);
230           }
231           break;
232         case ND_NLA_ORDER:
233           ED_region_tag_redraw(region);
234           break;
235       }
236       break;
237     case NC_GPENCIL:
238       if (ELEM(wmn->action, NA_EDITED, NA_SELECTED)) {
239         ED_region_tag_redraw(region);
240       }
241       break;
242     case NC_SCREEN:
243       if (ELEM(wmn->data, ND_LAYOUTDELETE, ND_LAYER)) {
244         ED_region_tag_redraw(region);
245       }
246       break;
247     case NC_MASK:
248       if (ELEM(wmn->action, NA_ADDED)) {
249         ED_region_tag_redraw(region);
250       }
251       break;
252     case NC_PAINTCURVE:
253       if (ELEM(wmn->action, NA_ADDED)) {
254         ED_region_tag_redraw(region);
255       }
256       break;
257     case NC_TEXT:
258       if (ELEM(wmn->action, NA_ADDED, NA_REMOVED)) {
259         ED_region_tag_redraw(region);
260       }
261       break;
262   }
263 }
264
265 static void outliner_main_region_message_subscribe(const wmRegionMessageSubscribeParams *params)
266 {
267   struct wmMsgBus *mbus = params->message_bus;
268   ScrArea *area = params->area;
269   ARegion *region = params->region;
270   SpaceOutliner *space_outliner = area->spacedata.first;
271
272   wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
273       .owner = region,
274       .user_data = region,
275       .notify = ED_region_do_msg_notify_tag_redraw,
276   };
277
278   if (ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES, SO_OVERRIDES_LIBRARY)) {
279     WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw);
280   }
281 }
282
283 /* ************************ header outliner area region *********************** */
284
285 /* add handlers, stuff you only do once or on area/region changes */
286 static void outliner_header_region_init(wmWindowManager *UNUSED(wm), ARegion *region)
287 {
288   ED_region_header_init(region);
289 }
290
291 static void outliner_header_region_draw(const bContext *C, ARegion *region)
292 {
293   ED_region_header(C, region);
294 }
295
296 static void outliner_header_region_free(ARegion *UNUSED(region))
297 {
298 }
299
300 static void outliner_header_region_listener(const wmRegionListenerParams *params)
301 {
302   ARegion *region = params->region;
303   wmNotifier *wmn = params->notifier;
304
305   /* context changes */
306   switch (wmn->category) {
307     case NC_SCENE:
308       if (wmn->data == ND_KEYINGSET) {
309         ED_region_tag_redraw(region);
310       }
311       break;
312     case NC_SPACE:
313       if (wmn->data == ND_SPACE_OUTLINER) {
314         ED_region_tag_redraw(region);
315       }
316       break;
317   }
318 }
319
320 /* ******************** default callbacks for outliner space ***************** */
321
322 static SpaceLink *outliner_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene))
323 {
324   ARegion *region;
325   SpaceOutliner *space_outliner;
326
327   space_outliner = MEM_callocN(sizeof(SpaceOutliner), "initoutliner");
328   space_outliner->spacetype = SPACE_OUTLINER;
329   space_outliner->filter_id_type = ID_GR;
330   space_outliner->show_restrict_flags = SO_RESTRICT_ENABLE | SO_RESTRICT_HIDE | SO_RESTRICT_RENDER;
331   space_outliner->outlinevis = SO_VIEW_LAYER;
332   space_outliner->sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_ALL;
333   space_outliner->flag = SO_SYNC_SELECT | SO_MODE_COLUMN;
334
335   /* header */
336   region = MEM_callocN(sizeof(ARegion), "header for outliner");
337
338   BLI_addtail(&space_outliner->regionbase, region);
339   region->regiontype = RGN_TYPE_HEADER;
340   region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
341
342   /* main region */
343   region = MEM_callocN(sizeof(ARegion), "main region for outliner");
344
345   BLI_addtail(&space_outliner->regionbase, region);
346   region->regiontype = RGN_TYPE_WINDOW;
347
348   return (SpaceLink *)space_outliner;
349 }
350
351 /* not spacelink itself */
352 static void outliner_free(SpaceLink *sl)
353 {
354   SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
355
356   outliner_free_tree(&space_outliner->tree);
357   if (space_outliner->treestore) {
358     BLI_mempool_destroy(space_outliner->treestore);
359   }
360
361   if (space_outliner->runtime) {
362     outliner_tree_display_destroy(&space_outliner->runtime->tree_display);
363     if (space_outliner->runtime->treehash) {
364       BKE_outliner_treehash_free(space_outliner->runtime->treehash);
365     }
366     MEM_freeN(space_outliner->runtime);
367   }
368 }
369
370 /* spacetype; init callback */
371 static void outliner_init(wmWindowManager *UNUSED(wm), ScrArea *area)
372 {
373   SpaceOutliner *space_outliner = area->spacedata.first;
374
375   if (space_outliner->runtime == NULL) {
376     space_outliner->runtime = MEM_callocN(sizeof(*space_outliner->runtime),
377                                           "SpaceOutliner_Runtime");
378   }
379 }
380
381 static SpaceLink *outliner_duplicate(SpaceLink *sl)
382 {
383   SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
384   SpaceOutliner *space_outliner_new = MEM_dupallocN(space_outliner);
385
386   BLI_listbase_clear(&space_outliner_new->tree);
387   space_outliner_new->treestore = NULL;
388
389   space_outliner_new->sync_select_dirty = WM_OUTLINER_SYNC_SELECT_FROM_ALL;
390
391   if (space_outliner->runtime) {
392     space_outliner_new->runtime = MEM_dupallocN(space_outliner->runtime);
393     space_outliner_new->runtime->tree_display = NULL;
394     space_outliner_new->runtime->treehash = NULL;
395   }
396
397   return (SpaceLink *)space_outliner_new;
398 }
399
400 static void outliner_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id)
401 {
402   SpaceOutliner *space_outliner = (SpaceOutliner *)slink;
403
404   /* Some early out checks. */
405   if (!TREESTORE_ID_TYPE(old_id)) {
406     return; /* ID type is not used by outliner. */
407   }
408
409   if (space_outliner->search_tse.id == old_id) {
410     space_outliner->search_tse.id = new_id;
411   }
412
413   if (space_outliner->treestore) {
414     TreeStoreElem *tselem;
415     BLI_mempool_iter iter;
416     bool changed = false;
417
418     BLI_mempool_iternew(space_outliner->treestore, &iter);
419     while ((tselem = BLI_mempool_iterstep(&iter))) {
420       if (tselem->id == old_id) {
421         tselem->id = new_id;
422         changed = true;
423       }
424     }
425
426     /* Note that the Outliner may not be the active editor of the area, and hence not initialized.
427      * So runtime data might not have been created yet. */
428     if (space_outliner->runtime && space_outliner->runtime->treehash && changed) {
429       /* rebuild hash table, because it depends on ids too */
430       /* postpone a full rebuild because this can be called many times on-free */
431       space_outliner->storeflag |= SO_TREESTORE_REBUILD;
432
433       if (new_id == NULL) {
434         /* Redraw is needed when removing data for multiple outlines show the same data.
435          * without this, the stale data won't get fully flushed when this outliner
436          * is not the active outliner the user is interacting with. See T85976. */
437         ED_area_tag_redraw(area);
438       }
439     }
440   }
441 }
442
443 static void outliner_deactivate(struct ScrArea *area)
444 {
445   /* Remove hover highlights */
446   SpaceOutliner *space_outliner = area->spacedata.first;
447   outliner_flag_set(&space_outliner->tree, TSE_HIGHLIGHTED_ANY, false);
448   ED_region_tag_redraw_no_rebuild(BKE_area_find_region_type(area, RGN_TYPE_WINDOW));
449 }
450
451 /* only called once, from space_api/spacetypes.c */
452 void ED_spacetype_outliner(void)
453 {
454   SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype time");
455   ARegionType *art;
456
457   st->spaceid = SPACE_OUTLINER;
458   strncpy(st->name, "Outliner", BKE_ST_MAXNAME);
459
460   st->create = outliner_create;
461   st->free = outliner_free;
462   st->init = outliner_init;
463   st->duplicate = outliner_duplicate;
464   st->operatortypes = outliner_operatortypes;
465   st->keymap = outliner_keymap;
466   st->dropboxes = outliner_dropboxes;
467   st->id_remap = outliner_id_remap;
468   st->deactivate = outliner_deactivate;
469   st->context = outliner_context;
470
471   /* regions: main window */
472   art = MEM_callocN(sizeof(ARegionType), "spacetype outliner region");
473   art->regionid = RGN_TYPE_WINDOW;
474   art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D;
475
476   art->init = outliner_main_region_init;
477   art->draw = outliner_main_region_draw;
478   art->free = outliner_main_region_free;
479   art->listener = outliner_main_region_listener;
480   art->message_subscribe = outliner_main_region_message_subscribe;
481   BLI_addhead(&st->regiontypes, art);
482
483   /* regions: header */
484   art = MEM_callocN(sizeof(ARegionType), "spacetype outliner header region");
485   art->regionid = RGN_TYPE_HEADER;
486   art->prefsizey = HEADERY;
487   art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER;
488
489   art->init = outliner_header_region_init;
490   art->draw = outliner_header_region_draw;
491   art->free = outliner_header_region_free;
492   art->listener = outliner_header_region_listener;
493   BLI_addhead(&st->regiontypes, art);
494
495   BKE_spacetype_register(st);
496 }