Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <mail@jlucke.com>2019-05-17 18:31:26 +0300
committerJacques Lucke <mail@jlucke.com>2019-05-17 18:43:36 +0300
commit5ce3f69da438bf2bd58d5039f8a4165f31262e1e (patch)
tree4cc3347c85afe33a7d1a7fa761046eeafd9cdd92 /source/blender/windowmanager
parent56cdb0cf15ac83852f8db5ca3f816dbed67add9c (diff)
UI: File Close Dialog
This adds a new dialog that is shown whenever a file is closed. So, either when a new file is opened, or when Blender quits. The dialog allows to save unsaved changes. Furthermore it also allows saving images that have been modified in Blender, but are not saved yet. Known limitations: * Images that have no file path and have not been packed before, are not saved. * On MacOS the old dialog is shown when Blender quits. Reviewers: brecht, billreynish Differential Revision: https://developer.blender.org/D4860
Diffstat (limited to 'source/blender/windowmanager')
-rw-r--r--source/blender/windowmanager/WM_api.h4
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c11
-rw-r--r--source/blender/windowmanager/intern/wm_files.c275
-rw-r--r--source/blender/windowmanager/intern/wm_window.c162
-rw-r--r--source/blender/windowmanager/wm_files.h11
5 files changed, 297 insertions, 166 deletions
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index bd02a1e13c1..70f986732ad 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -401,6 +401,10 @@ int WM_operator_name_call(struct bContext *C,
const char *opstring,
short context,
struct PointerRNA *properties);
+int WM_operator_name_call_with_properties(struct bContext *C,
+ const char *opstring,
+ short context,
+ struct IDProperty *properties);
int WM_operator_call_py(struct bContext *C,
struct wmOperatorType *ot,
short context,
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 8ad23af446d..6683085e6d3 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -1678,6 +1678,17 @@ int WM_operator_name_call(bContext *C, const char *opstring, short context, Poin
return 0;
}
+int WM_operator_name_call_with_properties(struct bContext *C,
+ const char *opstring,
+ short context,
+ struct IDProperty *properties)
+{
+ PointerRNA props_ptr;
+ wmOperatorType *ot = WM_operatortype_find(opstring, false);
+ RNA_pointer_create(NULL, ot->srna, properties, &props_ptr);
+ return WM_operator_name_call_ptr(C, ot, context, &props_ptr);
+}
+
/**
* Call an existent menu. The menu can be created in C or Python.
*/
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 329763a17fd..fabdd71fc5c 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -76,6 +76,7 @@
#include "BKE_blender_undo.h"
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_idprop.h"
#include "BKE_main.h"
#include "BKE_packedFile.h"
#include "BKE_report.h"
@@ -98,6 +99,7 @@
#include "ED_datafiles.h"
#include "ED_fileselect.h"
+#include "ED_image.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "ED_util.h"
@@ -1922,11 +1924,27 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data)
+{
+ WM_operator_name_call_with_properties(
+ C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data);
+}
+
+static void wm_free_operator_properties_callback(void *user_data)
+{
+ IDProperty *properties = (IDProperty *)user_data;
+ IDP_FreeProperty(properties);
+}
+
static int wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- wmWindowManager *wm = CTX_wm_manager(C);
- if (U.uiflag & USER_SAVE_PROMPT && !wm->file_saved) {
- return WM_operator_confirm_message(C, op, "Changes in current file will be lost. Continue?");
+ if (U.uiflag & USER_SAVE_PROMPT && wm_file_or_image_is_modified(C)) {
+ GenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
+ callback->exec = wm_homefile_read_after_dialog_callback;
+ callback->user_data = IDP_CopyProperty(op->properties);
+ callback->free_user_data = wm_free_operator_properties_callback;
+ wm_close_file_dialog(C, callback);
+ return OPERATOR_INTERFACE;
}
else {
return wm_homefile_read_exec(C, op);
@@ -2071,6 +2089,12 @@ enum {
static int wm_open_mainfile_dispatch(bContext *C, wmOperator *op);
+static void wm_open_mainfile_after_dialog_callback(bContext *C, void *user_data)
+{
+ WM_operator_name_call_with_properties(
+ C, "WM_OT_open_mainfile", WM_OP_INVOKE_DEFAULT, (IDProperty *)user_data);
+}
+
static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op)
{
if (RNA_boolean_get(op->ptr, "display_file_selector")) {
@@ -2080,14 +2104,13 @@ static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op)
set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN);
}
- wmWindowManager *wm = CTX_wm_manager(C);
- if (U.uiflag & USER_SAVE_PROMPT && !wm->file_saved) {
- return WM_operator_confirm_message_ex(C,
- op,
- "Warning",
- ICON_INFO,
- "Changes in current file will be lost. Continue?",
- WM_OP_INVOKE_DEFAULT);
+ if (U.uiflag & USER_SAVE_PROMPT && wm_file_or_image_is_modified(C)) {
+ GenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
+ callback->exec = wm_open_mainfile_after_dialog_callback;
+ callback->user_data = IDP_CopyProperty(op->properties);
+ callback->free_user_data = wm_free_operator_properties_callback;
+ wm_close_file_dialog(C, callback);
+ return OPERATOR_INTERFACE;
}
else {
return wm_open_mainfile_dispatch(C, op);
@@ -2819,4 +2842,234 @@ void wm_test_autorun_warning(bContext *C)
}
}
+/* Close File Dialog
+ *************************************/
+
+static char save_images_when_file_is_closed = true;
+
+static void wm_block_file_close_cancel(bContext *C, void *arg_block, void *UNUSED(arg_data))
+{
+ wmWindow *win = CTX_wm_window(C);
+ UI_popup_block_close(C, win, arg_block);
+}
+
+static void wm_block_file_close_discard(bContext *C, void *arg_block, void *arg_data)
+{
+ GenericCallback *callback = wm_generic_callback_steal((GenericCallback *)arg_data);
+
+ /* Close the popup before executing the callback. Otherwise
+ * the popup might be closed by the callback, which will lead
+ * to a crash. */
+ wmWindow *win = CTX_wm_window(C);
+ UI_popup_block_close(C, win, arg_block);
+
+ callback->exec(C, callback->user_data);
+ wm_generic_callback_free(callback);
+}
+
+static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_data)
+{
+ GenericCallback *callback = wm_generic_callback_steal((GenericCallback *)arg_data);
+ bool execute_callback = true;
+
+ wmWindow *win = CTX_wm_window(C);
+ UI_popup_block_close(C, win, arg_block);
+
+ if (save_images_when_file_is_closed) {
+ ReportList *reports = CTX_wm_reports(C);
+ if (!ED_image_save_all_modified(C, reports)) {
+ execute_callback = false;
+ }
+ WM_report_banner_show();
+ }
+
+ Main *bmain = CTX_data_main(C);
+ bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0';
+
+ if (file_has_been_saved_before) {
+ WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL);
+ }
+ else {
+ WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, NULL);
+ execute_callback = false;
+ }
+
+ if (execute_callback) {
+ callback->exec(C, callback->user_data);
+ }
+ wm_generic_callback_free(callback);
+}
+
+static uiBlock *block_create__close_file_dialog(struct bContext *C, struct ARegion *ar, void *arg1)
+{
+ GenericCallback *post_action = (GenericCallback *)arg1;
+ Main *bmain = CTX_data_main(C);
+
+ uiStyle *style = UI_style_get();
+ uiBlock *block = UI_block_begin(C, ar, "file_close_popup", UI_EMBOSS);
+
+ UI_block_flag_enable(
+ block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT);
+ UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
+ UI_block_emboss_set(block, UI_EMBOSS);
+
+ uiLayout *layout = UI_block_layout(block,
+ UI_LAYOUT_VERTICAL,
+ UI_LAYOUT_PANEL,
+ 10,
+ 2,
+ U.widget_unit * 24,
+ U.widget_unit * 6,
+ 0,
+ style);
+
+ bool blend_file_is_saved = BKE_main_blendfile_path(bmain)[0] != '\0';
+ if (blend_file_is_saved) {
+ uiItemL(layout, "This file has unsaved changes.", ICON_NONE);
+ }
+ else {
+ uiItemL(layout, "This file has not been saved yet.", ICON_NONE);
+ }
+
+ ReportList reports;
+ BKE_reports_init(&reports, RPT_STORE);
+ uint modified_images_count = ED_image_save_all_modified_info(C, &reports);
+
+ if (modified_images_count > 0) {
+ char message[64];
+ BLI_snprintf(message,
+ sizeof(message),
+ (modified_images_count == 1) ? "Save %u modified image" :
+ "Save %u modified images",
+ modified_images_count);
+ uiDefButBitC(block,
+ UI_BTYPE_CHECKBOX,
+ 1,
+ 0,
+ message,
+ 0,
+ 0,
+ 0,
+ UI_UNIT_Y,
+ &save_images_when_file_is_closed,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+
+ LISTBASE_FOREACH (Report *, report, &reports.list) {
+ uiItemL(layout, report->message, ICON_ERROR);
+ }
+ }
+
+ BKE_reports_clear(&reports);
+
+ uiItemL(layout, "", ICON_NONE);
+
+ uiBut *but;
+ uiLayout *split = uiLayoutSplit(layout, 0.0f, true);
+ uiLayout *col = uiLayoutColumn(split, false);
+
+ but = uiDefIconTextBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_SCREEN_BACK,
+ IFACE_("Cancel"),
+ 0,
+ 0,
+ 0,
+ UI_UNIT_Y,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+ UI_but_func_set(but, wm_block_file_close_cancel, block, post_action);
+
+ /* empty space between buttons */
+ col = uiLayoutColumn(split, false);
+ uiItemS(col);
+
+ col = uiLayoutColumn(split, true);
+ but = uiDefIconTextBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_CANCEL,
+ IFACE_("Discard Changes"),
+ 0,
+ 0,
+ 50,
+ UI_UNIT_Y,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+ UI_but_func_set(but, wm_block_file_close_discard, block, post_action);
+
+ col = uiLayoutColumn(split, true);
+ but = uiDefIconTextBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_CANCEL,
+ IFACE_("Save"),
+ 0,
+ 0,
+ 50,
+ UI_UNIT_Y,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+ UI_but_func_set(but, wm_block_file_close_save, block, post_action);
+ UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT);
+
+ UI_block_bounds_set_centered(block, 10);
+ return block;
+}
+
+static void free_post_file_close_action(void *arg)
+{
+ GenericCallback *action = (GenericCallback *)arg;
+ wm_generic_callback_free(action);
+}
+
+void wm_close_file_dialog(bContext *C, GenericCallback *post_action)
+{
+ UI_popup_block_invoke(
+ C, block_create__close_file_dialog, post_action, free_post_file_close_action);
+}
+
+bool wm_file_or_image_is_modified(const bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ return !wm->file_saved || ED_image_should_save_modified(C);
+}
+
+void wm_generic_callback_free(GenericCallback *callback)
+{
+ if (callback->free_user_data) {
+ callback->free_user_data(callback->user_data);
+ }
+ MEM_freeN(callback);
+}
+
+static void do_nothing(bContext *UNUSED(C), void *UNUSED(user_data))
+{
+}
+
+GenericCallback *wm_generic_callback_steal(GenericCallback *callback)
+{
+ GenericCallback *new_callback = MEM_dupallocN(callback);
+ callback->exec = do_nothing;
+ callback->free_user_data = NULL;
+ callback->user_data = NULL;
+ return new_callback;
+}
+
/** \} */
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 409befd8c47..c7168673f26 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -61,6 +61,7 @@
#include "WM_types.h"
#include "wm.h"
#include "wm_draw.h"
+#include "wm_files.h"
#include "wm_window.h"
#include "wm_event_system.h"
@@ -356,150 +357,9 @@ wmWindow *wm_window_copy_test(bContext *C,
/** \name Quit Confirmation Dialog
* \{ */
-/** Cancel quitting and close the dialog */
-static void wm_block_confirm_quit_cancel(bContext *C, void *arg_block, void *UNUSED(arg))
+static void wm_save_file_on_quit_dialog_callback(bContext *C, void *UNUSED(user_data))
{
- wmWindow *win = CTX_wm_window(C);
- UI_popup_block_close(C, win, arg_block);
-}
-
-/** Discard the file changes and quit */
-ATTR_NORETURN
-static void wm_block_confirm_quit_discard(bContext *C, void *arg_block, void *UNUSED(arg))
-{
- wmWindow *win = CTX_wm_window(C);
- UI_popup_block_close(C, win, arg_block);
- WM_exit(C);
-}
-
-/* Save changes and quit */
-static void wm_block_confirm_quit_save(bContext *C, void *arg_block, void *UNUSED(arg))
-{
- PointerRNA props_ptr;
- wmWindow *win = CTX_wm_window(C);
-
- UI_popup_block_close(C, win, arg_block);
-
- wmOperatorType *ot = WM_operatortype_find("WM_OT_save_mainfile", false);
-
- WM_operator_properties_create_ptr(&props_ptr, ot);
- RNA_boolean_set(&props_ptr, "exit", true);
- /* No need for second confirmation popup. */
- RNA_boolean_set(&props_ptr, "check_existing", false);
- WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
- WM_operator_properties_free(&props_ptr);
-}
-
-/* Build the confirm dialog UI */
-static uiBlock *block_create_confirm_quit(struct bContext *C,
- struct ARegion *ar,
- void *UNUSED(arg1))
-{
- Main *bmain = CTX_data_main(C);
-
- uiStyle *style = UI_style_get();
- uiBlock *block = UI_block_begin(C, ar, "confirm_quit_popup", UI_EMBOSS);
-
- UI_block_flag_enable(
- block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT);
- UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
- UI_block_emboss_set(block, UI_EMBOSS);
-
- uiLayout *layout = UI_block_layout(block,
- UI_LAYOUT_VERTICAL,
- UI_LAYOUT_PANEL,
- 10,
- 2,
- U.widget_unit * 24,
- U.widget_unit * 6,
- 0,
- style);
-
- /* Text and some vertical space */
- {
- char *message;
- if (BKE_main_blendfile_path(bmain)[0] == '\0') {
- message = BLI_strdup(IFACE_("This file has not been saved yet. Save before closing?"));
- }
- else {
- const char *basename = BLI_path_basename(BKE_main_blendfile_path(bmain));
- message = BLI_sprintfN(IFACE_("Save changes to \"%s\" before closing?"), basename);
- }
- uiItemL(layout, message, ICON_ERROR);
- MEM_freeN(message);
- }
-
- uiItemS(layout);
- uiItemS(layout);
-
- /* Buttons */
- uiBut *but;
-
- uiLayout *split = uiLayoutSplit(layout, 0.0f, true);
-
- uiLayout *col = uiLayoutColumn(split, false);
-
- but = uiDefIconTextBut(block,
- UI_BTYPE_BUT,
- 0,
- ICON_SCREEN_BACK,
- IFACE_("Cancel"),
- 0,
- 0,
- 0,
- UI_UNIT_Y,
- NULL,
- 0,
- 0,
- 0,
- 0,
- TIP_("Do not quit"));
- UI_but_func_set(but, wm_block_confirm_quit_cancel, block, NULL);
-
- /* empty space between buttons */
- col = uiLayoutColumn(split, false);
- uiItemS(col);
-
- col = uiLayoutColumn(split, 1);
- but = uiDefIconTextBut(block,
- UI_BTYPE_BUT,
- 0,
- ICON_CANCEL,
- IFACE_("Discard Changes"),
- 0,
- 0,
- 50,
- UI_UNIT_Y,
- NULL,
- 0,
- 0,
- 0,
- 0,
- TIP_("Discard changes and quit"));
- UI_but_func_set(but, wm_block_confirm_quit_discard, block, NULL);
-
- col = uiLayoutColumn(split, 1);
- but = uiDefIconTextBut(block,
- UI_BTYPE_BUT,
- 0,
- ICON_FILE_TICK,
- IFACE_("Save & Quit"),
- 0,
- 0,
- 50,
- UI_UNIT_Y,
- NULL,
- 0,
- 0,
- 0,
- 0,
- TIP_("Save and quit"));
- UI_but_func_set(but, wm_block_confirm_quit_save, block, NULL);
- UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT);
-
- UI_block_bounds_set_centered(block, 10);
-
- return block;
+ wm_exit_schedule_delayed(C);
}
/**
@@ -508,16 +368,9 @@ static uiBlock *block_create_confirm_quit(struct bContext *C,
*/
static void wm_confirm_quit(bContext *C)
{
- wmWindow *win = CTX_wm_window(C);
-
- if (GHOST_SupportsNativeDialogs() == 0) {
- if (!UI_popup_block_name_exists(C, "confirm_quit_popup")) {
- UI_popup_block_invoke(C, block_create_confirm_quit, NULL, NULL);
- }
- }
- else if (GHOST_confirmQuit(win->ghostwin)) {
- wm_exit_schedule_delayed(C);
- }
+ GenericCallback *action = MEM_callocN(sizeof(*action), __func__);
+ action->exec = wm_save_file_on_quit_dialog_callback;
+ wm_close_file_dialog(C, action);
}
/**
@@ -529,7 +382,6 @@ static void wm_confirm_quit(bContext *C)
*/
void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win)
{
- wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win_ctx = CTX_wm_window(C);
/* The popup will be displayed in the context window which may not be set
@@ -537,7 +389,7 @@ void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win)
CTX_wm_window_set(C, win);
if (U.uiflag & USER_SAVE_PROMPT) {
- if (!wm->file_saved && !G.background) {
+ if (wm_file_or_image_is_modified(C) && !G.background) {
wm_confirm_quit(C);
}
else {
diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h
index 24209504a07..8b3f1ead215 100644
--- a/source/blender/windowmanager/wm_files.h
+++ b/source/blender/windowmanager/wm_files.h
@@ -40,6 +40,17 @@ void wm_homefile_read(struct bContext *C,
bool *r_is_factory_startup);
void wm_file_read_report(bContext *C, struct Main *bmain);
+typedef struct GenericCallback {
+ void (*exec)(bContext *C, void *user_data);
+ void *user_data;
+ void (*free_user_data)(void *user_data);
+} GenericCallback;
+
+GenericCallback *wm_generic_callback_steal(GenericCallback *callback);
+void wm_generic_callback_free(GenericCallback *callback);
+void wm_close_file_dialog(bContext *C, GenericCallback *post_action);
+bool wm_file_or_image_is_modified(const struct 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);