- added docs and examples for bpy.app.handlers
[blender.git] / source / blender / blenkernel / intern / screen.c
1 /* 
2  *
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s): none yet.
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /** \file blender/blenkernel/intern/screen.c
30  *  \ingroup bke
31  */
32
33 #include "BLI_winstuff.h"
34
35 #include <string.h>
36 #include <stdio.h>
37 #include <math.h>
38
39 #include "MEM_guardedalloc.h"
40
41 #include "DNA_scene_types.h"
42 #include "DNA_screen_types.h"
43 #include "DNA_space_types.h"
44 #include "DNA_view3d_types.h"
45
46 #include "BLI_blenlib.h"
47
48 #include "BKE_screen.h"
49
50 /* ************ Spacetype/regiontype handling ************** */
51
52 /* keep global; this has to be accessible outside of windowmanager */
53 static ListBase spacetypes= {NULL, NULL};
54
55 /* not SpaceType itself */
56 static void spacetype_free(SpaceType *st)
57 {
58         ARegionType *art;
59         PanelType *pt;
60         HeaderType *ht;
61         
62         for(art= st->regiontypes.first; art; art= art->next) {
63                 BLI_freelistN(&art->drawcalls);
64
65                 for(pt= art->paneltypes.first; pt; pt= pt->next)
66                         if(pt->ext.free)
67                                 pt->ext.free(pt->ext.data);
68
69                 for(ht= art->headertypes.first; ht; ht= ht->next)
70                         if(ht->ext.free)
71                                 ht->ext.free(ht->ext.data);
72
73                 BLI_freelistN(&art->paneltypes);
74                 BLI_freelistN(&art->headertypes);
75         }
76         
77         BLI_freelistN(&st->regiontypes);
78         BLI_freelistN(&st->toolshelf);
79
80 }
81
82 void BKE_spacetypes_free(void)
83 {
84         SpaceType *st;
85         
86         for(st= spacetypes.first; st; st= st->next) {
87                 spacetype_free(st);
88         }
89         
90         BLI_freelistN(&spacetypes);
91 }
92
93 SpaceType *BKE_spacetype_from_id(int spaceid)
94 {
95         SpaceType *st;
96         
97         for(st= spacetypes.first; st; st= st->next) {
98                 if(st->spaceid==spaceid)
99                         return st;
100         }
101         return NULL;
102 }
103
104 ARegionType *BKE_regiontype_from_id(SpaceType *st, int regionid)
105 {
106         ARegionType *art;
107         
108         for(art= st->regiontypes.first; art; art= art->next)
109                 if(art->regionid==regionid)
110                         return art;
111         
112         printf("Error, region type missing in - name:\"%s\", id:%d\n", st->name, st->spaceid);
113         return st->regiontypes.first;
114 }
115
116
117 const ListBase *BKE_spacetypes_list(void)
118 {
119         return &spacetypes;
120 }
121
122 void BKE_spacetype_register(SpaceType *st)
123 {
124         SpaceType *stype;
125         
126         /* sanity check */
127         stype= BKE_spacetype_from_id(st->spaceid);
128         if(stype) {
129                 printf("error: redefinition of spacetype %s\n", stype->name);
130                 spacetype_free(stype);
131                 MEM_freeN(stype);
132         }
133         
134         BLI_addtail(&spacetypes, st);
135 }
136
137 /* ***************** Space handling ********************** */
138
139 void BKE_spacedata_freelist(ListBase *lb)
140 {
141         SpaceLink *sl;
142         ARegion *ar;
143         
144         for (sl= lb->first; sl; sl= sl->next) {
145                 SpaceType *st= BKE_spacetype_from_id(sl->spacetype);
146                 
147                 /* free regions for pushed spaces */
148                 for(ar=sl->regionbase.first; ar; ar=ar->next)
149                         BKE_area_region_free(st, ar);
150
151                 BLI_freelistN(&sl->regionbase);
152                 
153                 if(st && st->free) 
154                         st->free(sl);
155         }
156         
157         BLI_freelistN(lb);
158 }
159
160 ARegion *BKE_area_region_copy(SpaceType *st, ARegion *ar)
161 {
162         ARegion *newar= MEM_dupallocN(ar);
163         Panel *pa, *newpa, *patab;
164         
165         newar->prev= newar->next= NULL;
166         newar->handlers.first= newar->handlers.last= NULL;
167         newar->uiblocks.first= newar->uiblocks.last= NULL;
168         newar->swinid= 0;
169         
170         /* use optional regiondata callback */
171         if(ar->regiondata) {
172                 ARegionType *art= BKE_regiontype_from_id(st, ar->regiontype);
173
174                 if(art && art->duplicate)
175                         newar->regiondata= art->duplicate(ar->regiondata);
176                 else
177                         newar->regiondata= MEM_dupallocN(ar->regiondata);
178         }
179
180         if(ar->v2d.tab_offset)
181                 newar->v2d.tab_offset= MEM_dupallocN(ar->v2d.tab_offset);
182         
183         newar->panels.first= newar->panels.last= NULL;
184         BLI_duplicatelist(&newar->panels, &ar->panels);
185         
186         /* copy panel pointers */
187         for(newpa= newar->panels.first; newpa; newpa= newpa->next) {
188                 patab= newar->panels.first;
189                 pa= ar->panels.first;
190                 while(patab) {
191                         if(newpa->paneltab == pa) {
192                                 newpa->paneltab = patab;
193                                 break;
194                         }
195                         patab= patab->next;
196                         pa= pa->next;
197                 }
198         }
199         
200         return newar;
201 }
202
203
204 /* from lb2 to lb1, lb1 is supposed to be free'd */
205 static void region_copylist(SpaceType *st, ListBase *lb1, ListBase *lb2)
206 {
207         ARegion *ar;
208         
209         /* to be sure */
210         lb1->first= lb1->last= NULL;
211         
212         for(ar= lb2->first; ar; ar= ar->next) {
213                 ARegion *arnew= BKE_area_region_copy(st, ar);
214                 BLI_addtail(lb1, arnew);
215         }
216 }
217
218
219 /* lb1 should be empty */
220 void BKE_spacedata_copylist(ListBase *lb1, ListBase *lb2)
221 {
222         SpaceLink *sl;
223         
224         lb1->first= lb1->last= NULL;    /* to be sure */
225         
226         for (sl= lb2->first; sl; sl= sl->next) {
227                 SpaceType *st= BKE_spacetype_from_id(sl->spacetype);
228                 
229                 if(st && st->duplicate) {
230                         SpaceLink *slnew= st->duplicate(sl);
231                         
232                         BLI_addtail(lb1, slnew);
233                         
234                         region_copylist(st, &slnew->regionbase, &sl->regionbase);
235                 }
236         }
237 }
238
239 /* facility to set locks for drawing to survive (render) threads accessing drawing data */
240 /* lock can become bitflag too */
241 /* should be replaced in future by better local data handling for threads */
242 void BKE_spacedata_draw_locks(int set)
243 {
244         SpaceType *st;
245         
246         for(st= spacetypes.first; st; st= st->next) {
247                 ARegionType *art;
248         
249                 for(art= st->regiontypes.first; art; art= art->next) {
250                         if(set) 
251                                 art->do_lock= art->lock;
252                         else 
253                                 art->do_lock= 0;
254                 }
255         }
256 }
257
258
259 /* not region itself */
260 void BKE_area_region_free(SpaceType *st, ARegion *ar)
261 {
262         if(st) {
263                 ARegionType *art= BKE_regiontype_from_id(st, ar->regiontype);
264                 
265                 if(art && art->free)
266                         art->free(ar);
267                 
268                 if(ar->regiondata)
269                         printf("regiondata free error\n");
270         }
271         else if(ar->type && ar->type->free)
272                 ar->type->free(ar);
273         
274         if(ar->v2d.tab_offset) {
275                 MEM_freeN(ar->v2d.tab_offset);
276                 ar->v2d.tab_offset= NULL;
277         }
278
279         BLI_freelistN(&ar->panels);
280 }
281
282 /* not area itself */
283 void BKE_screen_area_free(ScrArea *sa)
284 {
285         SpaceType *st= BKE_spacetype_from_id(sa->spacetype);
286         ARegion *ar;
287         
288         for(ar=sa->regionbase.first; ar; ar=ar->next)
289                 BKE_area_region_free(st, ar);
290
291         BLI_freelistN(&sa->regionbase);
292         
293         BKE_spacedata_freelist(&sa->spacedata);
294         
295         BLI_freelistN(&sa->actionzones);
296 }
297
298 /* don't free screen itself */
299 void free_screen(bScreen *sc)
300 {
301         ScrArea *sa, *san;
302         ARegion *ar;
303         
304         for(ar=sc->regionbase.first; ar; ar=ar->next)
305                 BKE_area_region_free(NULL, ar);
306
307         BLI_freelistN(&sc->regionbase);
308         
309         for(sa= sc->areabase.first; sa; sa= san) {
310                 san= sa->next;
311                 BKE_screen_area_free(sa);
312         }
313         
314         BLI_freelistN(&sc->vertbase);
315         BLI_freelistN(&sc->edgebase);
316         BLI_freelistN(&sc->areabase);
317 }
318
319 /* for depsgraph */
320 unsigned int BKE_screen_visible_layers(bScreen *screen, Scene *scene)
321 {
322         ScrArea *sa;
323         unsigned int layer= 0;
324
325         if(screen) {
326                 /* get all used view3d layers */
327                 for(sa= screen->areabase.first; sa; sa= sa->next)
328                         if(sa->spacetype==SPACE_VIEW3D)
329                                 layer |= ((View3D *)sa->spacedata.first)->lay;
330         }
331
332         if(!layer)
333                 return scene->lay;
334
335         return layer;
336 }
337
338 /* ***************** Utilities ********************** */
339
340 /* Find a region of the specified type from the given area */
341 ARegion *BKE_area_find_region_type(ScrArea *sa, int type)
342 {
343         if (sa) {
344                 ARegion *ar;
345                 
346                 for (ar=sa->regionbase.first; ar; ar= ar->next) {
347                         if (ar->regiontype == type)
348                                 return ar;
349                 }
350         }
351         return NULL;
352 }
353
354 /* note, using this function is generally a last resort, you really want to be
355  * using the context when you can - campbell
356  * -1 for any type */
357 struct ScrArea *BKE_screen_find_big_area(struct bScreen *sc, const int spacetype, const short min)
358 {
359         ScrArea *sa, *big= NULL;
360         int size, maxsize= 0;
361
362         for(sa= sc->areabase.first; sa; sa= sa->next) {
363                 if ((spacetype == -1) || sa->spacetype == spacetype) {
364                         if (min <= sa->winx && min <= sa->winy) {
365                                 size= sa->winx*sa->winy;
366                                 if (size > maxsize) {
367                                         maxsize= size;
368                                         big= sa;
369                                 }
370                         }
371                 }
372         }
373
374         return big;
375 }
376
377 void BKE_screen_view3d_sync(struct View3D *v3d, struct Scene *scene)
378 {
379         int bit;
380
381         if(v3d->scenelock && v3d->localvd==NULL) {
382                 v3d->lay= scene->lay;
383                 v3d->camera= scene->camera;
384
385                 if(v3d->camera==NULL) {
386                         ARegion *ar;
387
388                         for(ar=v3d->regionbase.first; ar; ar= ar->next) {
389                                 if(ar->regiontype == RGN_TYPE_WINDOW) {
390                                         RegionView3D *rv3d= ar->regiondata;
391                                         if(rv3d->persp==RV3D_CAMOB)
392                                                 rv3d->persp= RV3D_PERSP;
393                                 }
394                         }
395                 }
396
397                 if((v3d->lay & v3d->layact) == 0) {
398                         for(bit= 0; bit<32; bit++) {
399                                 if(v3d->lay & (1<<bit)) {
400                                         v3d->layact= 1<<bit;
401                                         break;
402                                 }
403                         }
404                 }
405         }
406 }
407
408 void BKE_screen_view3d_scene_sync(bScreen *sc)
409 {
410         /* are there cameras in the views that are not in the scene? */
411         ScrArea *sa;
412         for(sa= sc->areabase.first; sa; sa= sa->next) {
413                 SpaceLink *sl;
414                 for(sl= sa->spacedata.first; sl; sl= sl->next) {
415                         if(sl->spacetype==SPACE_VIEW3D) {
416                                 View3D *v3d= (View3D*) sl;
417                                 BKE_screen_view3d_sync(v3d, sc->scene);
418                         }
419                 }
420         }
421 }
422
423 void BKE_screen_view3d_main_sync(ListBase *screen_lb, Scene *scene)
424 {
425         bScreen *sc;
426         ScrArea *sa;
427         SpaceLink *sl;
428
429         /* from scene copy to the other views */
430         for(sc=screen_lb->first; sc; sc=sc->id.next) {
431                 if(sc->scene!=scene)
432                         continue;
433
434                 for(sa=sc->areabase.first; sa; sa=sa->next)
435                         for(sl=sa->spacedata.first; sl; sl=sl->next)
436                                 if(sl->spacetype==SPACE_VIEW3D)
437                                         BKE_screen_view3d_sync((View3D*)sl, scene);
438         }
439 }
440
441 /* magic zoom calculation, no idea what
442  * it signifies, if you find out, tell me! -zr
443  */
444
445 /* simple, its magic dude!
446  * well, to be honest, this gives a natural feeling zooming
447  * with multiple keypad presses (ton)
448  */
449 float BKE_screen_view3d_zoom_to_fac(float camzoom)
450 {
451         return powf(((float)M_SQRT2 + camzoom/50.0f), 2.0f) / 4.0f;
452 }
453
454 float BKE_screen_view3d_zoom_from_fac(float zoomfac)
455 {
456         return ((sqrtf(4.0f * zoomfac) - (float)M_SQRT2) * 50.0f);
457 }