- Synchronize with changes in trunk
[blender.git] / source / blender / windowmanager / intern / wm_files.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) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation 2007
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/windowmanager/intern/wm_files.c
27  *  \ingroup wm
28  */
29
30
31 /* placed up here because of crappy
32  * winsock stuff.
33  */
34 #include <stddef.h>
35 #include <string.h>
36 #include <errno.h>
37
38 #include "zlib.h" /* wm_read_exotic() */
39
40 #ifdef WIN32
41 #  include <windows.h> /* need to include windows.h so _WIN32_IE is defined  */
42 #  ifndef _WIN32_IE
43 #    define _WIN32_IE 0x0400 /* minimal requirements for SHGetSpecialFolderPath on MINGW MSVC has this defined already */
44 #  endif
45 #  include <shlobj.h>  /* for SHGetSpecialFolderPath, has to be done before BLI_winstuff
46                         * because 'near' is disabled through BLI_windstuff */
47 #  include <process.h> /* getpid */
48 #  include "BLI_winstuff.h"
49 #else
50 #  include <unistd.h> /* getpid */
51 #endif
52
53 #include "MEM_guardedalloc.h"
54 #include "MEM_CacheLimiterC-Api.h"
55
56 #include "BLI_blenlib.h"
57 #include "BLI_linklist.h"
58 #include "BLI_utildefines.h"
59 #include "BLI_callbacks.h"
60
61 #include "BLF_translation.h"
62
63 #include "DNA_anim_types.h"
64 #include "DNA_ipo_types.h" // XXX old animation system
65 #include "DNA_object_types.h"
66 #include "DNA_space_types.h"
67 #include "DNA_userdef_types.h"
68 #include "DNA_scene_types.h"
69 #include "DNA_screen_types.h"
70 #include "DNA_windowmanager_types.h"
71
72 #include "BKE_blender.h"
73 #include "BKE_context.h"
74 #include "BKE_depsgraph.h"
75 #include "BKE_DerivedMesh.h"
76 #include "BKE_font.h"
77 #include "BKE_global.h"
78 #include "BKE_library.h"
79 #include "BKE_main.h"
80 #include "BKE_packedFile.h"
81 #include "BKE_report.h"
82 #include "BKE_sound.h"
83 #include "BKE_screen.h"
84 #include "BKE_texture.h"
85
86
87 #include "BLO_readfile.h"
88 #include "BLO_writefile.h"
89
90 #include "RNA_access.h"
91
92 #include "IMB_imbuf.h"
93 #include "IMB_imbuf_types.h"
94 #include "IMB_thumbs.h"
95
96 #include "ED_datafiles.h"
97 #include "ED_object.h"
98 #include "ED_screen.h"
99 #include "ED_sculpt.h"
100 #include "ED_view3d.h"
101 #include "ED_util.h"
102
103 #include "RE_pipeline.h" /* only to report missing engine */
104
105 #include "GHOST_C-api.h"
106 #include "GHOST_Path-api.h"
107
108 #include "UI_interface.h"
109
110 #include "GPU_draw.h"
111
112 #ifdef WITH_PYTHON
113 #include "BPY_extern.h"
114 #endif
115
116 #include "WM_api.h"
117 #include "WM_types.h"
118 #include "wm.h"
119 #include "wm_files.h"
120 #include "wm_window.h"
121 #include "wm_event_system.h"
122
123 static void write_history(void);
124
125 /* To be able to read files without windows closing, opening, moving
126  * we try to prepare for worst case:
127  * - active window gets active screen from file
128  * - restoring the screens from non-active windows
129  * Best case is all screens match, in that case they get assigned to proper window
130  */
131 static void wm_window_match_init(bContext *C, ListBase *wmlist)
132 {
133         wmWindowManager *wm;
134         wmWindow *win, *active_win;
135         
136         *wmlist = G.main->wm;
137         G.main->wm.first = G.main->wm.last = NULL;
138         
139         active_win = CTX_wm_window(C);
140
141         /* first wrap up running stuff */
142         /* code copied from wm_init_exit.c */
143         for (wm = wmlist->first; wm; wm = wm->id.next) {
144                 
145                 WM_jobs_stop_all(wm);
146                 
147                 for (win = wm->windows.first; win; win = win->next) {
148                 
149                         CTX_wm_window_set(C, win);  /* needed by operator close callbacks */
150                         WM_event_remove_handlers(C, &win->handlers);
151                         WM_event_remove_handlers(C, &win->modalhandlers);
152                         ED_screen_exit(C, win, win->screen);
153                 }
154         }
155         
156         /* reset active window */
157         CTX_wm_window_set(C, active_win);
158
159         ED_editors_exit(C);
160
161         /* just had return; here from r12991, this code could just get removed?*/
162 #if 0
163         if (wm == NULL) return;
164         if (G.fileflags & G_FILE_NO_UI) return;
165         
166         /* we take apart the used screens from non-active window */
167         for (win = wm->windows.first; win; win = win->next) {
168                 BLI_strncpy(win->screenname, win->screen->id.name, MAX_ID_NAME);
169                 if (win != wm->winactive) {
170                         BLI_remlink(&G.main->screen, win->screen);
171                         //BLI_addtail(screenbase, win->screen);
172                 }
173         }
174 #endif
175 }
176
177 /* match old WM with new, 4 cases:
178  * 1- no current wm, no read wm: make new default
179  * 2- no current wm, but read wm: that's OK, do nothing
180  * 3- current wm, but not in file: try match screen names
181  * 4- current wm, and wm in file: try match ghostwin
182  */
183
184 static void wm_window_match_do(bContext *C, ListBase *oldwmlist)
185 {
186         wmWindowManager *oldwm, *wm;
187         wmWindow *oldwin, *win;
188         
189         /* cases 1 and 2 */
190         if (oldwmlist->first == NULL) {
191                 if (G.main->wm.first) ;  /* nothing todo */
192                 else
193                         wm_add_default(C);
194         }
195         else {
196                 /* cases 3 and 4 */
197                 
198                 /* we've read file without wm..., keep current one entirely alive */
199                 if (G.main->wm.first == NULL) {
200                         bScreen *screen = NULL;
201
202                         /* when loading without UI, no matching needed */
203                         if (!(G.fileflags & G_FILE_NO_UI) && (screen = CTX_wm_screen(C))) {
204
205                                 /* match oldwm to new dbase, only old files */
206                                 for (wm = oldwmlist->first; wm; wm = wm->id.next) {
207                                         
208                                         for (win = wm->windows.first; win; win = win->next) {
209                                                 /* all windows get active screen from file */
210                                                 if (screen->winid == 0)
211                                                         win->screen = screen;
212                                                 else 
213                                                         win->screen = ED_screen_duplicate(win, screen);
214                                                 
215                                                 BLI_strncpy(win->screenname, win->screen->id.name + 2, sizeof(win->screenname));
216                                                 win->screen->winid = win->winid;
217                                         }
218                                 }
219                         }
220                         
221                         G.main->wm = *oldwmlist;
222                         
223                         /* screens were read from file! */
224                         ED_screens_initialize(G.main->wm.first);
225                 }
226                 else {
227                         /* what if old was 3, and loaded 1? */
228                         /* this code could move to setup_appdata */
229                         oldwm = oldwmlist->first;
230                         wm = G.main->wm.first;
231
232                         /* preserve key configurations in new wm, to preserve their keymaps */
233                         wm->keyconfigs = oldwm->keyconfigs;
234                         wm->addonconf = oldwm->addonconf;
235                         wm->defaultconf = oldwm->defaultconf;
236                         wm->userconf = oldwm->userconf;
237
238                         oldwm->keyconfigs.first = oldwm->keyconfigs.last = NULL;
239                         oldwm->addonconf = NULL;
240                         oldwm->defaultconf = NULL;
241                         oldwm->userconf = NULL;
242
243                         /* ensure making new keymaps and set space types */
244                         wm->initialized = 0;
245                         wm->winactive = NULL;
246                         
247                         /* only first wm in list has ghostwins */
248                         for (win = wm->windows.first; win; win = win->next) {
249                                 for (oldwin = oldwm->windows.first; oldwin; oldwin = oldwin->next) {
250                                         
251                                         if (oldwin->winid == win->winid) {
252                                                 win->ghostwin = oldwin->ghostwin;
253                                                 win->active = oldwin->active;
254                                                 if (win->active)
255                                                         wm->winactive = win;
256
257                                                 if (!G.background) /* file loading in background mode still calls this */
258                                                         GHOST_SetWindowUserData(win->ghostwin, win);    /* pointer back */
259
260                                                 oldwin->ghostwin = NULL;
261                                                 
262                                                 win->eventstate = oldwin->eventstate;
263                                                 oldwin->eventstate = NULL;
264                                                 
265                                                 /* ensure proper screen rescaling */
266                                                 win->sizex = oldwin->sizex;
267                                                 win->sizey = oldwin->sizey;
268                                                 win->posx = oldwin->posx;
269                                                 win->posy = oldwin->posy;
270                                         }
271                                 }
272                         }
273                         wm_close_and_free_all(C, oldwmlist);
274                 }
275         }
276 }
277
278 /* in case UserDef was read, we re-initialize all, and do versioning */
279 static void wm_init_userdef(bContext *C)
280 {
281         UI_init_userdef();
282         MEM_CacheLimiter_set_maximum(((size_t)U.memcachelimit) * 1024 * 1024);
283         sound_init(CTX_data_main(C));
284
285         /* needed so loading a file from the command line respects user-pref [#26156] */
286         if (U.flag & USER_FILENOUI) G.fileflags |= G_FILE_NO_UI;
287         else G.fileflags &= ~G_FILE_NO_UI;
288
289         /* set the python auto-execute setting from user prefs */
290         /* enabled by default, unless explicitly enabled in the command line which overrides */
291         if ((G.f & G_SCRIPT_OVERRIDE_PREF) == 0) {
292                 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) G.f |=  G_SCRIPT_AUTOEXEC;
293                 else G.f &= ~G_SCRIPT_AUTOEXEC;
294         }
295
296         /* update tempdir from user preferences */
297         BLI_init_temporary_dir(U.tempdir);
298 }
299
300
301
302 /* return codes */
303 #define BKE_READ_EXOTIC_FAIL_PATH       -3 /* file format is not supported */
304 #define BKE_READ_EXOTIC_FAIL_FORMAT     -2 /* file format is not supported */
305 #define BKE_READ_EXOTIC_FAIL_OPEN       -1 /* Can't open the file */
306 #define BKE_READ_EXOTIC_OK_BLEND         0 /* .blend file */
307 #define BKE_READ_EXOTIC_OK_OTHER         1 /* other supported formats */
308
309
310 /* intended to check for non-blender formats but for now it only reads blends */
311 static int wm_read_exotic(Scene *UNUSED(scene), const char *name)
312 {
313         int len;
314         gzFile gzfile;
315         char header[7];
316         int retval;
317
318         // make sure we're not trying to read a directory....
319
320         len = strlen(name);
321         if (ELEM(name[len - 1], '/', '\\')) {
322                 retval = BKE_READ_EXOTIC_FAIL_PATH;
323         }
324         else {
325                 gzfile = BLI_gzopen(name, "rb");
326                 if (gzfile == NULL) {
327                         retval = BKE_READ_EXOTIC_FAIL_OPEN;
328                 }
329                 else {
330                         len = gzread(gzfile, header, sizeof(header));
331                         gzclose(gzfile);
332                         if (len == sizeof(header) && strncmp(header, "BLENDER", 7) == 0) {
333                                 retval = BKE_READ_EXOTIC_OK_BLEND;
334                         }
335                         else {
336                                 //XXX waitcursor(1);
337 #if 0           /* historic stuff - no longer used */
338                                 if (is_foo_format(name)) {
339                                         read_foo(name);
340                                         retval = BKE_READ_EXOTIC_OK_OTHER;
341                                 }
342                                 else
343 #endif
344                                 {
345                                         retval = BKE_READ_EXOTIC_FAIL_FORMAT;
346                                 }
347                                 //XXX waitcursor(0);
348                         }
349                 }
350         }
351
352         return retval;
353 }
354
355 void WM_read_file(bContext *C, const char *filepath, ReportList *reports)
356 {
357         int retval;
358
359         /* so we can get the error message */
360         errno = 0;
361
362         WM_cursor_wait(1);
363
364         BLI_exec_cb(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_PRE);
365
366         /* first try to append data from exotic file formats... */
367         /* it throws error box when file doesn't exist and returns -1 */
368         /* note; it should set some error message somewhere... (ton) */
369         retval = wm_read_exotic(CTX_data_scene(C), filepath);
370         
371         /* we didn't succeed, now try to read Blender file */
372         if (retval == BKE_READ_EXOTIC_OK_BLEND) {
373                 int G_f = G.f;
374                 ListBase wmbase;
375
376                 /* put aside screens to match with persistent windows later */
377                 /* also exit screens and editors */
378                 wm_window_match_init(C, &wmbase); 
379                 
380                 retval = BKE_read_file(C, filepath, reports);
381                 G.save_over = 1;
382
383                 /* this flag is initialized by the operator but overwritten on read.
384                  * need to re-enable it here else drivers + registered scripts wont work. */
385                 if (G.f != G_f) {
386                         const int flags_keep = (G_SCRIPT_AUTOEXEC | G_SCRIPT_OVERRIDE_PREF);
387                         G.f = (G.f & ~flags_keep) | (G_f & flags_keep);
388                 }
389
390                 /* match the read WM with current WM */
391                 wm_window_match_do(C, &wmbase);
392                 WM_check(C); /* opens window(s), checks keymaps */
393                 
394 // XXX          mainwindow_set_filename_to_title(G.main->name);
395
396                 if (retval == BKE_READ_FILE_OK_USERPREFS) {
397                         /* in case a userdef is read from regular .blend */
398                         wm_init_userdef(C);
399                 }
400                 
401                 if (retval != BKE_READ_FILE_FAIL) {
402                         G.relbase_valid = 1;
403                         if (!G.background) /* assume automated tasks with background, don't write recent file list */
404                                 write_history();
405                 }
406
407
408                 WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL);
409 //              refresh_interface_font();
410
411                 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
412
413                 ED_editors_init(C);
414                 DAG_on_visible_update(CTX_data_main(C), TRUE);
415
416 #ifdef WITH_PYTHON
417                 /* run any texts that were loaded in and flagged as modules */
418                 BPY_driver_reset();
419                 BPY_app_handlers_reset(FALSE);
420                 BPY_modules_load_user(C);
421 #endif
422
423                 /* important to do before NULL'ing the context */
424                 BLI_exec_cb(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_POST);
425
426                 if (!G.background) {
427                         /* in background mode this makes it hard to load
428                          * a blend file and do anything since the screen
429                          * won't be set to a valid value again */
430                         CTX_wm_window_set(C, NULL); /* exits queues */
431                 }
432
433 #if 0
434                 /* gives popups on windows but not linux, bug in report API
435                  * but disable for now to stop users getting annoyed  */
436                 /* TODO, make this show in header info window */
437                 {
438                         Scene *sce;
439                         for (sce = G.main->scene.first; sce; sce = sce->id.next) {
440                                 if (sce->r.engine[0] &&
441                                     BLI_findstring(&R_engines, sce->r.engine, offsetof(RenderEngineType, idname)) == NULL)
442                                 {
443                                         BKE_reportf(reports, RPT_WARNING,
444                                                     "Engine not available: '%s' for scene: %s, "
445                                                     "an addon may need to be installed or enabled",
446                                                     sce->r.engine, sce->id.name + 2);
447                                 }
448                         }
449                 }
450 #endif
451
452                 // XXX          undo_editmode_clear();
453                 BKE_reset_undo();
454                 BKE_write_undo(C, "original");  /* save current state */
455         }
456         else if (retval == BKE_READ_EXOTIC_OK_OTHER)
457                 BKE_write_undo(C, "Import file");
458         else if (retval == BKE_READ_EXOTIC_FAIL_OPEN) {
459                 BKE_reportf(reports, RPT_ERROR, IFACE_("Can't read file: \"%s\", %s."), filepath,
460                             errno ? strerror(errno) : IFACE_("Unable to open the file"));
461         }
462         else if (retval == BKE_READ_EXOTIC_FAIL_FORMAT) {
463                 BKE_reportf(reports, RPT_ERROR, IFACE_("File format is not supported in file: \"%s\"."), filepath);
464         }
465         else if (retval == BKE_READ_EXOTIC_FAIL_PATH) {
466                 BKE_reportf(reports, RPT_ERROR, IFACE_("File path invalid: \"%s\"."), filepath);
467         }
468         else {
469                 BKE_reportf(reports, RPT_ERROR, IFACE_("Unknown error loading: \"%s\"."), filepath);
470                 BLI_assert(!"invalid 'retval'");
471         }
472
473         WM_cursor_wait(0);
474
475 }
476
477
478 /* called on startup,  (context entirely filled with NULLs) */
479 /* or called for 'New File' */
480 /* op can be NULL */
481 int WM_read_homefile(bContext *C, ReportList *UNUSED(reports), short from_memory)
482 {
483         ListBase wmbase;
484         char tstr[FILE_MAX];
485         int success = 0;
486         
487         free_ttfont(); /* still weird... what does it here? */
488                 
489         G.relbase_valid = 0;
490         if (!from_memory) {
491                 char *cfgdir = BLI_get_folder(BLENDER_USER_CONFIG, NULL);
492                 if (cfgdir) {
493                         BLI_make_file_string(G.main->name, tstr, cfgdir, BLENDER_STARTUP_FILE);
494                 }
495                 else {
496                         tstr[0] = '\0';
497                         from_memory = 1;
498                 }
499         }
500         
501         /* prevent loading no UI */
502         G.fileflags &= ~G_FILE_NO_UI;
503         
504         /* put aside screens to match with persistent windows later */
505         wm_window_match_init(C, &wmbase); 
506         
507         if (!from_memory && BLI_exists(tstr)) {
508                 success = (BKE_read_file(C, tstr, NULL) != BKE_READ_FILE_FAIL);
509                 
510                 if (U.themes.first == NULL) {
511                         printf("\nError: No valid "STRINGIFY (BLENDER_STARTUP_FILE)", fall back to built-in default.\n\n");
512                         success = 0;
513                 }
514         }
515         if (success == 0) {
516                 success = BKE_read_file_from_memory(C, datatoc_startup_blend, datatoc_startup_blend_size, NULL);
517                 if (wmbase.first == NULL) wm_clear_default_size(C);
518
519 #ifdef WITH_PYTHON_SECURITY /* not default */
520                 /* use alternative setting for security nuts
521                  * otherwise we'd need to patch the binary blob - startup.blend.c */
522                 U.flag |= USER_SCRIPT_AUTOEXEC_DISABLE;
523 #endif
524         }
525         
526         /* prevent buggy files that had G_FILE_RELATIVE_REMAP written out by mistake. Screws up autosaves otherwise
527          * can remove this eventually, only in a 2.53 and older, now its not written */
528         G.fileflags &= ~G_FILE_RELATIVE_REMAP;
529         
530         /* check userdef before open window, keymaps etc */
531         wm_init_userdef(C);
532         
533         /* match the read WM with current WM */
534         wm_window_match_do(C, &wmbase); 
535         WM_check(C); /* opens window(s), checks keymaps */
536
537         G.main->name[0] = '\0';
538
539         /* When loading factory settings, the reset solid OpenGL lights need to be applied. */
540         if (!G.background) GPU_default_lights();
541         
542         /* XXX */
543         G.save_over = 0;    // start with save preference untitled.blend
544         G.fileflags &= ~G_FILE_AUTOPLAY;    /*  disable autoplay in startup.blend... */
545 //      mainwindow_set_filename_to_title("");   // empty string re-initializes title to "Blender"
546         
547 //      refresh_interface_font();
548         
549 //      undo_editmode_clear();
550         BKE_reset_undo();
551         BKE_write_undo(C, "original");  /* save current state */
552
553         ED_editors_init(C);
554         DAG_on_visible_update(CTX_data_main(C), TRUE);
555
556 #ifdef WITH_PYTHON
557         if (CTX_py_init_get(C)) {
558                 /* sync addons, these may have changed from the defaults */
559                 BPY_string_exec(C, "__import__('addon_utils').reset_all()");
560
561                 BPY_driver_reset();
562                 BPY_app_handlers_reset(FALSE);
563                 BPY_modules_load_user(C);
564         }
565 #endif
566
567         WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL);
568
569         /* in background mode the scene will stay NULL */
570         if (!G.background) {
571                 CTX_wm_window_set(C, NULL); /* exits queues */
572         }
573
574         return TRUE;
575 }
576
577 int WM_read_homefile_exec(bContext *C, wmOperator *op)
578 {
579         int from_memory = strcmp(op->type->idname, "WM_OT_read_factory_settings") == 0;
580         return WM_read_homefile(C, op->reports, from_memory) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
581 }
582
583 void WM_read_history(void)
584 {
585         char name[FILE_MAX];
586         LinkNode *l, *lines;
587         struct RecentFile *recent;
588         char *line;
589         int num;
590         char *cfgdir = BLI_get_folder(BLENDER_USER_CONFIG, NULL);
591
592         if (!cfgdir) return;
593
594         BLI_make_file_string("/", name, cfgdir, BLENDER_HISTORY_FILE);
595
596         lines = BLI_file_read_as_lines(name);
597
598         G.recent_files.first = G.recent_files.last = NULL;
599
600         /* read list of recent opened files from recent-files.txt to memory */
601         for (l = lines, num = 0; l && (num < U.recent_files); l = l->next) {
602                 line = l->link;
603                 if (line[0] && BLI_exists(line)) {
604                         recent = (RecentFile *)MEM_mallocN(sizeof(RecentFile), "RecentFile");
605                         BLI_addtail(&(G.recent_files), recent);
606                         recent->filepath = BLI_strdup(line);
607                         num++;
608                 }
609         }
610         
611         BLI_file_free_lines(lines);
612
613 }
614
615 static void write_history(void)
616 {
617         struct RecentFile *recent, *next_recent;
618         char name[FILE_MAX];
619         char *user_config_dir;
620         FILE *fp;
621         int i;
622
623         /* will be NULL in background mode */
624         user_config_dir = BLI_get_folder_create(BLENDER_USER_CONFIG, NULL);
625         if (!user_config_dir)
626                 return;
627
628         BLI_make_file_string("/", name, user_config_dir, BLENDER_HISTORY_FILE);
629
630         recent = G.recent_files.first;
631         /* refresh recent-files.txt of recent opened files, when current file was changed */
632         if (!(recent) || (BLI_path_cmp(recent->filepath, G.main->name) != 0)) {
633                 fp = BLI_fopen(name, "w");
634                 if (fp) {
635                         /* add current file to the beginning of list */
636                         recent = (RecentFile *)MEM_mallocN(sizeof(RecentFile), "RecentFile");
637                         recent->filepath = BLI_strdup(G.main->name);
638                         BLI_addhead(&(G.recent_files), recent);
639                         /* write current file to recent-files.txt */
640                         fprintf(fp, "%s\n", recent->filepath);
641                         recent = recent->next;
642                         i = 1;
643                         /* write rest of recent opened files to recent-files.txt */
644                         while ((i < U.recent_files) && (recent)) {
645                                 /* this prevents to have duplicities in list */
646                                 if (BLI_path_cmp(recent->filepath, G.main->name) != 0) {
647                                         fprintf(fp, "%s\n", recent->filepath);
648                                         recent = recent->next;
649                                 }
650                                 else {
651                                         next_recent = recent->next;
652                                         MEM_freeN(recent->filepath);
653                                         BLI_freelinkN(&(G.recent_files), recent);
654                                         recent = next_recent;
655                                 }
656                                 i++;
657                         }
658                         fclose(fp);
659                 }
660
661                 /* also update most recent files on System */
662                 GHOST_addToSystemRecentFiles(G.main->name);
663         }
664 }
665
666 /* screen can be NULL */
667 static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, int **thumb_pt)
668 {
669         /* will be scaled down, but gives some nice oversampling */
670         ImBuf *ibuf;
671         int *thumb;
672         char err_out[256] = "unknown";
673
674         /* screen if no camera found */
675         ScrArea *sa = NULL;
676         ARegion *ar = NULL;
677         View3D *v3d = NULL;
678
679         *thumb_pt = NULL;
680
681         /* scene can be NULL if running a script at startup and calling the save operator */
682         if (G.background || scene == NULL)
683                 return NULL;
684
685         if ((scene->camera == NULL) && (screen != NULL)) {
686                 sa = BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0);
687                 ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
688                 if (ar) {
689                         v3d = sa->spacedata.first;
690                 }
691         }
692
693         if (scene->camera == NULL && v3d == NULL) {
694                 return NULL;
695         }
696
697         /* gets scaled to BLEN_THUMB_SIZE */
698         if (scene->camera) {
699                 ibuf = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera,
700                                                              BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
701                                                              IB_rect, OB_SOLID, FALSE, err_out);
702         }
703         else {
704                 ibuf = ED_view3d_draw_offscreen_imbuf(scene, v3d, ar, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
705                                                       IB_rect, FALSE, err_out);
706         }
707
708         if (ibuf) {             
709                 float aspect = (scene->r.xsch * scene->r.xasp) / (scene->r.ysch * scene->r.yasp);
710
711                 /* dirty oversampling */
712                 IMB_scaleImBuf(ibuf, BLEN_THUMB_SIZE, BLEN_THUMB_SIZE);
713
714                 /* add pretty overlay */
715                 IMB_overlayblend_thumb(ibuf->rect, ibuf->x, ibuf->y, aspect);
716                 
717                 /* first write into thumb buffer */
718                 thumb = MEM_mallocN(((2 + (BLEN_THUMB_SIZE * BLEN_THUMB_SIZE))) * sizeof(int), "write_file thumb");
719
720                 thumb[0] = BLEN_THUMB_SIZE;
721                 thumb[1] = BLEN_THUMB_SIZE;
722
723                 memcpy(thumb + 2, ibuf->rect, BLEN_THUMB_SIZE * BLEN_THUMB_SIZE * sizeof(int));
724         }
725         else {
726                 /* '*thumb_pt' needs to stay NULL to prevent a bad thumbnail from being handled */
727                 fprintf(stderr, "blend_file_thumb failed to create thumbnail: %s\n", err_out);
728                 thumb = NULL;
729         }
730         
731         /* must be freed by caller */
732         *thumb_pt = thumb;
733         
734         return ibuf;
735 }
736
737 /* easy access from gdb */
738 int write_crash_blend(void)
739 {
740         char path[FILE_MAX];
741         int fileflags = G.fileflags & ~(G_FILE_HISTORY); /* don't do file history on crash file */
742
743         BLI_strncpy(path, G.main->name, sizeof(path));
744         BLI_replace_extension(path, sizeof(path), "_crash.blend");
745         if (BLO_write_file(G.main, path, fileflags, NULL, NULL)) {
746                 printf("written: %s\n", path);
747                 return 1;
748         }
749         else {
750                 printf("failed: %s\n", path);
751                 return 0;
752         }
753 }
754
755 int WM_write_file(bContext *C, const char *target, int fileflags, ReportList *reports, int copy)
756 {
757         Library *li;
758         int len;
759         char filepath[FILE_MAX];
760
761         int *thumb = NULL;
762         ImBuf *ibuf_thumb = NULL;
763
764         len = strlen(target);
765         
766         if (len == 0) {
767                 BKE_report(reports, RPT_ERROR, "Path is empty, cannot save");
768                 return -1;
769         }
770
771         if (len >= FILE_MAX) {
772                 BKE_report(reports, RPT_ERROR, "Path too long, cannot save");
773                 return -1;
774         }
775  
776         BLI_strncpy(filepath, target, FILE_MAX);
777         BLI_replace_extension(filepath, FILE_MAX, ".blend");
778         /* don't use 'target' anymore */
779         
780         /* send the OnSave event */
781         for (li = G.main->library.first; li; li = li->id.next) {
782                 if (BLI_path_cmp(li->filepath, filepath) == 0) {
783                         BKE_reportf(reports, RPT_ERROR, "Can't overwrite used library '%.240s'", filepath);
784                         return -1;
785                 }
786         }
787
788         /* blend file thumbnail */
789         /* save before exit_editmode, otherwise derivedmeshes for shared data corrupt #27765) */
790         if (U.flag & USER_SAVE_PREVIEWS) {
791                 ibuf_thumb = blend_file_thumb(CTX_data_scene(C), CTX_wm_screen(C), &thumb);
792         }
793
794         BLI_exec_cb(G.main, NULL, BLI_CB_EVT_SAVE_PRE);
795
796         /* operator now handles overwrite checks */
797
798         if (G.fileflags & G_AUTOPACK) {
799                 packAll(G.main, reports);
800         }
801         
802         ED_object_exit_editmode(C, EM_DO_UNDO);
803         ED_sculpt_force_update(C);
804
805         /* don't forget not to return without! */
806         WM_cursor_wait(1);
807         
808         fileflags |= G_FILE_HISTORY; /* write file history */
809
810         if (BLO_write_file(CTX_data_main(C), filepath, fileflags, reports, thumb)) {
811                 if (!copy) {
812                         G.relbase_valid = 1;
813                         BLI_strncpy(G.main->name, filepath, sizeof(G.main->name));  /* is guaranteed current file */
814         
815                         G.save_over = 1; /* disable untitled.blend convention */
816                 }
817
818                 if (fileflags & G_FILE_COMPRESS) G.fileflags |= G_FILE_COMPRESS;
819                 else G.fileflags &= ~G_FILE_COMPRESS;
820                 
821                 if (fileflags & G_FILE_AUTOPLAY) G.fileflags |= G_FILE_AUTOPLAY;
822                 else G.fileflags &= ~G_FILE_AUTOPLAY;
823
824                 /* prevent background mode scripts from clobbering history */
825                 if (!G.background) {
826                         write_history();
827                 }
828
829                 BLI_exec_cb(G.main, NULL, BLI_CB_EVT_SAVE_POST);
830
831                 /* run this function after because the file cant be written before the blend is */
832                 if (ibuf_thumb) {
833                         IMB_thumb_delete(filepath, THB_FAIL); /* without this a failed thumb overrides */
834                         ibuf_thumb = IMB_thumb_create(filepath, THB_NORMAL, THB_SOURCE_BLEND, ibuf_thumb);
835                         IMB_freeImBuf(ibuf_thumb);
836                 }
837
838                 if (thumb) MEM_freeN(thumb);
839         }
840         else {
841                 if (ibuf_thumb) IMB_freeImBuf(ibuf_thumb);
842                 if (thumb) MEM_freeN(thumb);
843                 
844                 WM_cursor_wait(0);
845                 return -1;
846         }
847
848         WM_cursor_wait(0);
849         
850         return 0;
851 }
852
853 /* operator entry */
854 int WM_write_homefile(bContext *C, wmOperator *op)
855 {
856         wmWindowManager *wm = CTX_wm_manager(C);
857         wmWindow *win = CTX_wm_window(C);
858         char filepath[FILE_MAX];
859         int fileflags;
860
861         /* check current window and close it if temp */
862         if (win->screen->temp)
863                 wm_window_close(C, wm, win);
864         
865         /* update keymaps in user preferences */
866         WM_keyconfig_update(wm);
867         
868         BLI_make_file_string("/", filepath, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_STARTUP_FILE);
869         printf("trying to save homefile at %s ", filepath);
870         
871         /*  force save as regular blend file */
872         fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_LOCK | G_FILE_SIGN | G_FILE_HISTORY);
873
874         if (BLO_write_file(CTX_data_main(C), filepath, fileflags, op->reports, NULL) == 0) {
875                 printf("fail\n");
876                 return OPERATOR_CANCELLED;
877         }
878         
879         printf("ok\n");
880
881         G.save_over = 0;
882
883         return OPERATOR_FINISHED;
884 }
885
886 /************************ autosave ****************************/
887
888 void wm_autosave_location(char *filepath)
889 {
890         char pidstr[32];
891 #ifdef WIN32
892         char *savedir;
893 #endif
894
895         BLI_snprintf(pidstr, sizeof(pidstr), "%d.blend", abs(getpid()));
896
897 #ifdef WIN32
898         /* XXX Need to investigate how to handle default location of '/tmp/'
899          * This is a relative directory on Windows, and it may be
900          * found. Example:
901          * Blender installed on D:\ drive, D:\ drive has D:\tmp\
902          * Now, BLI_exists() will find '/tmp/' exists, but
903          * BLI_make_file_string will create string that has it most likely on C:\
904          * through get_default_root().
905          * If there is no C:\tmp autosave fails. */
906         if (!BLI_exists(BLI_temporary_dir())) {
907                 savedir = BLI_get_folder_create(BLENDER_USER_AUTOSAVE, NULL);
908                 BLI_make_file_string("/", filepath, savedir, pidstr);
909                 return;
910         }
911 #endif
912
913         BLI_make_file_string("/", filepath, BLI_temporary_dir(), pidstr);
914 }
915
916 void WM_autosave_init(wmWindowManager *wm)
917 {
918         wm_autosave_timer_ended(wm);
919
920         if (U.flag & USER_AUTOSAVE)
921                 wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, U.savetime * 60.0);
922 }
923
924 void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *UNUSED(wt))
925 {
926         wmWindow *win;
927         wmEventHandler *handler;
928         char filepath[FILE_MAX];
929         int fileflags;
930
931         WM_event_remove_timer(wm, NULL, wm->autosavetimer);
932
933         /* if a modal operator is running, don't autosave, but try again in 10 seconds */
934         for (win = wm->windows.first; win; win = win->next) {
935                 for (handler = win->modalhandlers.first; handler; handler = handler->next) {
936                         if (handler->op) {
937                                 wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, 10.0);
938                                 return;
939                         }
940                 }
941         }
942         
943         wm_autosave_location(filepath);
944
945         /*  force save as regular blend file */
946         fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_LOCK | G_FILE_SIGN | G_FILE_HISTORY);
947
948         /* no error reporting to console */
949         BLO_write_file(CTX_data_main(C), filepath, fileflags, NULL, NULL);
950
951         /* do timer after file write, just in case file write takes a long time */
952         wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, U.savetime * 60.0);
953 }
954
955 void wm_autosave_timer_ended(wmWindowManager *wm)
956 {
957         if (wm->autosavetimer) {
958                 WM_event_remove_timer(wm, NULL, wm->autosavetimer);
959                 wm->autosavetimer = NULL;
960         }
961 }
962
963 void wm_autosave_delete(void)
964 {
965         char filename[FILE_MAX];
966         
967         wm_autosave_location(filename);
968
969         if (BLI_exists(filename)) {
970                 char str[FILE_MAX];
971                 BLI_make_file_string("/", str, BLI_temporary_dir(), "quit.blend");
972
973                 /* if global undo; remove tempsave, otherwise rename */
974                 if (U.uiflag & USER_GLOBALUNDO) BLI_delete(filename, 0, 0);
975                 else BLI_rename(filename, str);
976         }
977 }
978
979 void wm_autosave_read(bContext *C, ReportList *reports)
980 {
981         char filename[FILE_MAX];
982
983         wm_autosave_location(filename);
984         WM_read_file(C, filename, reports);
985 }
986