diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2016-04-19 18:08:31 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2016-04-19 23:53:18 +0300 |
commit | 52e09ed4f93b21aee8643de2f8b53e9fa7c99543 (patch) | |
tree | a711758277e06071c69abd1c92f24fa540269cd3 /source/blender | |
parent | dd423a6818cc8132ba073dfbeb66cb40d3f09611 (diff) |
Cleanup: move operators & related code of files & append/link to relevant dedicated source files.
This commit adds a new `wm_files_link.c` which contains everything related to append/link code,
moved from `wm_operators.c` (rather small currently, but will expand quite a bit with future reload & asset works).
It also moves all load/save .bland files (and related userpref/startup stuff) from `wm_operators.c`
to `wm_files.c` (some helper funcs were already there).
This also makes `wm_operators.c` significantly lighter.
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/windowmanager/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_files.c | 833 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_files_link.c | 538 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_operators.c | 1020 | ||||
-rw-r--r-- | source/blender/windowmanager/wm_files.h | 28 |
5 files changed, 1292 insertions, 1128 deletions
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index af8a7cca8dd..01188cb7f65 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -58,6 +58,7 @@ set(SRC intern/wm_draw.c intern/wm_event_system.c intern/wm_files.c + intern/wm_files_link.c intern/wm_gesture.c intern/wm_init_exit.c intern/wm_jobs.c diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index ce722c732e6..498a3f5bdda 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -26,7 +26,7 @@ /** \file blender/windowmanager/intern/wm_files.c * \ingroup wm * - * User level access for blend file read/write, file-history and userprefs. + * User level access for blend file read/write, file-history and userprefs (including relevant operators). */ @@ -62,6 +62,7 @@ #include "BLT_translation.h" +#include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */ #include "DNA_object_types.h" #include "DNA_space_types.h" #include "DNA_userdef_types.h" @@ -88,6 +89,7 @@ #include "BLO_writefile.h" #include "RNA_access.h" +#include "RNA_define.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -103,6 +105,7 @@ #include "GHOST_Path-api.h" #include "UI_interface.h" +#include "UI_resources.h" #include "UI_view2d.h" #include "GPU_draw.h" @@ -749,44 +752,6 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c return true; } -int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) -{ - ED_file_read_bookmarks(); - wm_history_file_read(); - return OPERATOR_FINISHED; -} - -int wm_homefile_read_exec(bContext *C, wmOperator *op) -{ - const bool from_memory = (STREQ(op->type->idname, "WM_OT_read_factory_settings")); - char filepath_buf[FILE_MAX]; - const char *filepath = NULL; - - if (!from_memory) { - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath"); - - /* This can be used when loading of a start-up file should only change - * the scene content but keep the blender UI as it is. */ - wm_open_init_load_ui(op, true); - BKE_BIT_TEST_SET(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI); - - if (RNA_property_is_set(op->ptr, prop)) { - RNA_property_string_get(op->ptr, prop, filepath_buf); - filepath = filepath_buf; - if (BLI_access(filepath, R_OK)) { - BKE_reportf(op->reports, RPT_ERROR, "Can't read alternative start-up file: '%s'", filepath); - return OPERATOR_CANCELLED; - } - } - } - else { - /* always load UI for factory settings (prefs will re-init) */ - G.fileflags &= ~G_FILE_NO_UI; - } - - return wm_homefile_read(C, op->reports, from_memory, filepath) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; -} - /** \name WM History File API * \{ */ @@ -1005,7 +970,7 @@ bool write_crash_blend(void) /** * \see #wm_homefile_write_exec wraps #BLO_write_file in a similar way. */ -int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports) +static int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports) { Library *li; int len; @@ -1117,69 +1082,6 @@ int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList * return ret; } -/** - * \see #wm_file_write wraps #BLO_write_file in a similar way. - */ -int wm_homefile_write_exec(bContext *C, wmOperator *op) -{ - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - char filepath[FILE_MAX]; - int fileflags; - - BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE); - - /* check current window and close it if temp */ - if (win && win->screen->temp) - wm_window_close(C, wm, win); - - /* update keymaps in user preferences */ - WM_keyconfig_update(wm); - - BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_STARTUP_FILE); - printf("trying to save homefile at %s ", filepath); - - ED_editors_flush_edits(C, false); - - /* force save as regular blend file */ - fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY); - - if (BLO_write_file(CTX_data_main(C), filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) { - printf("fail\n"); - return OPERATOR_CANCELLED; - } - - printf("ok\n"); - - G.save_over = 0; - - BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_POST); - - return OPERATOR_FINISHED; -} - -/* Only save the prefs block. operator entry */ -int wm_userpref_write_exec(bContext *C, wmOperator *op) -{ - wmWindowManager *wm = CTX_wm_manager(C); - char filepath[FILE_MAX]; - - /* update keymaps in user preferences */ - WM_keyconfig_update(wm); - - BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_USERPREF_FILE); - printf("trying to save userpref at %s ", filepath); - - if (BKE_write_file_userdef(filepath, op->reports) == 0) { - printf("fail\n"); - return OPERATOR_CANCELLED; - } - - printf("ok\n"); - - return OPERATOR_FINISHED; -} - /************************ autosave ****************************/ void wm_autosave_location(char *filepath) @@ -1341,3 +1243,728 @@ void WM_file_tag_modified(const bContext *C) WM_event_add_notifier(C, NC_WM | ND_DATACHANGED, NULL); } } + +/** \name Preferences/startup save & load. + * + * \{ */ + +/** + * \see #wm_file_write wraps #BLO_write_file in a similar way. + */ +static int wm_homefile_write_exec(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + char filepath[FILE_MAX]; + int fileflags; + + BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE); + + /* check current window and close it if temp */ + if (win && win->screen->temp) + wm_window_close(C, wm, win); + + /* update keymaps in user preferences */ + WM_keyconfig_update(wm); + + BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_STARTUP_FILE); + printf("trying to save homefile at %s ", filepath); + + ED_editors_flush_edits(C, false); + + /* force save as regular blend file */ + fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY); + + if (BLO_write_file(CTX_data_main(C), filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) { + printf("fail\n"); + return OPERATOR_CANCELLED; + } + + printf("ok\n"); + + G.save_over = 0; + + BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_POST); + + return OPERATOR_FINISHED; +} + +void WM_OT_save_homefile(wmOperatorType *ot) +{ + ot->name = "Save Startup File"; + ot->idname = "WM_OT_save_homefile"; + ot->description = "Make the current file the default .blend file, includes preferences"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_homefile_write_exec; +} + +static int wm_userpref_autoexec_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + bPathCompare *path_cmp = MEM_callocN(sizeof(bPathCompare), "bPathCompare"); + BLI_addtail(&U.autoexec_paths, path_cmp); + return OPERATOR_FINISHED; +} + +void WM_OT_userpref_autoexec_path_add(wmOperatorType *ot) +{ + ot->name = "Add Autoexec Path"; + ot->idname = "WM_OT_userpref_autoexec_path_add"; + ot->description = "Add path to exclude from autoexecution"; + + ot->exec = wm_userpref_autoexec_add_exec; + + ot->flag = OPTYPE_INTERNAL; +} + +static int wm_userpref_autoexec_remove_exec(bContext *UNUSED(C), wmOperator *op) +{ + const int index = RNA_int_get(op->ptr, "index"); + bPathCompare *path_cmp = BLI_findlink(&U.autoexec_paths, index); + if (path_cmp) { + BLI_freelinkN(&U.autoexec_paths, path_cmp); + } + return OPERATOR_FINISHED; +} + +void WM_OT_userpref_autoexec_path_remove(wmOperatorType *ot) +{ + ot->name = "Remove Autoexec Path"; + ot->idname = "WM_OT_userpref_autoexec_path_remove"; + ot->description = "Remove path to exclude from autoexecution"; + + ot->exec = wm_userpref_autoexec_remove_exec; + + ot->flag = OPTYPE_INTERNAL; + + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000); +} + +/* Only save the prefs block. operator entry */ +static int wm_userpref_write_exec(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + char filepath[FILE_MAX]; + + /* update keymaps in user preferences */ + WM_keyconfig_update(wm); + + BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_USERPREF_FILE); + printf("trying to save userpref at %s ", filepath); + + if (BKE_write_file_userdef(filepath, op->reports) == 0) { + printf("fail\n"); + return OPERATOR_CANCELLED; + } + + printf("ok\n"); + + return OPERATOR_FINISHED; +} + +void WM_OT_save_userpref(wmOperatorType *ot) +{ + ot->name = "Save User Settings"; + ot->idname = "WM_OT_save_userpref"; + ot->description = "Save user preferences separately, overrides startup file preferences"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_userpref_write_exec; +} + +static int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + ED_file_read_bookmarks(); + wm_history_file_read(); + return OPERATOR_FINISHED; +} + +void WM_OT_read_history(wmOperatorType *ot) +{ + ot->name = "Reload History File"; + ot->idname = "WM_OT_read_history"; + ot->description = "Reloads history and bookmarks"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_history_file_read_exec; + + /* this operator is only used for loading settings from a previous blender install */ + ot->flag = OPTYPE_INTERNAL; +} + +static int wm_homefile_read_exec(bContext *C, wmOperator *op) +{ + const bool from_memory = (STREQ(op->type->idname, "WM_OT_read_factory_settings")); + char filepath_buf[FILE_MAX]; + const char *filepath = NULL; + + if (!from_memory) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath"); + + /* This can be used when loading of a start-up file should only change + * the scene content but keep the blender UI as it is. */ + wm_open_init_load_ui(op, true); + BKE_BIT_TEST_SET(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI); + + if (RNA_property_is_set(op->ptr, prop)) { + RNA_property_string_get(op->ptr, prop, filepath_buf); + filepath = filepath_buf; + if (BLI_access(filepath, R_OK)) { + BKE_reportf(op->reports, RPT_ERROR, "Can't read alternative start-up file: '%s'", filepath); + return OPERATOR_CANCELLED; + } + } + } + else { + /* always load UI for factory settings (prefs will re-init) */ + G.fileflags &= ~G_FILE_NO_UI; + } + + return wm_homefile_read(C, op->reports, from_memory, filepath) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + +void WM_OT_read_homefile(wmOperatorType *ot) +{ + PropertyRNA *prop; + ot->name = "Reload Start-Up File"; + ot->idname = "WM_OT_read_homefile"; + ot->description = "Open the default file (doesn't save the current file)"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_homefile_read_exec; + + prop = RNA_def_string_file_path(ot->srna, "filepath", NULL, + FILE_MAX, "File Path", + "Path to an alternative start-up file"); + RNA_def_property_flag(prop, PROP_HIDDEN); + + /* So scripts can use an alternative start-up file without the UI */ + prop = RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", + "Load user interface setup from the .blend file"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + /* omit poll to run in background mode */ +} + +void WM_OT_read_factory_settings(wmOperatorType *ot) +{ + ot->name = "Load Factory Settings"; + ot->idname = "WM_OT_read_factory_settings"; + ot->description = "Load default file and user preferences"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_homefile_read_exec; + /* omit poll to run in background mode */ +} + +/** \} */ + +/** \name Open main .blend file. + * + * \{ */ + +/** + * Wrap #WM_file_read, shared by file reading operators. + */ +static bool wm_file_read_opwrap(bContext *C, const char *filepath, ReportList *reports, + const bool autoexec_init) +{ + bool success; + + /* XXX wm in context is not set correctly after WM_file_read -> crash */ + /* do it before for now, but is this correct with multiple windows? */ + WM_event_add_notifier(C, NC_WINDOW, NULL); + + if (autoexec_init) { + WM_file_autoexec_init(filepath); + } + + success = WM_file_read(C, filepath, reports); + + return success; +} + +/* currently fits in a pointer */ +struct FileRuntime { + bool is_untrusted; +}; + +static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + const char *openname = G.main->name; + + if (CTX_wm_window(C) == NULL) { + /* in rare cases this could happen, when trying to invoke in background + * mode on load for example. Don't use poll for this because exec() + * can still run without a window */ + BKE_report(op->reports, RPT_ERROR, "Context window not set"); + return OPERATOR_CANCELLED; + } + + /* if possible, get the name of the most recently used .blend file */ + if (G.recent_files.first) { + struct RecentFile *recent = G.recent_files.first; + openname = recent->filepath; + } + + RNA_string_set(op->ptr, "filepath", openname); + wm_open_init_load_ui(op, true); + wm_open_init_use_scripts(op, true); + op->customdata = NULL; + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int wm_open_mainfile_exec(bContext *C, wmOperator *op) +{ + char filepath[FILE_MAX]; + bool success; + + RNA_string_get(op->ptr, "filepath", filepath); + + /* re-use last loaded setting so we can reload a file without changing */ + wm_open_init_load_ui(op, false); + wm_open_init_use_scripts(op, false); + + if (RNA_boolean_get(op->ptr, "load_ui")) + G.fileflags &= ~G_FILE_NO_UI; + else + G.fileflags |= G_FILE_NO_UI; + + if (RNA_boolean_get(op->ptr, "use_scripts")) + G.f |= G_SCRIPT_AUTOEXEC; + else + G.f &= ~G_SCRIPT_AUTOEXEC; + + success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); + + /* for file open also popup for warnings, not only errors */ + BKE_report_print_level_set(op->reports, RPT_WARNING); + + if (success) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op) +{ + struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts"); + bool is_untrusted = false; + char path[FILE_MAX]; + char *lslash; + + RNA_string_get(op->ptr, "filepath", path); + + /* get the dir */ + lslash = (char *)BLI_last_slash(path); + if (lslash) *(lslash + 1) = '\0'; + + if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) { + if (BKE_autoexec_match(path) == true) { + RNA_property_boolean_set(op->ptr, prop, false); + is_untrusted = true; + } + } + + if (file_info) { + file_info->is_untrusted = is_untrusted; + } + + return is_untrusted; +} + +static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op) +{ + struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; + uiLayout *layout = op->layout; + uiLayout *col = op->layout; + const char *autoexec_text; + + uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE); + + col = uiLayoutColumn(layout, false); + if (file_info->is_untrusted) { + autoexec_text = IFACE_("Trusted Source [Untrusted Path]"); + uiLayoutSetActive(col, false); + uiLayoutSetEnabled(col, false); + } + else { + autoexec_text = IFACE_("Trusted Source"); + } + + uiItemR(col, op->ptr, "use_scripts", 0, autoexec_text, ICON_NONE); +} + +void WM_OT_open_mainfile(wmOperatorType *ot) +{ + ot->name = "Open Blender File"; + ot->idname = "WM_OT_open_mainfile"; + ot->description = "Open a Blender file"; + + ot->invoke = wm_open_mainfile_invoke; + ot->exec = wm_open_mainfile_exec; + ot->check = wm_open_mainfile_check; + ot->ui = wm_open_mainfile_ui; + /* omit window poll so this can work in background mode */ + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file"); + RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", + "Allow .blend file to execute scripts automatically, default available from system preferences"); +} + +/** \} */ + +/** \name Reload (revert) main .blend file. + * + * \{ */ + +static int wm_revert_mainfile_exec(bContext *C, wmOperator *op) +{ + bool success; + + wm_open_init_use_scripts(op, false); + + if (RNA_boolean_get(op->ptr, "use_scripts")) + G.f |= G_SCRIPT_AUTOEXEC; + else + G.f &= ~G_SCRIPT_AUTOEXEC; + + success = wm_file_read_opwrap(C, G.main->name, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); + + if (success) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static int wm_revert_mainfile_poll(bContext *UNUSED(C)) +{ + return G.relbase_valid; +} + +void WM_OT_revert_mainfile(wmOperatorType *ot) +{ + ot->name = "Revert"; + ot->idname = "WM_OT_revert_mainfile"; + ot->description = "Reload the saved file"; + ot->invoke = WM_operator_confirm; + + RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", + "Allow .blend file to execute scripts automatically, default available from system preferences"); + + ot->exec = wm_revert_mainfile_exec; + ot->poll = wm_revert_mainfile_poll; +} + +/** \} */ + +/** \name Recover last session & auto-save. + * + * \{ */ + +void WM_recover_last_session(bContext *C, ReportList *reports) +{ + char filepath[FILE_MAX]; + + BLI_make_file_string("/", filepath, BKE_tempdir_base(), BLENDER_QUIT_FILE); + /* if reports==NULL, it's called directly without operator, we add a quick check here */ + if (reports || BLI_exists(filepath)) { + G.fileflags |= G_FILE_RECOVER; + + wm_file_read_opwrap(C, filepath, reports, true); + + G.fileflags &= ~G_FILE_RECOVER; + + /* XXX bad global... fixme */ + if (G.main->name[0]) + G.file_loaded = 1; /* prevents splash to show */ + else { + G.relbase_valid = 0; + G.save_over = 0; /* start with save preference untitled.blend */ + } + + } +} + +static int wm_recover_last_session_exec(bContext *C, wmOperator *op) +{ + WM_recover_last_session(C, op->reports); + return OPERATOR_FINISHED; +} + +void WM_OT_recover_last_session(wmOperatorType *ot) +{ + ot->name = "Recover Last Session"; + ot->idname = "WM_OT_recover_last_session"; + ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")"; + ot->invoke = WM_operator_confirm; + + ot->exec = wm_recover_last_session_exec; +} + +static int wm_recover_auto_save_exec(bContext *C, wmOperator *op) +{ + char filepath[FILE_MAX]; + bool success; + + RNA_string_get(op->ptr, "filepath", filepath); + + G.fileflags |= G_FILE_RECOVER; + + success = wm_file_read_opwrap(C, filepath, op->reports, true); + + G.fileflags &= ~G_FILE_RECOVER; + + if (success) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + char filename[FILE_MAX]; + + wm_autosave_location(filename); + RNA_string_set(op->ptr, "filepath", filename); + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +void WM_OT_recover_auto_save(wmOperatorType *ot) +{ + ot->name = "Recover Auto Save"; + ot->idname = "WM_OT_recover_auto_save"; + ot->description = "Open an automatically saved file to recover it"; + + ot->exec = wm_recover_auto_save_exec; + ot->invoke = wm_recover_auto_save_invoke; + + WM_operator_properties_filesel( + ot, FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, + WM_FILESEL_FILEPATH, FILE_LONGDISPLAY, FILE_SORT_TIME); +} + +/** \} */ + +/** \name Save main .blend file. + * + * \{ */ + +static void wm_filepath_default(char *filepath) +{ + if (G.save_over == false) { + BLI_ensure_filename(filepath, FILE_MAX, "untitled.blend"); + } +} + +static void save_set_compress(wmOperator *op) +{ + PropertyRNA *prop; + + prop = RNA_struct_find_property(op->ptr, "compress"); + if (!RNA_property_is_set(op->ptr, prop)) { + if (G.save_over) { /* keep flag for existing file */ + RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0); + } + else { /* use userdef for new file */ + RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0); + } + } +} + +static void save_set_filepath(wmOperator *op) +{ + PropertyRNA *prop; + char name[FILE_MAX]; + + prop = RNA_struct_find_property(op->ptr, "filepath"); + if (!RNA_property_is_set(op->ptr, prop)) { + /* if not saved before, get the name of the most recently used .blend file */ + if (G.main->name[0] == 0 && G.recent_files.first) { + struct RecentFile *recent = G.recent_files.first; + BLI_strncpy(name, recent->filepath, FILE_MAX); + } + else { + BLI_strncpy(name, G.main->name, FILE_MAX); + } + + wm_filepath_default(name); + RNA_property_string_set(op->ptr, prop, name); + } +} + +static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + + save_set_compress(op); + save_set_filepath(op); + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +/* function used for WM_OT_save_mainfile too */ +static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) +{ + char path[FILE_MAX]; + int fileflags; + + save_set_compress(op); + + if (RNA_struct_property_is_set(op->ptr, "filepath")) { + RNA_string_get(op->ptr, "filepath", path); + } + else { + BLI_strncpy(path, G.main->name, FILE_MAX); + wm_filepath_default(path); + } + + fileflags = G.fileflags & ~G_FILE_USERPREFS; + + /* set compression flag */ + BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "compress"), + G_FILE_COMPRESS); + BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "relative_remap"), + G_FILE_RELATIVE_REMAP); + BKE_BIT_TEST_SET(fileflags, + (RNA_struct_property_is_set(op->ptr, "copy") && + RNA_boolean_get(op->ptr, "copy")), + G_FILE_SAVE_COPY); + +#ifdef USE_BMESH_SAVE_AS_COMPAT + BKE_BIT_TEST_SET(fileflags, + (RNA_struct_find_property(op->ptr, "use_mesh_compat") && + RNA_boolean_get(op->ptr, "use_mesh_compat")), + G_FILE_MESH_COMPAT); +#else +# error "don't remove by accident" +#endif + + if (wm_file_write(C, path, fileflags, op->reports) != 0) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL); + + return OPERATOR_FINISHED; +} + +/* function used for WM_OT_save_mainfile too */ +static bool blend_save_check(bContext *UNUSED(C), wmOperator *op) +{ + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + if (!BLO_has_bfile_extension(filepath)) { + /* some users would prefer BLI_replace_extension(), + * we keep getting nitpicking bug reports about this - campbell */ + BLI_ensure_extension(filepath, FILE_MAX, ".blend"); + RNA_string_set(op->ptr, "filepath", filepath); + return true; + } + return false; +} + +void WM_OT_save_as_mainfile(wmOperatorType *ot) +{ + PropertyRNA *prop; + + ot->name = "Save As Blender File"; + ot->idname = "WM_OT_save_as_mainfile"; + ot->description = "Save the current file in the desired location"; + + ot->invoke = wm_save_as_mainfile_invoke; + ot->exec = wm_save_as_mainfile_exec; + ot->check = blend_save_check; + /* omit window poll so this can work in background mode */ + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); + RNA_def_boolean(ot->srna, "relative_remap", true, "Remap Relative", + "Remap relative paths when saving in a different directory"); + prop = RNA_def_boolean(ot->srna, "copy", false, "Save Copy", + "Save a copy of the actual working state but does not make saved file active"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +#ifdef USE_BMESH_SAVE_AS_COMPAT + RNA_def_boolean(ot->srna, "use_mesh_compat", false, "Legacy Mesh Format", + "Save using legacy mesh format (no ngons) - WARNING: only saves tris and quads, other ngons will " + "be lost (no implicit triangulation)"); +#endif +} + +static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + int ret; + + /* cancel if no active window */ + if (CTX_wm_window(C) == NULL) + return OPERATOR_CANCELLED; + + save_set_compress(op); + save_set_filepath(op); + + /* if we're saving for the first time and prefer relative paths - any existing paths will be absolute, + * enable the option to remap paths to avoid confusion [#37240] */ + if ((G.relbase_valid == false) && (U.flag & USER_RELPATHS)) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_boolean_set(op->ptr, prop, true); + } + } + + if (G.save_over) { + char path[FILE_MAX]; + + RNA_string_get(op->ptr, "filepath", path); + if (BLI_exists(path)) { + ret = WM_operator_confirm_message_ex(C, op, IFACE_("Save Over?"), ICON_QUESTION, path); + } + else { + ret = wm_save_as_mainfile_exec(C, op); + } + } + else { + WM_event_add_fileselect(C, op); + ret = OPERATOR_RUNNING_MODAL; + } + + return ret; +} + +void WM_OT_save_mainfile(wmOperatorType *ot) +{ + ot->name = "Save Blender File"; + ot->idname = "WM_OT_save_mainfile"; + ot->description = "Save the current Blender file"; + + ot->invoke = wm_save_mainfile_invoke; + ot->exec = wm_save_as_mainfile_exec; + ot->check = blend_save_check; + /* omit window poll so this can work in background mode */ + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); + RNA_def_boolean(ot->srna, "relative_remap", false, "Remap Relative", + "Remap relative paths when saving in a different directory"); +} + +/** \} */ diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c new file mode 100644 index 00000000000..f769efd1e6f --- /dev/null +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -0,0 +1,538 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/intern/wm_files_link.c + * \ingroup wm + * + * Functions for dealing with append/link operators and helpers. + */ + + +#include <float.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <stddef.h> +#include <assert.h> +#include <errno.h> + +#ifdef WIN32 +# include "GHOST_C-api.h" +#endif + +#include "MEM_guardedalloc.h" + +#include "DNA_ID.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_userdef_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */ + +#include "BLT_translation.h" + +#include "PIL_time.h" + +#include "BLI_blenlib.h" +#include "BLI_bitmap.h" +#include "BLI_dial.h" +#include "BLI_dynstr.h" /*for WM_operator_pystring */ +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_utildefines.h" +#include "BLI_ghash.h" + +#include "BLO_readfile.h" + +#include "BKE_appdir.h" +#include "BKE_autoexec.h" +#include "BKE_blender.h" +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_icons.h" +#include "BKE_idprop.h" +#include "BKE_image.h" +#include "BKE_library.h" +#include "BKE_library_query.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" /* BKE_ST_MAXNAME */ +#include "BKE_unit.h" +#include "BKE_utildefines.h" + +#include "BKE_idcode.h" + +#include "BIF_glutil.h" /* for paint cursor */ +#include "BLF_api.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" + +#include "ED_numinput.h" +#include "ED_screen.h" +#include "ED_util.h" +#include "ED_view3d.h" + +#include "GPU_basic_shader.h" +#include "GPU_material.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_interface_icons.h" +#include "UI_resources.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm.h" +#include "wm_draw.h" +#include "wm_event_system.h" +#include "wm_event_types.h" +#include "wm_files.h" +#include "wm_subwindow.h" +#include "wm_window.h" + +/* **************** link/append *************** */ + +static int wm_link_append_poll(bContext *C) +{ + if (WM_operator_winactive(C)) { + /* linking changes active object which is pretty useful in general, + * but which totally confuses edit mode (i.e. it becoming not so obvious + * to leave from edit mode and invalid tools in toolbar might be displayed) + * so disable link/append when in edit mode (sergey) */ + if (CTX_data_edit_object(C)) + return 0; + + return 1; + } + + return 0; +} + +static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (RNA_struct_property_is_set(op->ptr, "filepath")) { + return WM_operator_call_notest(C, op); + } + else { + /* XXX TODO solve where to get last linked library from */ + if (G.lib[0] != '\0') { + RNA_string_set(op->ptr, "filepath", G.lib); + } + else if (G.relbase_valid) { + char path[FILE_MAX]; + BLI_strncpy(path, G.main->name, sizeof(G.main->name)); + BLI_parent_dir(path); + RNA_string_set(op->ptr, "filepath", path); + } + WM_event_add_fileselect(C, op); + return OPERATOR_RUNNING_MODAL; + } +} + +static short wm_link_append_flag(wmOperator *op) +{ + PropertyRNA *prop; + short flag = 0; + + if (RNA_boolean_get(op->ptr, "autoselect")) + flag |= FILE_AUTOSELECT; + if (RNA_boolean_get(op->ptr, "active_layer")) + flag |= FILE_ACTIVELAY; + if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop)) + flag |= FILE_RELPATH; + if (RNA_boolean_get(op->ptr, "link")) + flag |= FILE_LINK; + if (RNA_boolean_get(op->ptr, "instance_groups")) + flag |= FILE_GROUP_INSTANCE; + + return flag; +} + +typedef struct WMLinkAppendDataItem { + char *name; + BLI_bitmap *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */ + short idcode; + + ID *new_id; + void *customdata; +} WMLinkAppendDataItem; + +typedef struct WMLinkAppendData { + LinkNodePair libraries; + LinkNodePair items; + int num_libraries; + int num_items; + short flag; + + /* Internal 'private' data */ + MemArena *memarena; +} WMLinkAppendData; + +static WMLinkAppendData *wm_link_append_data_new(const int flag) +{ + MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + WMLinkAppendData *lapp_data = BLI_memarena_calloc(ma, sizeof(*lapp_data)); + + lapp_data->flag = flag; + lapp_data->memarena = ma; + + return lapp_data; +} + +static void wm_link_append_data_free(WMLinkAppendData *lapp_data) +{ + BLI_memarena_free(lapp_data->memarena); +} + +/* WARNING! *Never* call wm_link_append_data_library_add() after having added some items! */ + +static void wm_link_append_data_library_add(WMLinkAppendData *lapp_data, const char *libname) +{ + size_t len = strlen(libname) + 1; + char *libpath = BLI_memarena_alloc(lapp_data->memarena, len); + + BLI_strncpy(libpath, libname, len); + BLI_linklist_append_arena(&lapp_data->libraries, libpath, lapp_data->memarena); + lapp_data->num_libraries++; +} + +static WMLinkAppendDataItem *wm_link_append_data_item_add( + WMLinkAppendData *lapp_data, const char *idname, const short idcode, void *customdata) +{ + WMLinkAppendDataItem *item = BLI_memarena_alloc(lapp_data->memarena, sizeof(*item)); + size_t len = strlen(idname) + 1; + + item->name = BLI_memarena_alloc(lapp_data->memarena, len); + BLI_strncpy(item->name, idname, len); + item->idcode = idcode; + item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries); + + item->new_id = NULL; + item->customdata = customdata; + + BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena); + lapp_data->num_items++; + + return item; +} + +static void wm_link_do( + WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d) +{ + Main *mainl; + BlendHandle *bh; + Library *lib; + + const int flag = lapp_data->flag; + + LinkNode *liblink, *itemlink; + int lib_idx, item_idx; + + BLI_assert(lapp_data->num_items && lapp_data->num_libraries); + + for (lib_idx = 0, liblink = lapp_data->libraries.list; liblink; lib_idx++, liblink = liblink->next) { + char *libname = liblink->link; + + bh = BLO_blendhandle_from_file(libname, reports); + + if (bh == NULL) { + /* Unlikely since we just browsed it, but possible + * Error reports will have been made by BLO_blendhandle_from_file() */ + continue; + } + + /* here appending/linking starts */ + mainl = BLO_library_link_begin(bmain, &bh, libname); + lib = mainl->curlib; + BLI_assert(lib); + UNUSED_VARS_NDEBUG(lib); + + if (mainl->versionfile < 250) { + BKE_reportf(reports, RPT_WARNING, + "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will " + "be done! You may want to re-save your lib file with current Blender", + mainl->versionfile, mainl->subversionfile); + } + + /* For each lib file, we try to link all items belonging to that lib, + * and tag those successful to not try to load them again with the other libs. */ + for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *new_id; + + if (!BLI_BITMAP_TEST(item->libraries, lib_idx)) { + continue; + } + + new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d); + if (new_id) { + /* If the link is sucessful, clear item's libs 'todo' flags. + * This avoids trying to link same item with other libraries to come. */ + BLI_BITMAP_SET_ALL(item->libraries, false, lapp_data->num_libraries); + item->new_id = new_id; + } + } + + BLO_library_link_end(mainl, &bh, flag, scene, v3d); + BLO_blendhandle_close(bh); + } +} + +static int wm_link_append_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + PropertyRNA *prop; + WMLinkAppendData *lapp_data; + char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX]; + char *group, *name; + int totfiles = 0; + short flag; + + RNA_string_get(op->ptr, "filename", relname); + RNA_string_get(op->ptr, "directory", root); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + /* test if we have a valid data */ + if (!BLO_library_path_explode(path, libname, &group, &name)) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': not a library", path); + return OPERATOR_CANCELLED; + } + else if (!group) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); + return OPERATOR_CANCELLED; + } + else if (BLI_path_cmp(bmain->name, libname) == 0) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': cannot use current file as library", path); + return OPERATOR_CANCELLED; + } + + /* check if something is indicated for append/link */ + prop = RNA_struct_find_property(op->ptr, "files"); + if (prop) { + totfiles = RNA_property_collection_length(op->ptr, prop); + if (totfiles == 0) { + if (!name) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); + return OPERATOR_CANCELLED; + } + } + } + else if (!name) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); + return OPERATOR_CANCELLED; + } + + flag = wm_link_append_flag(op); + + /* sanity checks for flag */ + if (scene && scene->id.lib) { + BKE_reportf(op->reports, RPT_WARNING, + "Scene '%s' is linked, instantiation of objects & groups is disabled", scene->id.name + 2); + flag &= ~FILE_GROUP_INSTANCE; + scene = NULL; + } + + /* from here down, no error returns */ + + if (scene && RNA_boolean_get(op->ptr, "autoselect")) { + BKE_scene_base_deselect_all(scene); + } + + /* tag everything, all untagged data can be made local + * its also generally useful to know what is new + * + * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */ + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); + + /* We define our working data... + * Note that here, each item 'uses' one library, and only one. */ + lapp_data = wm_link_append_data_new(flag); + if (totfiles != 0) { + GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); + int lib_idx = 0; + + RNA_BEGIN (op->ptr, itemptr, "files") + { + RNA_string_get(&itemptr, "name", relname); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + if (BLO_library_path_explode(path, libname, &group, &name)) { + if (!group || !name) { + continue; + } + + if (!BLI_ghash_haskey(libraries, libname)) { + BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx)); + lib_idx++; + wm_link_append_data_library_add(lapp_data, libname); + } + } + } + RNA_END; + + RNA_BEGIN (op->ptr, itemptr, "files") + { + RNA_string_get(&itemptr, "name", relname); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + if (BLO_library_path_explode(path, libname, &group, &name)) { + WMLinkAppendDataItem *item; + if (!group || !name) { + printf("skipping %s\n", path); + continue; + } + + lib_idx = GET_INT_FROM_POINTER(BLI_ghash_lookup(libraries, libname)); + + item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); + BLI_BITMAP_ENABLE(item->libraries, lib_idx); + } + } + RNA_END; + + BLI_ghash_free(libraries, MEM_freeN, NULL); + } + else { + WMLinkAppendDataItem *item; + + wm_link_append_data_library_add(lapp_data, libname); + item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); + BLI_BITMAP_ENABLE(item->libraries, 0); + } + + /* XXX We'd need re-entrant locking on Main for this to work... */ + /* BKE_main_lock(bmain); */ + + wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C)); + + /* BKE_main_unlock(bmain); */ + + wm_link_append_data_free(lapp_data); + + /* mark all library linked objects to be updated */ + BKE_main_lib_objects_recalc_all(bmain); + IMB_colormanagement_check_file_config(bmain); + + /* append, rather than linking */ + if ((flag & FILE_LINK) == 0) { + bool set_fake = RNA_boolean_get(op->ptr, "set_fake"); + BKE_library_make_local(bmain, NULL, true, set_fake); + } + + /* important we unset, otherwise these object wont + * link into other scenes from this blend file */ + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + + /* recreate dependency graph to include new objects */ + DAG_scene_relations_rebuild(bmain, scene); + + /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ + GPU_materials_free(); + + /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ + BLI_strncpy(G.lib, root, FILE_MAX); + + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; +} + +static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link) +{ + PropertyRNA *prop; + + /* better not save _any_ settings for this operator */ + /* properties */ + prop = RNA_def_boolean(ot->srna, "link", is_link, + "Link", "Link the objects or datablocks rather than appending"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "autoselect", true, + "Select", "Select new objects"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "active_layer", true, + "Active Layer", "Put new objects on the active layer"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "instance_groups", is_link, + "Instance Groups", "Create Dupli-Group instances for each group"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +void WM_OT_link(wmOperatorType *ot) +{ + ot->name = "Link from Library"; + ot->idname = "WM_OT_link"; + ot->description = "Link from a Library .blend file"; + + ot->invoke = wm_link_append_invoke; + ot->exec = wm_link_append_exec; + ot->poll = wm_link_append_poll; + + ot->flag |= OPTYPE_UNDO; + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + wm_link_append_properties_common(ot, true); +} + +void WM_OT_append(wmOperatorType *ot) +{ + ot->name = "Append from Library"; + ot->idname = "WM_OT_append"; + ot->description = "Append from a Library .blend file"; + + ot->invoke = wm_link_append_invoke; + ot->exec = wm_link_append_exec; + ot->poll = wm_link_append_poll; + + ot->flag |= OPTYPE_UNDO; + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + wm_link_append_properties_common(ot, false); + RNA_def_boolean(ot->srna, "set_fake", false, "Fake User", "Set Fake User for appended items (except Objects and Groups)"); +} diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index b5ad027148a..fdb6db66577 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -52,7 +52,6 @@ #include "DNA_scene_types.h" #include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h" -#include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */ #include "BLT_translation.h" @@ -2073,1025 +2072,6 @@ static void WM_OT_window_duplicate(wmOperatorType *ot) ot->poll = wm_operator_winactive_normal; } -static void WM_OT_save_homefile(wmOperatorType *ot) -{ - ot->name = "Save Startup File"; - ot->idname = "WM_OT_save_homefile"; - ot->description = "Make the current file the default .blend file, includes preferences"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_homefile_write_exec; -} - -static int wm_userpref_autoexec_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) -{ - bPathCompare *path_cmp = MEM_callocN(sizeof(bPathCompare), "bPathCompare"); - BLI_addtail(&U.autoexec_paths, path_cmp); - return OPERATOR_FINISHED; -} - -static void WM_OT_userpref_autoexec_path_add(wmOperatorType *ot) -{ - ot->name = "Add Autoexec Path"; - ot->idname = "WM_OT_userpref_autoexec_path_add"; - ot->description = "Add path to exclude from autoexecution"; - - ot->exec = wm_userpref_autoexec_add_exec; - - ot->flag = OPTYPE_INTERNAL; -} - -static int wm_userpref_autoexec_remove_exec(bContext *UNUSED(C), wmOperator *op) -{ - const int index = RNA_int_get(op->ptr, "index"); - bPathCompare *path_cmp = BLI_findlink(&U.autoexec_paths, index); - if (path_cmp) { - BLI_freelinkN(&U.autoexec_paths, path_cmp); - } - return OPERATOR_FINISHED; -} - -static void WM_OT_userpref_autoexec_path_remove(wmOperatorType *ot) -{ - ot->name = "Remove Autoexec Path"; - ot->idname = "WM_OT_userpref_autoexec_path_remove"; - ot->description = "Remove path to exclude from autoexecution"; - - ot->exec = wm_userpref_autoexec_remove_exec; - - ot->flag = OPTYPE_INTERNAL; - - RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000); -} - -static void WM_OT_save_userpref(wmOperatorType *ot) -{ - ot->name = "Save User Settings"; - ot->idname = "WM_OT_save_userpref"; - ot->description = "Save user preferences separately, overrides startup file preferences"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_userpref_write_exec; -} - -static void WM_OT_read_history(wmOperatorType *ot) -{ - ot->name = "Reload History File"; - ot->idname = "WM_OT_read_history"; - ot->description = "Reloads history and bookmarks"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_history_file_read_exec; - - /* this operator is only used for loading settings from a previous blender install */ - ot->flag = OPTYPE_INTERNAL; -} - -static void WM_OT_read_homefile(wmOperatorType *ot) -{ - PropertyRNA *prop; - ot->name = "Reload Start-Up File"; - ot->idname = "WM_OT_read_homefile"; - ot->description = "Open the default file (doesn't save the current file)"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_homefile_read_exec; - - prop = RNA_def_string_file_path(ot->srna, "filepath", NULL, - FILE_MAX, "File Path", - "Path to an alternative start-up file"); - RNA_def_property_flag(prop, PROP_HIDDEN); - - /* So scripts can use an alternative start-up file without the UI */ - prop = RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", - "Load user interface setup from the .blend file"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - /* omit poll to run in background mode */ -} - -static void WM_OT_read_factory_settings(wmOperatorType *ot) -{ - ot->name = "Load Factory Settings"; - ot->idname = "WM_OT_read_factory_settings"; - ot->description = "Load default file and user preferences"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_homefile_read_exec; - /* omit poll to run in background mode */ -} - -/* *************** open file **************** */ - -/** - * Wrap #WM_file_read, shared by file reading operators. - */ -static bool wm_file_read_opwrap(bContext *C, const char *filepath, ReportList *reports, - const bool autoexec_init) -{ - bool success; - - /* XXX wm in context is not set correctly after WM_file_read -> crash */ - /* do it before for now, but is this correct with multiple windows? */ - WM_event_add_notifier(C, NC_WINDOW, NULL); - - if (autoexec_init) { - WM_file_autoexec_init(filepath); - } - - success = WM_file_read(C, filepath, reports); - - return success; -} - -/* currently fits in a pointer */ -struct FileRuntime { - bool is_untrusted; -}; - -static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - const char *openname = G.main->name; - - if (CTX_wm_window(C) == NULL) { - /* in rare cases this could happen, when trying to invoke in background - * mode on load for example. Don't use poll for this because exec() - * can still run without a window */ - BKE_report(op->reports, RPT_ERROR, "Context window not set"); - return OPERATOR_CANCELLED; - } - - /* if possible, get the name of the most recently used .blend file */ - if (G.recent_files.first) { - struct RecentFile *recent = G.recent_files.first; - openname = recent->filepath; - } - - RNA_string_set(op->ptr, "filepath", openname); - wm_open_init_load_ui(op, true); - wm_open_init_use_scripts(op, true); - op->customdata = NULL; - - WM_event_add_fileselect(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static int wm_open_mainfile_exec(bContext *C, wmOperator *op) -{ - char filepath[FILE_MAX]; - bool success; - - RNA_string_get(op->ptr, "filepath", filepath); - - /* re-use last loaded setting so we can reload a file without changing */ - wm_open_init_load_ui(op, false); - wm_open_init_use_scripts(op, false); - - if (RNA_boolean_get(op->ptr, "load_ui")) - G.fileflags &= ~G_FILE_NO_UI; - else - G.fileflags |= G_FILE_NO_UI; - - if (RNA_boolean_get(op->ptr, "use_scripts")) - G.f |= G_SCRIPT_AUTOEXEC; - else - G.f &= ~G_SCRIPT_AUTOEXEC; - - success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); - - /* for file open also popup for warnings, not only errors */ - BKE_report_print_level_set(op->reports, RPT_WARNING); - - if (success) { - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} - -static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op) -{ - struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts"); - bool is_untrusted = false; - char path[FILE_MAX]; - char *lslash; - - RNA_string_get(op->ptr, "filepath", path); - - /* get the dir */ - lslash = (char *)BLI_last_slash(path); - if (lslash) *(lslash + 1) = '\0'; - - if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) { - if (BKE_autoexec_match(path) == true) { - RNA_property_boolean_set(op->ptr, prop, false); - is_untrusted = true; - } - } - - if (file_info) { - file_info->is_untrusted = is_untrusted; - } - - return is_untrusted; -} - -static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op) -{ - struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; - uiLayout *layout = op->layout; - uiLayout *col = op->layout; - const char *autoexec_text; - - uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, false); - if (file_info->is_untrusted) { - autoexec_text = IFACE_("Trusted Source [Untrusted Path]"); - uiLayoutSetActive(col, false); - uiLayoutSetEnabled(col, false); - } - else { - autoexec_text = IFACE_("Trusted Source"); - } - - uiItemR(col, op->ptr, "use_scripts", 0, autoexec_text, ICON_NONE); -} - -static void WM_OT_open_mainfile(wmOperatorType *ot) -{ - ot->name = "Open Blender File"; - ot->idname = "WM_OT_open_mainfile"; - ot->description = "Open a Blender file"; - - ot->invoke = wm_open_mainfile_invoke; - ot->exec = wm_open_mainfile_exec; - ot->check = wm_open_mainfile_check; - ot->ui = wm_open_mainfile_ui; - /* omit window poll so this can work in background mode */ - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - - RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file"); - RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", - "Allow .blend file to execute scripts automatically, default available from system preferences"); -} - - -/* *************** revert file **************** */ - -static int wm_revert_mainfile_exec(bContext *C, wmOperator *op) -{ - bool success; - - wm_open_init_use_scripts(op, false); - - if (RNA_boolean_get(op->ptr, "use_scripts")) - G.f |= G_SCRIPT_AUTOEXEC; - else - G.f &= ~G_SCRIPT_AUTOEXEC; - - success = wm_file_read_opwrap(C, G.main->name, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); - - if (success) { - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} - -static int wm_revert_mainfile_poll(bContext *UNUSED(C)) -{ - return G.relbase_valid; -} - -static void WM_OT_revert_mainfile(wmOperatorType *ot) -{ - ot->name = "Revert"; - ot->idname = "WM_OT_revert_mainfile"; - ot->description = "Reload the saved file"; - ot->invoke = WM_operator_confirm; - - RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", - "Allow .blend file to execute scripts automatically, default available from system preferences"); - - ot->exec = wm_revert_mainfile_exec; - ot->poll = wm_revert_mainfile_poll; -} - -/* **************** link/append *************** */ - -static int wm_link_append_poll(bContext *C) -{ - if (WM_operator_winactive(C)) { - /* linking changes active object which is pretty useful in general, - * but which totally confuses edit mode (i.e. it becoming not so obvious - * to leave from edit mode and invalid tools in toolbar might be displayed) - * so disable link/append when in edit mode (sergey) */ - if (CTX_data_edit_object(C)) - return 0; - - return 1; - } - - return 0; -} - -static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - if (RNA_struct_property_is_set(op->ptr, "filepath")) { - return WM_operator_call_notest(C, op); - } - else { - /* XXX TODO solve where to get last linked library from */ - if (G.lib[0] != '\0') { - RNA_string_set(op->ptr, "filepath", G.lib); - } - else if (G.relbase_valid) { - char path[FILE_MAX]; - BLI_strncpy(path, G.main->name, sizeof(G.main->name)); - BLI_parent_dir(path); - RNA_string_set(op->ptr, "filepath", path); - } - WM_event_add_fileselect(C, op); - return OPERATOR_RUNNING_MODAL; - } -} - -static short wm_link_append_flag(wmOperator *op) -{ - PropertyRNA *prop; - short flag = 0; - - if (RNA_boolean_get(op->ptr, "autoselect")) - flag |= FILE_AUTOSELECT; - if (RNA_boolean_get(op->ptr, "active_layer")) - flag |= FILE_ACTIVELAY; - if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop)) - flag |= FILE_RELPATH; - if (RNA_boolean_get(op->ptr, "link")) - flag |= FILE_LINK; - if (RNA_boolean_get(op->ptr, "instance_groups")) - flag |= FILE_GROUP_INSTANCE; - - return flag; -} - -typedef struct WMLinkAppendDataItem { - char *name; - BLI_bitmap *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */ - short idcode; - - ID *new_id; - void *customdata; -} WMLinkAppendDataItem; - -typedef struct WMLinkAppendData { - LinkNodePair libraries; - LinkNodePair items; - int num_libraries; - int num_items; - short flag; - - /* Internal 'private' data */ - MemArena *memarena; -} WMLinkAppendData; - -static WMLinkAppendData *wm_link_append_data_new(const int flag) -{ - MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - WMLinkAppendData *lapp_data = BLI_memarena_calloc(ma, sizeof(*lapp_data)); - - lapp_data->flag = flag; - lapp_data->memarena = ma; - - return lapp_data; -} - -static void wm_link_append_data_free(WMLinkAppendData *lapp_data) -{ - BLI_memarena_free(lapp_data->memarena); -} - -/* WARNING! *Never* call wm_link_append_data_library_add() after having added some items! */ - -static void wm_link_append_data_library_add(WMLinkAppendData *lapp_data, const char *libname) -{ - size_t len = strlen(libname) + 1; - char *libpath = BLI_memarena_alloc(lapp_data->memarena, len); - - BLI_strncpy(libpath, libname, len); - BLI_linklist_append_arena(&lapp_data->libraries, libpath, lapp_data->memarena); - lapp_data->num_libraries++; -} - -static WMLinkAppendDataItem *wm_link_append_data_item_add( - WMLinkAppendData *lapp_data, const char *idname, const short idcode, void *customdata) -{ - WMLinkAppendDataItem *item = BLI_memarena_alloc(lapp_data->memarena, sizeof(*item)); - size_t len = strlen(idname) + 1; - - item->name = BLI_memarena_alloc(lapp_data->memarena, len); - BLI_strncpy(item->name, idname, len); - item->idcode = idcode; - item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries); - - item->new_id = NULL; - item->customdata = customdata; - - BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena); - lapp_data->num_items++; - - return item; -} - -static void wm_link_do( - WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d) -{ - Main *mainl; - BlendHandle *bh; - Library *lib; - - const int flag = lapp_data->flag; - - LinkNode *liblink, *itemlink; - int lib_idx, item_idx; - - BLI_assert(lapp_data->num_items && lapp_data->num_libraries); - - for (lib_idx = 0, liblink = lapp_data->libraries.list; liblink; lib_idx++, liblink = liblink->next) { - char *libname = liblink->link; - - bh = BLO_blendhandle_from_file(libname, reports); - - if (bh == NULL) { - /* Unlikely since we just browsed it, but possible - * Error reports will have been made by BLO_blendhandle_from_file() */ - continue; - } - - /* here appending/linking starts */ - mainl = BLO_library_link_begin(bmain, &bh, libname); - lib = mainl->curlib; - BLI_assert(lib); - UNUSED_VARS_NDEBUG(lib); - - if (mainl->versionfile < 250) { - BKE_reportf(reports, RPT_WARNING, - "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will " - "be done! You may want to re-save your lib file with current Blender", - mainl->versionfile, mainl->subversionfile); - } - - /* For each lib file, we try to link all items belonging to that lib, - * and tag those successful to not try to load them again with the other libs. */ - for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) { - WMLinkAppendDataItem *item = itemlink->link; - ID *new_id; - - if (!BLI_BITMAP_TEST(item->libraries, lib_idx)) { - continue; - } - - new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d); - if (new_id) { - /* If the link is sucessful, clear item's libs 'todo' flags. - * This avoids trying to link same item with other libraries to come. */ - BLI_BITMAP_SET_ALL(item->libraries, false, lapp_data->num_libraries); - item->new_id = new_id; - } - } - - BLO_library_link_end(mainl, &bh, flag, scene, v3d); - BLO_blendhandle_close(bh); - } -} - -static int wm_link_append_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - PropertyRNA *prop; - WMLinkAppendData *lapp_data; - char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX]; - char *group, *name; - int totfiles = 0; - short flag; - - RNA_string_get(op->ptr, "filename", relname); - RNA_string_get(op->ptr, "directory", root); - - BLI_join_dirfile(path, sizeof(path), root, relname); - - /* test if we have a valid data */ - if (!BLO_library_path_explode(path, libname, &group, &name)) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': not a library", path); - return OPERATOR_CANCELLED; - } - else if (!group) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); - return OPERATOR_CANCELLED; - } - else if (BLI_path_cmp(bmain->name, libname) == 0) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': cannot use current file as library", path); - return OPERATOR_CANCELLED; - } - - /* check if something is indicated for append/link */ - prop = RNA_struct_find_property(op->ptr, "files"); - if (prop) { - totfiles = RNA_property_collection_length(op->ptr, prop); - if (totfiles == 0) { - if (!name) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); - return OPERATOR_CANCELLED; - } - } - } - else if (!name) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); - return OPERATOR_CANCELLED; - } - - flag = wm_link_append_flag(op); - - /* sanity checks for flag */ - if (scene && scene->id.lib) { - BKE_reportf(op->reports, RPT_WARNING, - "Scene '%s' is linked, instantiation of objects & groups is disabled", scene->id.name + 2); - flag &= ~FILE_GROUP_INSTANCE; - scene = NULL; - } - - /* from here down, no error returns */ - - if (scene && RNA_boolean_get(op->ptr, "autoselect")) { - BKE_scene_base_deselect_all(scene); - } - - /* tag everything, all untagged data can be made local - * its also generally useful to know what is new - * - * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */ - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); - - /* We define our working data... - * Note that here, each item 'uses' one library, and only one. */ - lapp_data = wm_link_append_data_new(flag); - if (totfiles != 0) { - GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); - int lib_idx = 0; - - RNA_BEGIN (op->ptr, itemptr, "files") - { - RNA_string_get(&itemptr, "name", relname); - - BLI_join_dirfile(path, sizeof(path), root, relname); - - if (BLO_library_path_explode(path, libname, &group, &name)) { - if (!group || !name) { - continue; - } - - if (!BLI_ghash_haskey(libraries, libname)) { - BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx)); - lib_idx++; - wm_link_append_data_library_add(lapp_data, libname); - } - } - } - RNA_END; - - RNA_BEGIN (op->ptr, itemptr, "files") - { - RNA_string_get(&itemptr, "name", relname); - - BLI_join_dirfile(path, sizeof(path), root, relname); - - if (BLO_library_path_explode(path, libname, &group, &name)) { - WMLinkAppendDataItem *item; - if (!group || !name) { - printf("skipping %s\n", path); - continue; - } - - lib_idx = GET_INT_FROM_POINTER(BLI_ghash_lookup(libraries, libname)); - - item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); - BLI_BITMAP_ENABLE(item->libraries, lib_idx); - } - } - RNA_END; - - BLI_ghash_free(libraries, MEM_freeN, NULL); - } - else { - WMLinkAppendDataItem *item; - - wm_link_append_data_library_add(lapp_data, libname); - item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); - BLI_BITMAP_ENABLE(item->libraries, 0); - } - - /* XXX We'd need re-entrant locking on Main for this to work... */ - /* BKE_main_lock(bmain); */ - - wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C)); - - /* BKE_main_unlock(bmain); */ - - wm_link_append_data_free(lapp_data); - - /* mark all library linked objects to be updated */ - BKE_main_lib_objects_recalc_all(bmain); - IMB_colormanagement_check_file_config(bmain); - - /* append, rather than linking */ - if ((flag & FILE_LINK) == 0) { - bool set_fake = RNA_boolean_get(op->ptr, "set_fake"); - BKE_library_make_local(bmain, NULL, true, set_fake); - } - - /* important we unset, otherwise these object wont - * link into other scenes from this blend file */ - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); - - /* recreate dependency graph to include new objects */ - DAG_scene_relations_rebuild(bmain, scene); - - /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ - GPU_materials_free(); - - /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ - BLI_strncpy(G.lib, root, FILE_MAX); - - WM_event_add_notifier(C, NC_WINDOW, NULL); - - return OPERATOR_FINISHED; -} - -static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link) -{ - PropertyRNA *prop; - - /* better not save _any_ settings for this operator */ - /* properties */ - prop = RNA_def_boolean(ot->srna, "link", is_link, - "Link", "Link the objects or datablocks rather than appending"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); - prop = RNA_def_boolean(ot->srna, "autoselect", true, - "Select", "Select new objects"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "active_layer", true, - "Active Layer", "Put new objects on the active layer"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "instance_groups", is_link, - "Instance Groups", "Create Dupli-Group instances for each group"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} - -static void WM_OT_link(wmOperatorType *ot) -{ - ot->name = "Link from Library"; - ot->idname = "WM_OT_link"; - ot->description = "Link from a Library .blend file"; - - ot->invoke = wm_link_append_invoke; - ot->exec = wm_link_append_exec; - ot->poll = wm_link_append_poll; - - ot->flag |= OPTYPE_UNDO; - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, - WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES, - FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - - wm_link_append_properties_common(ot, true); -} - -static void WM_OT_append(wmOperatorType *ot) -{ - ot->name = "Append from Library"; - ot->idname = "WM_OT_append"; - ot->description = "Append from a Library .blend file"; - - ot->invoke = wm_link_append_invoke; - ot->exec = wm_link_append_exec; - ot->poll = wm_link_append_poll; - - ot->flag |= OPTYPE_UNDO; - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, - WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES, - FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - - wm_link_append_properties_common(ot, false); - RNA_def_boolean(ot->srna, "set_fake", false, "Fake User", "Set Fake User for appended items (except Objects and Groups)"); -} - -/* *************** recover last session **************** */ - -void WM_recover_last_session(bContext *C, ReportList *reports) -{ - char filepath[FILE_MAX]; - - BLI_make_file_string("/", filepath, BKE_tempdir_base(), BLENDER_QUIT_FILE); - /* if reports==NULL, it's called directly without operator, we add a quick check here */ - if (reports || BLI_exists(filepath)) { - G.fileflags |= G_FILE_RECOVER; - - wm_file_read_opwrap(C, filepath, reports, true); - - G.fileflags &= ~G_FILE_RECOVER; - - /* XXX bad global... fixme */ - if (G.main->name[0]) - G.file_loaded = 1; /* prevents splash to show */ - else { - G.relbase_valid = 0; - G.save_over = 0; /* start with save preference untitled.blend */ - } - - } -} - -static int wm_recover_last_session_exec(bContext *C, wmOperator *op) -{ - WM_recover_last_session(C, op->reports); - return OPERATOR_FINISHED; -} - -static void WM_OT_recover_last_session(wmOperatorType *ot) -{ - ot->name = "Recover Last Session"; - ot->idname = "WM_OT_recover_last_session"; - ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")"; - ot->invoke = WM_operator_confirm; - - ot->exec = wm_recover_last_session_exec; -} - -/* *************** recover auto save **************** */ - -static int wm_recover_auto_save_exec(bContext *C, wmOperator *op) -{ - char filepath[FILE_MAX]; - bool success; - - RNA_string_get(op->ptr, "filepath", filepath); - - G.fileflags |= G_FILE_RECOVER; - - success = wm_file_read_opwrap(C, filepath, op->reports, true); - - G.fileflags &= ~G_FILE_RECOVER; - - if (success) { - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} - -static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - char filename[FILE_MAX]; - - wm_autosave_location(filename); - RNA_string_set(op->ptr, "filepath", filename); - WM_event_add_fileselect(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static void WM_OT_recover_auto_save(wmOperatorType *ot) -{ - ot->name = "Recover Auto Save"; - ot->idname = "WM_OT_recover_auto_save"; - ot->description = "Open an automatically saved file to recover it"; - - ot->exec = wm_recover_auto_save_exec; - ot->invoke = wm_recover_auto_save_invoke; - - WM_operator_properties_filesel( - ot, FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH, FILE_LONGDISPLAY, FILE_SORT_TIME); -} - -/* *************** save file as **************** */ - -static void wm_filepath_default(char *filepath) -{ - if (G.save_over == false) { - BLI_ensure_filename(filepath, FILE_MAX, "untitled.blend"); - } -} - -static void save_set_compress(wmOperator *op) -{ - PropertyRNA *prop; - - prop = RNA_struct_find_property(op->ptr, "compress"); - if (!RNA_property_is_set(op->ptr, prop)) { - if (G.save_over) { /* keep flag for existing file */ - RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0); - } - else { /* use userdef for new file */ - RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0); - } - } -} - -static void save_set_filepath(wmOperator *op) -{ - PropertyRNA *prop; - char name[FILE_MAX]; - - prop = RNA_struct_find_property(op->ptr, "filepath"); - if (!RNA_property_is_set(op->ptr, prop)) { - /* if not saved before, get the name of the most recently used .blend file */ - if (G.main->name[0] == 0 && G.recent_files.first) { - struct RecentFile *recent = G.recent_files.first; - BLI_strncpy(name, recent->filepath, FILE_MAX); - } - else { - BLI_strncpy(name, G.main->name, FILE_MAX); - } - - wm_filepath_default(name); - RNA_property_string_set(op->ptr, prop, name); - } -} - -static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - - save_set_compress(op); - save_set_filepath(op); - - WM_event_add_fileselect(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -/* function used for WM_OT_save_mainfile too */ -static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) -{ - char path[FILE_MAX]; - int fileflags; - - save_set_compress(op); - - if (RNA_struct_property_is_set(op->ptr, "filepath")) { - RNA_string_get(op->ptr, "filepath", path); - } - else { - BLI_strncpy(path, G.main->name, FILE_MAX); - wm_filepath_default(path); - } - - fileflags = G.fileflags & ~G_FILE_USERPREFS; - - /* set compression flag */ - BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "compress"), - G_FILE_COMPRESS); - BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "relative_remap"), - G_FILE_RELATIVE_REMAP); - BKE_BIT_TEST_SET(fileflags, - (RNA_struct_property_is_set(op->ptr, "copy") && - RNA_boolean_get(op->ptr, "copy")), - G_FILE_SAVE_COPY); - -#ifdef USE_BMESH_SAVE_AS_COMPAT - BKE_BIT_TEST_SET(fileflags, - (RNA_struct_find_property(op->ptr, "use_mesh_compat") && - RNA_boolean_get(op->ptr, "use_mesh_compat")), - G_FILE_MESH_COMPAT); -#else -# error "don't remove by accident" -#endif - - if (wm_file_write(C, path, fileflags, op->reports) != 0) - return OPERATOR_CANCELLED; - - WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL); - - return OPERATOR_FINISHED; -} - -/* function used for WM_OT_save_mainfile too */ -static bool blend_save_check(bContext *UNUSED(C), wmOperator *op) -{ - char filepath[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", filepath); - if (!BLO_has_bfile_extension(filepath)) { - /* some users would prefer BLI_replace_extension(), - * we keep getting nitpicking bug reports about this - campbell */ - BLI_ensure_extension(filepath, FILE_MAX, ".blend"); - RNA_string_set(op->ptr, "filepath", filepath); - return true; - } - return false; -} - -static void WM_OT_save_as_mainfile(wmOperatorType *ot) -{ - PropertyRNA *prop; - - ot->name = "Save As Blender File"; - ot->idname = "WM_OT_save_as_mainfile"; - ot->description = "Save the current file in the desired location"; - - ot->invoke = wm_save_as_mainfile_invoke; - ot->exec = wm_save_as_mainfile_exec; - ot->check = blend_save_check; - /* omit window poll so this can work in background mode */ - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); - RNA_def_boolean(ot->srna, "relative_remap", true, "Remap Relative", - "Remap relative paths when saving in a different directory"); - prop = RNA_def_boolean(ot->srna, "copy", false, "Save Copy", - "Save a copy of the actual working state but does not make saved file active"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -#ifdef USE_BMESH_SAVE_AS_COMPAT - RNA_def_boolean(ot->srna, "use_mesh_compat", false, "Legacy Mesh Format", - "Save using legacy mesh format (no ngons) - WARNING: only saves tris and quads, other ngons will " - "be lost (no implicit triangulation)"); -#endif -} - -/* *************** save file directly ******** */ - -static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - int ret; - - /* cancel if no active window */ - if (CTX_wm_window(C) == NULL) - return OPERATOR_CANCELLED; - - save_set_compress(op); - save_set_filepath(op); - - /* if we're saving for the first time and prefer relative paths - any existing paths will be absolute, - * enable the option to remap paths to avoid confusion [#37240] */ - if ((G.relbase_valid == false) && (U.flag & USER_RELPATHS)) { - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_boolean_set(op->ptr, prop, true); - } - } - - if (G.save_over) { - char path[FILE_MAX]; - - RNA_string_get(op->ptr, "filepath", path); - if (BLI_exists(path)) { - ret = WM_operator_confirm_message_ex(C, op, IFACE_("Save Over?"), ICON_QUESTION, path); - } - else { - ret = wm_save_as_mainfile_exec(C, op); - } - } - else { - WM_event_add_fileselect(C, op); - ret = OPERATOR_RUNNING_MODAL; - } - - return ret; -} - -static void WM_OT_save_mainfile(wmOperatorType *ot) -{ - ot->name = "Save Blender File"; - ot->idname = "WM_OT_save_mainfile"; - ot->description = "Save the current Blender file"; - - ot->invoke = wm_save_mainfile_invoke; - ot->exec = wm_save_as_mainfile_exec; - ot->check = blend_save_check; - /* omit window poll so this can work in background mode */ - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); - RNA_def_boolean(ot->srna, "relative_remap", false, "Remap Relative", - "Remap relative paths when saving in a different directory"); -} - static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot) { ot->name = "Toggle Window Fullscreen"; diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 4b35f662a99..2eae9cdb012 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -31,15 +31,33 @@ #ifndef __WM_FILES_H__ #define __WM_FILES_H__ +struct wmOperatorType; + +/* wm_files.c */ void wm_history_file_read(void); -int wm_history_file_read_exec(bContext *C, wmOperator *op); -int wm_file_write(struct bContext *C, const char *target, int fileflags, struct ReportList *reports); -int wm_homefile_read_exec(struct bContext *C, struct wmOperator *op); int wm_homefile_read(struct bContext *C, struct ReportList *reports, bool from_memory, const char *filepath); -int wm_homefile_write_exec(struct bContext *C, struct wmOperator *op); -int wm_userpref_write_exec(struct bContext *C, struct wmOperator *op); void wm_file_read_report(bContext *C); +void WM_OT_save_homefile(struct wmOperatorType *ot); +void WM_OT_userpref_autoexec_path_add(struct wmOperatorType *ot); +void WM_OT_userpref_autoexec_path_remove(struct wmOperatorType *ot); +void WM_OT_save_userpref(struct wmOperatorType *ot); +void WM_OT_read_history(struct wmOperatorType *ot); +void WM_OT_read_homefile(struct wmOperatorType *ot); +void WM_OT_read_factory_settings(struct wmOperatorType *ot); + +void WM_OT_open_mainfile(struct wmOperatorType *ot); + +void WM_OT_revert_mainfile(struct wmOperatorType *ot); +void WM_OT_recover_last_session(struct wmOperatorType *ot); +void WM_OT_recover_auto_save(struct wmOperatorType *ot); + +void WM_OT_save_as_mainfile(struct wmOperatorType *ot); +void WM_OT_save_mainfile(struct wmOperatorType *ot); + +/* wm_files_link.c */ +void WM_OT_link(struct wmOperatorType *ot); +void WM_OT_append(struct wmOperatorType *ot); #endif /* __WM_FILES_H__ */ |