fix for crash opening the file selector twice with multiple windows open (when the...
[blender.git] / source / blender / editors / render / render_view.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/render/render_view.c
28  *  \ingroup edrend
29  */
30
31 #include <string.h>
32 #include <stddef.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "BLI_blenlib.h"
37 #include "BLI_utildefines.h"
38
39 #include "DNA_scene_types.h"
40
41 #include "BKE_blender.h"
42 #include "BKE_context.h"
43 #include "BKE_image.h"
44 #include "BKE_global.h"
45 #include "BKE_main.h"
46 #include "BKE_node.h"
47 #include "BKE_report.h"
48 #include "BKE_screen.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52
53 #include "ED_screen.h"
54
55 #include "wm_window.h"
56
57 #include "render_intern.h"
58
59 /*********************** utilities for finding areas *************************/
60
61 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
62 /* window as the last possible alternative.                                                                        */
63 static ScrArea *biggest_non_image_area(bContext *C)
64 {
65         bScreen *sc= CTX_wm_screen(C);
66         ScrArea *sa, *big= NULL;
67         int size, maxsize= 0, bwmaxsize= 0;
68         short foundwin= 0;
69
70         for(sa= sc->areabase.first; sa; sa= sa->next) {
71                 if(sa->winx > 30 && sa->winy > 30) {
72                         size= sa->winx*sa->winy;
73                         if(sa->spacetype == SPACE_BUTS) {
74                                 if(foundwin == 0 && size > bwmaxsize) {
75                                         bwmaxsize= size;
76                                         big= sa;
77                                 }
78                         }
79                         else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
80                                 maxsize= size;
81                                 big= sa;
82                                 foundwin= 1;
83                         }
84                 }
85         }
86
87         return big;
88 }
89
90 static ScrArea *biggest_area(bContext *C)
91 {
92         bScreen *sc= CTX_wm_screen(C);
93         ScrArea *sa, *big= NULL;
94         int size, maxsize= 0;
95
96         for(sa= sc->areabase.first; sa; sa= sa->next) {
97                 size= sa->winx*sa->winy;
98                 if(size > maxsize) {
99                         maxsize= size;
100                         big= sa;
101                 }
102         }
103         return big;
104 }
105
106 static ScrArea *find_area_showing_r_result(bContext *C, wmWindow **win)
107 {
108         wmWindowManager *wm= CTX_wm_manager(C);
109         ScrArea *sa = NULL;
110         SpaceImage *sima;
111
112         /* find an imagewindow showing render result */
113         for(*win=wm->windows.first; *win; *win= (*win)->next) {
114                 for(sa= (*win)->screen->areabase.first; sa; sa= sa->next) {
115                         if(sa->spacetype==SPACE_IMAGE) {
116                                 sima= sa->spacedata.first;
117                                 if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
118                                         break;
119                         }
120                 }
121                 if(sa)
122                         break;
123         }
124         
125         return sa;
126 }
127
128 static ScrArea *find_area_image_empty(bContext *C)
129 {
130         bScreen *sc= CTX_wm_screen(C);
131         ScrArea *sa;
132         SpaceImage *sima;
133
134         /* find an imagewindow showing render result */
135         for(sa=sc->areabase.first; sa; sa= sa->next) {
136                 if(sa->spacetype==SPACE_IMAGE) {
137                         sima= sa->spacedata.first;
138                         if(!sima->image)
139                                 break;
140                 }
141         }
142
143         return sa;
144 }
145
146 /********************** open image editor for render *************************/
147
148 /* new window uses x,y to set position */
149 void render_view_open(bContext *C, int mx, int my)
150 {
151         wmWindow *win= CTX_wm_window(C);
152         Scene *scene= CTX_data_scene(C);
153         ScrArea *sa= NULL;
154         SpaceImage *sima;
155         int area_was_image=0;
156
157         if(scene->r.displaymode==R_OUTPUT_NONE)
158                 return;
159         
160         if(scene->r.displaymode==R_OUTPUT_WINDOW) {
161                 rcti rect;
162                 int sizex, sizey;
163
164                 sizex= 10 + (scene->r.xsch*scene->r.size)/100;
165                 sizey= 40 + (scene->r.ysch*scene->r.size)/100;
166
167                 /* arbitrary... miniature image window views don't make much sense */
168                 if(sizex < 320) sizex= 320;
169                 if(sizey < 256) sizey= 256;
170
171                 /* XXX some magic to calculate postition */
172                 rect.xmin= mx + win->posx - sizex/2;
173                 rect.ymin= my + win->posy - sizey/2;
174                 rect.xmax= rect.xmin + sizex;
175                 rect.ymax= rect.ymin + sizey;
176
177                 /* changes context! */
178                 WM_window_open_temp(C, &rect, WM_WINDOW_RENDER);
179
180                 sa= CTX_wm_area(C);
181         }
182         else if(scene->r.displaymode==R_OUTPUT_SCREEN) {
183                 if (CTX_wm_area(C) && CTX_wm_area(C)->spacetype == SPACE_IMAGE)
184                         area_was_image = 1;
185
186                 /* this function returns with changed context */
187                 sa= ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_IMAGE);
188         }
189
190         if(!sa) {
191                 sa= find_area_showing_r_result(C, &win); 
192                 if(sa==NULL)
193                         sa= find_area_image_empty(C);
194                 
195                 /* if area found in other window, we make that one show in front */
196                 if(win && win!=CTX_wm_window(C))
197                         wm_window_raise(win);
198
199                 if(sa==NULL) {
200                         /* find largest open non-image area */
201                         sa= biggest_non_image_area(C);
202                         if(sa) {
203                                 ED_area_newspace(C, sa, SPACE_IMAGE);
204                                 sima= sa->spacedata.first;
205
206                                 /* makes ESC go back to prev space */
207                                 sima->flag |= SI_PREVSPACE;
208                         }
209                         else {
210                                 /* use any area of decent size */
211                                 sa= biggest_area(C);
212                                 if(sa->spacetype!=SPACE_IMAGE) {
213                                         // XXX newspace(sa, SPACE_IMAGE);
214                                         sima= sa->spacedata.first;
215
216                                         /* makes ESC go back to prev space */
217                                         sima->flag |= SI_PREVSPACE;
218                                 }
219                         }
220                 }
221         }
222         sima= sa->spacedata.first;
223
224         /* get the correct image, and scale it */
225         sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
226
227
228         /* if we're rendering to full screen, set appropriate hints on image editor
229          * so it can restore properly on pressing esc */
230         if(sa->full) {
231                 sima->flag |= SI_FULLWINDOW;
232
233                 /* Tell the image editor to revert to previous space in space list on close
234                  * _only_ if it wasn't already an image editor when the render was invoked */
235                 if (area_was_image == 0)
236                         sima->flag |= SI_PREVSPACE;
237                 else {
238                         /* Leave it alone so the image editor will just go back from
239                          * full screen to the original tiled setup */
240                         ;
241                 }
242         }
243 }
244
245 /*************************** cancel render viewer **********************/
246
247 static int render_view_cancel_exec(bContext *C, wmOperator *UNUSED(op))
248 {
249         wmWindow *win= CTX_wm_window(C);
250         ScrArea *sa= CTX_wm_area(C);
251         SpaceImage *sima= sa->spacedata.first;
252
253         /* test if we have a temp screen in front */
254         if(CTX_wm_window(C)->screen->temp) {
255                 wm_window_lower(CTX_wm_window(C));
256                 return OPERATOR_FINISHED;
257         }
258         /* determine if render already shows */
259         else if(sima->flag & SI_PREVSPACE) {
260                 sima->flag &= ~SI_PREVSPACE;
261
262                 if(sima->flag & SI_FULLWINDOW) {
263                         sima->flag &= ~SI_FULLWINDOW;
264                         ED_screen_full_prevspace(C, sa);
265                 }
266                 else
267                         ED_area_prevspace(C, sa);
268
269                 return OPERATOR_FINISHED;
270         }
271         else if(sima->flag & SI_FULLWINDOW) {
272                 sima->flag &= ~SI_FULLWINDOW;
273                 ED_screen_full_toggle(C, win, sa);
274                 return OPERATOR_FINISHED;
275         }
276
277         return OPERATOR_PASS_THROUGH;
278 }
279
280 void RENDER_OT_view_cancel(struct wmOperatorType *ot)
281 {
282         /* identifiers */
283         ot->name= "Cancel Render View";
284         ot->description= "Cancel show render view";
285         ot->idname= "RENDER_OT_view_cancel";
286
287         /* api callbacks */
288         ot->exec= render_view_cancel_exec;
289         ot->poll= ED_operator_image_active;
290 }
291
292 /************************* show render viewer *****************/
293
294 static int render_view_show_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
295 {
296         wmWindow *wincur = CTX_wm_window(C);
297         
298         /* test if we have currently a temp screen active */
299         if(wincur->screen->temp) {
300                 wm_window_lower(wincur);
301         }
302         else { 
303                 wmWindow *win, *winshow;
304                 ScrArea *sa= find_area_showing_r_result(C, &winshow);
305                 
306                 /* is there another window showing result? */
307                 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
308                         if(win->screen->temp || (win==winshow && winshow!=wincur)) {
309                                 wm_window_raise(win);
310                                 return OPERATOR_FINISHED;
311                         }
312                 }
313                 
314                 /* determine if render already shows */
315                 if(sa) {
316                         /* but don't close it when rendering */
317                         if(!G.rendering) {
318                                 SpaceImage *sima= sa->spacedata.first;
319
320                                 if(sima->flag & SI_PREVSPACE) {
321                                         sima->flag &= ~SI_PREVSPACE;
322
323                                         if(sima->flag & SI_FULLWINDOW) {
324                                                 sima->flag &= ~SI_FULLWINDOW;
325                                                 ED_screen_full_prevspace(C, sa);
326                                         }
327                                         else if(sima->next) {
328                                                 /* workaround for case of double prevspace, render window
329                                                    with a file browser on top of it (same as in ED_area_prevspace) */
330                                                 if(sima->next->spacetype == SPACE_FILE && sima->next->next)
331                                                         ED_area_newspace(C, sa, sima->next->next->spacetype);
332                                                 else
333                                                         ED_area_newspace(C, sa, sima->next->spacetype);
334                                                 ED_area_tag_redraw(sa);
335                                         }
336                                 }
337                         }
338                 }
339                 else {
340                         render_view_open(C, event->x, event->y);
341                 }
342         }
343
344         return OPERATOR_FINISHED;
345 }
346
347 void RENDER_OT_view_show(struct wmOperatorType *ot)
348 {
349         /* identifiers */
350         ot->name= "Show/Hide Render View";
351         ot->description= "Toggle show render view";
352         ot->idname= "RENDER_OT_view_show";
353
354         /* api callbacks */
355         ot->invoke= render_view_show_invoke;
356         ot->poll= ED_operator_screenactive;
357 }
358