diff options
author | Gaia Clary <gaia.clary@machinimatrix.org> | 2019-05-22 23:35:40 +0300 |
---|---|---|
committer | Gaia Clary <gaia.clary@machinimatrix.org> | 2019-05-22 23:35:40 +0300 |
commit | 4755c2345854dd97234de2438e00008311537dde (patch) | |
tree | 3f7f5a029c50fda39630bdb6ef518f925df01f18 /source/blender/windowmanager | |
parent | 43e6bb85cee0802887eae9489a2bd73836daf41d (diff) | |
parent | b471e48c305b6fdee69a862b50547a59dd368c4d (diff) |
Merge branch 'master' into collada
Diffstat (limited to 'source/blender/windowmanager')
23 files changed, 890 insertions, 357 deletions
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 7e53c652ab5..64f506f03a8 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -74,6 +74,7 @@ set(SRC intern/wm_toolsystem.c intern/wm_tooltip.c intern/wm_uilist_type.c + intern/wm_utils.c intern/wm_window.c gizmo/intern/wm_gizmo.c gizmo/intern/wm_gizmo_group.c diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index f1037dadf85..4a7953d7a24 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -60,6 +60,7 @@ struct wmEvent; struct wmEventHandler; struct wmEventHandler_Keymap; struct wmEventHandler_UI; +struct wmGenericUserData; struct wmGesture; struct wmJob; struct wmMsgSubscribeKey; @@ -373,7 +374,8 @@ int WM_operator_confirm_message_ex(struct bContext *C, struct wmOperator *op, const char *title, const int icon, - const char *message); + const char *message, + const short opcontext); int WM_operator_confirm_message(struct bContext *C, struct wmOperator *op, const char *message); /* operator api */ @@ -400,6 +402,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, @@ -660,6 +666,7 @@ enum { WM_JOB_TYPE_SHADER_COMPILATION, WM_JOB_TYPE_STUDIOLIGHT, WM_JOB_TYPE_LIGHT_BAKE, + WM_JOB_TYPE_FSMENU_BOOKMARK_VALIDATE, /* add as needed, screencast, seq proxy build * if having hard coded values is a problem */ }; @@ -789,6 +796,12 @@ void WM_tooltip_init(struct bContext *C, struct wmWindow *win); void WM_tooltip_refresh(struct bContext *C, struct wmWindow *win); double WM_tooltip_time_closed(void); +/* wm_utils.c */ +struct wmGenericCallback *WM_generic_callback_steal(struct wmGenericCallback *callback); +void WM_generic_callback_free(struct wmGenericCallback *callback); + +void WM_generic_user_data_free(struct wmGenericUserData *user_data); + #ifdef __cplusplus } #endif diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 154c4837a68..eddea3b2062 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -120,6 +120,19 @@ struct wmWindowManager; /* Include external gizmo API's */ #include "gizmo/WM_gizmo_api.h" +typedef struct wmGenericUserData { + void *data; + /** When NULL, use #MEM_freeN. */ + void (*free_fn)(void *data); + bool use_free; +} wmGenericUserData; + +typedef struct wmGenericCallback { + void (*exec)(struct bContext *C, void *user_data); + void *user_data; + void (*free_user_data)(void *user_data); +} wmGenericCallback; + /* ************** wmOperatorType ************************ */ /* flag */ @@ -442,8 +455,7 @@ typedef struct wmGesture { /* customdata for straight line is a recti: (xmin,ymin) is start, (xmax, ymax) is end */ /* free pointer to use for operator allocs (if set, its freed on exit)*/ - void *userdata; - bool userdata_free; + wmGenericUserData user_data; } wmGesture; /* ************** wmEvent ************************ */ @@ -605,7 +617,7 @@ typedef struct wmOperatorType { * that the operator might still fail to execute even if this return true */ bool (*poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT; - /* Use to check of properties should be displayed in auto-generated UI. + /* Use to check if properties should be displayed in auto-generated UI. * Use 'check' callback to enforce refreshing. */ bool (*poll_property)(const struct bContext *C, struct wmOperator *op, diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index 1c298f378a4..7d38194db1b 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -198,6 +198,10 @@ struct wmGizmo { /** For single click button gizmos, use a different part as a fallback, -1 when unused. */ int drag_part; + /** Distance to bias this gizmo above others when picking + * (in worldspace, scaled by the gizmo scale - when used). */ + float select_bias; + /** * Transformation of the gizmo in 2d or 3d space. * - Matrix axis are expected to be unit length (scale is applied after). diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c index e2e5096ef99..b05865aa7bb 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c @@ -754,7 +754,6 @@ void WM_gizmo_properties_free(PointerRNA *ptr) if (properties) { IDP_FreeProperty(properties); - MEM_freeN(properties); ptr->data = NULL; /* just in case */ } } diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 31c5e1fb94c..9f36af8b616 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -25,8 +25,10 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_math_bits.h" #include "BLI_rect.h" #include "BLI_ghash.h" +#include "BLI_array.h" #include "BKE_context.h" #include "BKE_global.h" @@ -470,7 +472,8 @@ void WM_gizmomap_draw(wmGizmoMap *gzmap, static void gizmo_draw_select_3D_loop(const bContext *C, ListBase *visible_gizmos, - const wmGizmo *gz_stop) + const wmGizmo *gz_stop, + bool *r_use_select_bias) { int select_id = 0; wmGizmo *gz; @@ -511,6 +514,10 @@ static void gizmo_draw_select_3D_loop(const bContext *C, is_depth_skip_prev = is_depth_skip; } + if (gz->select_bias != 0.0) { + *r_use_select_bias = true; + } + /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected gizmo part id */ gz->type->draw_select(C, gz, select_id << 8); @@ -543,24 +550,68 @@ static int gizmo_find_intersected_3d_intern(ListBase *visible_gizmos, ED_view3d_draw_setup_view( CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, &rect); + bool use_select_bias = false; + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0); /* do the drawing */ - gizmo_draw_select_3D_loop(C, visible_gizmos, gz_stop); + gizmo_draw_select_3D_loop(C, visible_gizmos, gz_stop, &use_select_bias); hits = GPU_select_end(); if (hits > 0) { GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits); - gizmo_draw_select_3D_loop(C, visible_gizmos, gz_stop); + gizmo_draw_select_3D_loop(C, visible_gizmos, gz_stop, &use_select_bias); GPU_select_end(); } ED_view3d_draw_setup_view( CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, NULL); - const GLuint *hit_near = GPU_select_buffer_near(buffer, hits); - - return hit_near ? hit_near[3] : -1; + if (use_select_bias && (hits > 1)) { + wmGizmo **gizmo_table = NULL; + BLI_array_staticdeclare(gizmo_table, 1024); + for (LinkData *link = visible_gizmos->first; link; link = link->next) { + BLI_array_append(gizmo_table, link->data); + } + float co_direction[3]; + float co_screen[3] = {co[0], co[1], 0.0f}; + ED_view3d_win_to_vector(ar, (float[2]){UNPACK2(co)}, co_direction); + + RegionView3D *rv3d = ar->regiondata; + const int viewport[4] = {0, 0, ar->winx, ar->winy}; + float co_3d_origin[3]; + + GPU_matrix_unproject_model_inverted( + co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d_origin); + + GLuint *buf_iter = buffer; + int hit_found = -1; + float dot_best = FLT_MAX; + + for (int i = 0; i < hits; i++, buf_iter += 4) { + BLI_assert(buf_iter[3] != -1); + wmGizmo *gz = gizmo_table[buf_iter[3] >> 8]; + float co_3d[3]; + co_screen[2] = int_as_float(buf_iter[1]); + GPU_matrix_unproject_model_inverted(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d); + float select_bias = gz->select_bias; + if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) { + select_bias *= gz->scale_final; + } + sub_v3_v3(co_3d, co_3d_origin); + const float dot_test = dot_v3v3(co_3d, co_direction) - select_bias; + if (dot_best > dot_test) { + dot_best = dot_test; + hit_found = buf_iter[3]; + } + } + BLI_array_free(gizmo_table); + return hit_found; + } + else { + const GLuint *hit_near = GPU_select_buffer_near(buffer, hits); + return hit_near ? hit_near[3] : -1; + } } /** diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 7647e9f558f..77e17ad4687 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -82,7 +82,6 @@ void WM_operator_free(wmOperator *op) if (op->properties) { IDP_FreeProperty(op->properties); - MEM_freeN(op->properties); } if (op->reports && (op->reports->flag & RPT_FREE)) { diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 30311d83509..e4ecf7e6e94 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -442,7 +442,7 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) drag_rect_minmax(rect, x, y, x + iconsize, y + iconsize); } else { - UI_icon_draw_aspect(x, y, drag->icon, 1.0f / UI_DPI_FAC, 0.8, text_col); + UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false); } } diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 6ecbf4ef5ac..6b6a04cacad 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -917,6 +917,17 @@ void WM_draw_region_free(ARegion *ar) ar->visible = 0; } +void wm_draw_region_test(bContext *C, ScrArea *sa, ARegion *ar) +{ + /* Function for redraw timer benchmark. */ + bool use_viewport = wm_region_use_viewport(sa, ar); + wm_draw_region_buffer_create(ar, false, use_viewport); + wm_draw_region_bind(ar, 0); + ED_region_do_draw(C, ar); + wm_draw_region_unbind(ar, 0); + ar->do_draw = false; +} + void WM_redraw_windows(bContext *C) { wmWindow *win_prev = CTX_wm_window(C); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 13b6260e2b9..6683085e6d3 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1293,7 +1293,6 @@ static bool operator_last_properties_init_impl(wmOperator *op, IDProperty *last_ IDP_MergeGroup(op->properties, replaceprops, true); IDP_FreeProperty(replaceprops); - MEM_freeN(replaceprops); return changed; } @@ -1316,7 +1315,6 @@ bool WM_operator_last_properties_store(wmOperator *op) { if (op->type->last_properties) { IDP_FreeProperty(op->type->last_properties); - MEM_freeN(op->type->last_properties); op->type->last_properties = NULL; } @@ -1680,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 6d90d4745a6..097db49aea6 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" @@ -488,18 +490,21 @@ void wm_file_read_report(bContext *C, Main *bmain) static void wm_file_read_post(bContext *C, const bool is_startup_file, const bool is_factory_startup, + const bool use_data, + const bool use_userdef, const bool reset_app_template) { bool addons_loaded = false; wmWindowManager *wm = CTX_wm_manager(C); - if (!G.background) { - /* remove windows which failed to be added via WM_check */ - wm_window_ghostwindows_remove_invalid(C, wm); + if (use_data) { + if (!G.background) { + /* remove windows which failed to be added via WM_check */ + wm_window_ghostwindows_remove_invalid(C, wm); + } + CTX_wm_window_set(C, wm->windows.first); } - CTX_wm_window_set(C, wm->windows.first); - #ifdef WITH_PYTHON if (is_startup_file) { /* possible python hasn't been initialized */ @@ -513,41 +518,56 @@ static void wm_file_read_post(bContext *C, /* sync addons, these may have changed from the defaults */ BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.reset_all()"); } - BPY_python_reset(C); + if (use_data) { + BPY_python_reset(C); + } addons_loaded = true; } } else { /* run any texts that were loaded in and flagged as modules */ - BPY_python_reset(C); + if (use_data) { + BPY_python_reset(C); + } addons_loaded = true; } #else UNUSED_VARS(is_startup_file, reset_app_template); #endif /* WITH_PYTHON */ - WM_operatortype_last_properties_clear_all(); - - /* important to do before NULL'ing the context */ Main *bmain = CTX_data_main(C); - BLI_callback_exec(bmain, NULL, BLI_CB_EVT_VERSION_UPDATE); - BLI_callback_exec(bmain, NULL, BLI_CB_EVT_LOAD_POST); - if (is_factory_startup) { - BLI_callback_exec(bmain, NULL, BLI_CB_EVT_LOAD_FACTORY_STARTUP_POST); + + if (use_userdef) { + if (is_factory_startup) { + BLI_callback_exec(bmain, NULL, BLI_CB_EVT_LOAD_FACTORY_USERDEF_POST); + } + } + + if (use_data) { + /* important to do before NULL'ing the context */ + BLI_callback_exec(bmain, NULL, BLI_CB_EVT_VERSION_UPDATE); + BLI_callback_exec(bmain, NULL, BLI_CB_EVT_LOAD_POST); + if (is_factory_startup) { + BLI_callback_exec(bmain, NULL, BLI_CB_EVT_LOAD_FACTORY_STARTUP_POST); + } } - /* After load post, so for example the driver namespace can be filled - * before evaluating the depsgraph. */ - DEG_on_visible_update(bmain, true); - wm_event_do_depsgraph(C); + if (use_data) { + WM_operatortype_last_properties_clear_all(); - ED_editors_init(C); + /* After load post, so for example the driver namespace can be filled + * before evaluating the depsgraph. */ + DEG_on_visible_update(bmain, true); + wm_event_do_depsgraph(C); + + ED_editors_init(C); #if 1 - WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL); + WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL); #else - WM_msg_publish_static(CTX_wm_message_bus(C), WM_MSG_STATICTYPE_FILE_READ); + WM_msg_publish_static(CTX_wm_message_bus(C), WM_MSG_STATICTYPE_FILE_READ); #endif + } /* report any errors. * currently disabled if addons aren't yet loaded */ @@ -555,25 +575,29 @@ static void wm_file_read_post(bContext *C, wm_file_read_report(C, bmain); } - if (!G.background) { - if (wm->undo_stack == NULL) { - wm->undo_stack = BKE_undosys_stack_create(); - } - else { - BKE_undosys_stack_clear(wm->undo_stack); + if (use_data) { + if (!G.background) { + if (wm->undo_stack == NULL) { + wm->undo_stack = BKE_undosys_stack_create(); + } + else { + BKE_undosys_stack_clear(wm->undo_stack); + } + BKE_undosys_stack_init_from_main(wm->undo_stack, bmain); + BKE_undosys_stack_init_from_context(wm->undo_stack, C); } - BKE_undosys_stack_init_from_main(wm->undo_stack, bmain); - BKE_undosys_stack_init_from_context(wm->undo_stack, C); } - if (!G.background) { - /* in background mode this makes it hard to load - * a blend file and do anything since the screen - * won't be set to a valid value again */ - CTX_wm_window_set(C, NULL); /* exits queues */ + if (use_data) { + if (!G.background) { + /* in background mode this makes it hard to load + * a blend file and do anything since the screen + * won't be set to a valid value again */ + CTX_wm_window_set(C, NULL); /* exits queues */ - /* Ensure tools are registered. */ - WM_toolsystem_init(C); + /* Ensure tools are registered. */ + WM_toolsystem_init(C); + } } } @@ -600,6 +624,8 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) /* we didn't succeed, now try to read Blender file */ if (retval == BKE_READ_EXOTIC_OK_BLEND) { + bool use_data = true; + bool use_userdef = false; const int G_f_orig = G.f; ListBase wmbase; @@ -638,6 +664,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) if (retval == BKE_BLENDFILE_READ_OK_USERPREFS) { /* in case a userdef is read from regular .blend */ wm_init_userdef(bmain, false); + use_userdef = true; } if (retval != BKE_BLENDFILE_READ_FAIL) { @@ -646,7 +673,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) } } - wm_file_read_post(C, false, false, false); + wm_file_read_post(C, false, false, use_data, use_userdef, false); success = true; } @@ -716,26 +743,6 @@ const char *WM_init_state_app_template_get(void) return wm_init_state_app_template.override ? wm_init_state_app_template.app_template : NULL; } -static bool wm_app_template_has_userpref(const char *app_template) -{ - /* Test if app template provides a userpref.blend. If not, we will - * share user preferences with the rest of Blender. */ - if (!app_template && app_template[0]) { - return false; - } - - char app_template_path[FILE_MAX]; - if (!BKE_appdir_app_template_id_search( - app_template, app_template_path, sizeof(app_template_path))) { - return false; - } - - char userpref_path[FILE_MAX]; - BLI_path_join( - userpref_path, sizeof(userpref_path), app_template_path, BLENDER_USERPREF_FILE, NULL); - return BLI_exists(userpref_path); -} - /** * Called on startup, (context entirely filled with NULLs) * or called for 'New File' both startup.blend and userpref.blend are checked. @@ -758,6 +765,7 @@ void wm_homefile_read(bContext *C, ReportList *reports, bool use_factory_settings, bool use_empty_data, + bool use_data, bool use_userdef, const char *filepath_startup_override, const char *app_template_override, @@ -788,7 +796,14 @@ void wm_homefile_read(bContext *C, * And in this case versioning code is to be run. */ bool read_userdef_from_memory = false; - eBLOReadSkip skip_flags = use_userdef ? 0 : BLO_READ_SKIP_USERDEF; + eBLOReadSkip skip_flags = 0; + + if (use_data == false) { + skip_flags |= BLO_READ_SKIP_DATA; + } + if (use_userdef == false) { + skip_flags |= BLO_READ_SKIP_USERDEF; + } /* True if we load startup.blend from memory * or use app-template startup.blend which the user hasn't saved. */ @@ -801,14 +816,16 @@ void wm_homefile_read(bContext *C, SET_FLAG_FROM_TEST(G.f, (U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0, G_FLAG_SCRIPT_AUTOEXEC); } - BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_PRE); + if (use_data) { + BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_PRE); - UI_view2d_zoom_cache_reset(); + G.relbase_valid = 0; - G.relbase_valid = 0; + /* put aside screens to match with persistent windows later */ + wm_window_match_init(C, &wmbase); + } - /* put aside screens to match with persistent windows later */ - wm_window_match_init(C, &wmbase); + UI_view2d_zoom_cache_reset(); filepath_startup[0] = '\0'; filepath_userdef[0] = '\0'; @@ -931,7 +948,9 @@ void wm_homefile_read(bContext *C, } if (success) { if (update_defaults) { - BLO_update_defaults_startup_blend(CTX_data_main(C), app_template); + if (use_data) { + BLO_update_defaults_startup_blend(CTX_data_main(C), app_template); + } } is_factory_startup = filepath_startup_is_factory; } @@ -1010,10 +1029,12 @@ void wm_homefile_read(bContext *C, BLI_strncpy(U.app_template, app_template_override, sizeof(U.app_template)); } - /* Prevent buggy files that had G_FILE_RELATIVE_REMAP written out by mistake. - * Screws up autosaves otherwise can remove this eventually, - * only in a 2.53 and older, now its not written. */ - G.fileflags &= ~G_FILE_RELATIVE_REMAP; + if (use_data) { + /* Prevent buggy files that had G_FILE_RELATIVE_REMAP written out by mistake. + * Screws up autosaves otherwise can remove this eventually, + * only in a 2.53 and older, now its not written. */ + G.fileflags &= ~G_FILE_RELATIVE_REMAP; + } bmain = CTX_data_main(C); @@ -1023,8 +1044,10 @@ void wm_homefile_read(bContext *C, reset_app_template = true; } - /* match the read WM with current WM */ - wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm); + if (use_data) { + /* match the read WM with current WM */ + wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm); + } if (use_factory_settings) { /* Clear keymaps because the current default keymap may have been initialized @@ -1036,14 +1059,16 @@ void wm_homefile_read(bContext *C, } } - WM_check(C); /* opens window(s), checks keymaps */ + if (use_data) { + WM_check(C); /* opens window(s), checks keymaps */ - bmain->name[0] = '\0'; + bmain->name[0] = '\0'; - /* start with save preference untitled.blend */ - G.save_over = 0; + /* start with save preference untitled.blend */ + G.save_over = 0; + } - wm_file_read_post(C, true, is_factory_startup, reset_app_template); + wm_file_read_post(C, true, is_factory_startup, use_data, use_userdef, reset_app_template); if (r_is_factory_startup) { *r_is_factory_startup = is_factory_startup; @@ -1245,7 +1270,6 @@ static ImBuf *blend_file_thumb(const bContext *C, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, IB_rect, - V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL, @@ -1653,6 +1677,7 @@ static int wm_userpref_autoexec_add_exec(bContext *UNUSED(C), wmOperator *UNUSED { bPathCompare *path_cmp = MEM_callocN(sizeof(bPathCompare), "bPathCompare"); BLI_addtail(&U.autoexec_paths, path_cmp); + U.runtime.is_dirty = true; return OPERATOR_FINISHED; } @@ -1673,6 +1698,7 @@ static int wm_userpref_autoexec_remove_exec(bContext *UNUSED(C), wmOperator *op) bPathCompare *path_cmp = BLI_findlink(&U.autoexec_paths, index); if (path_cmp) { BLI_freelinkN(&U.autoexec_paths, path_cmp); + U.runtime.is_dirty = true; } return OPERATOR_FINISHED; } @@ -1694,69 +1720,141 @@ void WM_OT_userpref_autoexec_path_remove(wmOperatorType *ot) static int wm_userpref_write_exec(bContext *C, wmOperator *op) { wmWindowManager *wm = CTX_wm_manager(C); - char filepath[FILE_MAX]; - const char *cfgdir; - bool ok = true; - bool use_template_userpref = wm_app_template_has_userpref(U.app_template); - /* update keymaps in user preferences */ + /* Update keymaps in user preferences. */ WM_keyconfig_update(wm); - if ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL))) { - bool ok_write; - BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL); + const bool ok = BKE_blendfile_userdef_write_all(op->reports); - printf("Writing userprefs: '%s' ", filepath); - if (use_template_userpref) { - ok_write = BKE_blendfile_userdef_write_app_template(filepath, op->reports); - } - else { - ok_write = BKE_blendfile_userdef_write(filepath, op->reports); - } + return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} - if (ok_write) { - printf("ok\n"); - } - else { - printf("fail\n"); - ok = false; - } - } - else { - BKE_report(op->reports, RPT_ERROR, "Unable to create userpref path"); - } +void WM_OT_save_userpref(wmOperatorType *ot) +{ + ot->name = "Save Preferences"; + ot->idname = "WM_OT_save_userpref"; + ot->description = "Save preferences separately, overrides startup file preferences"; - if (use_template_userpref) { - if ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, U.app_template))) { - /* Also save app-template prefs */ - BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL); + ot->invoke = WM_operator_confirm; + ot->exec = wm_userpref_write_exec; +} - printf("Writing userprefs app-template: '%s' ", filepath); - if (BKE_blendfile_userdef_write(filepath, op->reports) != 0) { - printf("ok\n"); +static void rna_struct_update_when_changed(bContext *C, + Main *bmain, + PointerRNA *ptr_a, + PointerRNA *ptr_b) +{ + CollectionPropertyIterator iter; + PropertyRNA *iterprop = RNA_struct_iterator_property(ptr_a->type); + BLI_assert(ptr_a->type == ptr_b->type); + RNA_property_collection_begin(ptr_a, iterprop, &iter); + for (; iter.valid; RNA_property_collection_next(&iter)) { + PropertyRNA *prop = iter.ptr.data; + if (STREQ(RNA_property_identifier(prop), "rna_type")) { + continue; + } + switch (RNA_property_type(prop)) { + case PROP_POINTER: { + PointerRNA ptr_sub_a = RNA_property_pointer_get(ptr_a, prop); + PointerRNA ptr_sub_b = RNA_property_pointer_get(ptr_b, prop); + rna_struct_update_when_changed(C, bmain, &ptr_sub_a, &ptr_sub_b); + break; } - else { - printf("fail\n"); - ok = false; + case PROP_COLLECTION: + /* Don't handle collections. */ + break; + default: { + if (!RNA_property_equals(bmain, ptr_a, ptr_b, prop, RNA_EQ_STRICT)) { + RNA_property_update(C, ptr_b, prop); + } } } - else { - BKE_report(op->reports, RPT_ERROR, "Unable to create app-template userpref path"); - ok = false; - } } + RNA_property_collection_end(&iter); +} - return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +static void wm_userpref_update_when_changed(bContext *C, + Main *bmain, + UserDef *userdef_prev, + UserDef *userdef_curr) +{ + PointerRNA ptr_a, ptr_b; + RNA_pointer_create(NULL, &RNA_Preferences, userdef_prev, &ptr_a); + RNA_pointer_create(NULL, &RNA_Preferences, userdef_curr, &ptr_b); + const bool is_dirty = userdef_curr->runtime.is_dirty; + + rna_struct_update_when_changed(C, bmain, &ptr_a, &ptr_b); + +#ifdef WITH_PYTHON + BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.reset_all()"); +#endif + + WM_keyconfig_reload(C); + userdef_curr->runtime.is_dirty = is_dirty; } -void WM_OT_save_userpref(wmOperatorType *ot) +static int wm_userpref_read_exec(bContext *C, wmOperator *op) { - ot->name = "Save Preferences"; - ot->idname = "WM_OT_save_userpref"; - ot->description = "Save preferences separately, overrides startup file preferences"; + const bool use_data = false; + const bool use_userdef = true; + const bool use_factory_settings = STREQ(op->type->idname, "WM_OT_read_factory_userpref"); + + UserDef U_backup = U; + + wm_homefile_read(C, + op->reports, + use_factory_settings, + false, + use_data, + use_userdef, + NULL, + WM_init_state_app_template_get(), + NULL); + +#define USERDEF_RESTORE(member) \ + { \ + U.member = U_backup.member; \ + } \ + ((void)0) + + USERDEF_RESTORE(userpref); + +#undef USERDEF_RESTORE + + Main *bmain = CTX_data_main(C); + + wm_userpref_update_when_changed(C, bmain, &U_backup, &U); + + if (use_factory_settings) { + U.runtime.is_dirty = true; + } + + /* Needed to recalculate UI scaling values (eg, #UserDef.inv_dpi_fac). */ + wm_window_clear_drawable(bmain->wm.first); + + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; +} + +void WM_OT_read_userpref(wmOperatorType *ot) +{ + ot->name = "Load Preferences"; + ot->idname = "WM_OT_read_userpref"; + ot->description = "Load last saved preferences"; ot->invoke = WM_operator_confirm; - ot->exec = wm_userpref_write_exec; + ot->exec = wm_userpref_read_exec; +} + +void WM_OT_read_factory_userpref(wmOperatorType *ot) +{ + ot->name = "Load Factory Preferences"; + ot->idname = "WM_OT_read_factory_userpref"; + ot->description = "Load default preferences"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_userpref_read_exec; } static int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) @@ -1816,14 +1914,15 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op) PropertyRNA *prop_app_template = RNA_struct_find_property(op->ptr, "app_template"); const bool use_splash = !use_factory_settings && RNA_boolean_get(op->ptr, "use_splash"); const bool use_empty_data = RNA_boolean_get(op->ptr, "use_empty"); + const bool use_temporary_preferences = RNA_boolean_get(op->ptr, "use_temporary_preferences"); if (prop_app_template && RNA_property_is_set(op->ptr, prop_app_template)) { RNA_property_string_get(op->ptr, prop_app_template, app_template_buf); app_template = app_template_buf; /* Always load preferences when switching templates with own preferences. */ - use_userdef = wm_app_template_has_userpref(app_template) || - wm_app_template_has_userpref(U.app_template); + use_userdef = BKE_appdir_app_template_has_userpref(app_template) || + BKE_appdir_app_template_has_userpref(U.app_template); /* Turn override off, since we're explicitly loading a different app-template. */ WM_init_state_app_template_set(NULL); @@ -1833,10 +1932,12 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op) app_template = WM_init_state_app_template_get(); } + bool use_data = true; wm_homefile_read(C, op->reports, use_factory_settings, use_empty_data, + use_data, use_userdef, filepath, app_template, @@ -1844,20 +1945,56 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op) if (use_splash) { WM_init_splash(C); } + SET_FLAG_FROM_TEST(G.f, use_temporary_preferences, G_FLAG_USERPREF_NO_SAVE_ON_EXIT); + 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)) { + wmGenericCallback *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); } } +static void read_homefile_props(wmOperatorType *ot) +{ + PropertyRNA *prop; + + prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "use_empty", false, "Empty", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, + "use_temporary_preferences", + false, + "Temporary Preferences", + "Don't save preferences on exit"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + void WM_OT_read_homefile(wmOperatorType *ot) { PropertyRNA *prop; @@ -1877,23 +2014,17 @@ void WM_OT_read_homefile(wmOperatorType *ot) 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); - prop = RNA_def_boolean(ot->srna, "use_empty", false, "Empty", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - /* So the splash can be kept open after loading a file (for templates). */ prop = RNA_def_boolean(ot->srna, "use_splash", false, "Splash", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + read_homefile_props(ot); /* omit poll to run in background mode */ } void WM_OT_read_factory_settings(wmOperatorType *ot) { - PropertyRNA *prop; - ot->name = "Load Factory Settings"; ot->idname = "WM_OT_read_factory_settings"; ot->description = "Load default file and preferences"; @@ -1901,12 +2032,7 @@ void WM_OT_read_factory_settings(wmOperatorType *ot) ot->invoke = WM_operator_confirm; ot->exec = wm_homefile_read_exec; - prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - prop = RNA_def_boolean(ot->srna, "use_empty", false, "Empty", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - + read_homefile_props(ot); /* omit poll to run in background mode */ } @@ -1939,13 +2065,88 @@ static bool wm_file_read_opwrap(bContext *C, return success; } -/* currently fits in a pointer */ -struct FileRuntime { - bool is_untrusted; +/* Generic operator state utilities + *********************************************/ + +static void create_operator_state(wmOperatorType *ot, int first_state) +{ + PropertyRNA *prop = RNA_def_int( + ot->srna, "state", first_state, INT32_MIN, INT32_MAX, "State", "", INT32_MIN, INT32_MAX); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_flag(prop, PROP_HIDDEN); +} + +static int get_operator_state(wmOperator *op) +{ + return RNA_int_get(op->ptr, "state"); +} + +static void set_next_operator_state(wmOperator *op, int state) +{ + RNA_int_set(op->ptr, "state", state); +} + +typedef struct OperatorDispatchTarget { + int state; + int (*run)(bContext *C, wmOperator *op); +} OperatorDispatchTarget; + +static int operator_state_dispatch(bContext *C, wmOperator *op, OperatorDispatchTarget *targets) +{ + int state = get_operator_state(op); + for (int i = 0; targets[i].run; i++) { + OperatorDispatchTarget target = targets[i]; + if (target.state == state) { + return target.run(C, op); + } + } + BLI_assert(false); + return OPERATOR_CANCELLED; +} + +/* Open Mainfile operator + ********************************************/ + +enum { + OPEN_MAINFILE_STATE_DISCARD_CHANGES, + OPEN_MAINFILE_STATE_SELECT_FILE_PATH, + OPEN_MAINFILE_STATE_OPEN, }; -static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +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")) { + set_next_operator_state(op, OPEN_MAINFILE_STATE_SELECT_FILE_PATH); + } + else { + set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN); + } + + if (U.uiflag & USER_SAVE_PROMPT && wm_file_or_image_is_modified(C)) { + wmGenericCallback *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); + } +} + +static int wm_open_mainfile__select_file_path(bContext *C, wmOperator *op) +{ + set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN); + Main *bmain = CTX_data_main(C); const char *openname = BKE_main_blendfile_path(bmain); @@ -1973,7 +2174,7 @@ static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *U return OPERATOR_RUNNING_MODAL; } -static int wm_open_mainfile_exec(bContext *C, wmOperator *op) +static int wm_open_mainfile__open(bContext *C, wmOperator *op) { char filepath[FILE_MAX]; bool success; @@ -2011,6 +2212,33 @@ static int wm_open_mainfile_exec(bContext *C, wmOperator *op) } } +static OperatorDispatchTarget wm_open_mainfile_dispatch_targets[] = { + {OPEN_MAINFILE_STATE_DISCARD_CHANGES, wm_open_mainfile__discard_changes}, + {OPEN_MAINFILE_STATE_SELECT_FILE_PATH, wm_open_mainfile__select_file_path}, + {OPEN_MAINFILE_STATE_OPEN, wm_open_mainfile__open}, + {0, NULL}, +}; + +static int wm_open_mainfile_dispatch(bContext *C, wmOperator *op) +{ + return operator_state_dispatch(C, op, wm_open_mainfile_dispatch_targets); +} + +static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + return wm_open_mainfile_dispatch(C, op); +} + +static int wm_open_mainfile_exec(bContext *C, wmOperator *op) +{ + return wm_open_mainfile__open(C, op); +} + +/* currently fits in a pointer */ +struct FileRuntime { + bool is_untrusted; +}; + static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op) { struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; @@ -2091,6 +2319,12 @@ void WM_OT_open_mainfile(wmOperatorType *ot) "Trusted Source", "Allow .blend file to execute scripts automatically, default available from " "system preferences"); + + PropertyRNA *prop = RNA_def_boolean( + ot->srna, "display_file_selector", true, "Display File Selector", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + create_operator_state(ot, OPEN_MAINFILE_STATE_DISCARD_CHANGES); } /** \} */ @@ -2629,9 +2863,228 @@ void wm_test_autorun_warning(bContext *C) if (win) { wmWindow *prevwin = CTX_wm_window(C); CTX_wm_window_set(C, win); - UI_popup_block_invoke(C, block_create_autorun_warning, NULL); + UI_popup_block_invoke(C, block_create_autorun_warning, NULL, NULL); CTX_wm_window_set(C, prevwin); } } +/* 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) +{ + wmGenericCallback *callback = WM_generic_callback_steal((wmGenericCallback *)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) +{ + wmGenericCallback *callback = WM_generic_callback_steal((wmGenericCallback *)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 void wm_block_file_close_cancel_button(uiBlock *block, wmGenericCallback *post_action) +{ + uiBut *but = uiDefIconTextBut( + block, UI_BTYPE_BUT, 0, 0, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, ""); + UI_but_func_set(but, wm_block_file_close_cancel, block, post_action); + UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); +} + +static void wm_block_file_close_discard_button(uiBlock *block, wmGenericCallback *post_action) +{ + uiBut *but = uiDefIconTextBut( + block, UI_BTYPE_BUT, 0, 0, IFACE_("Discard Changes"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, ""); + UI_but_func_set(but, wm_block_file_close_discard, block, post_action); + UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); +} + +static void wm_block_file_close_save_button(uiBlock *block, wmGenericCallback *post_action) +{ + uiBut *but = uiDefIconTextBut( + block, UI_BTYPE_BUT, 0, 0, IFACE_("Save"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, ""); + UI_but_func_set(but, wm_block_file_close_save, block, post_action); + UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); + UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT); +} + +static const char *close_file_dialog_name = "file_close_popup"; + +static uiBlock *block_create__close_file_dialog(struct bContext *C, struct ARegion *ar, void *arg1) +{ + wmGenericCallback *post_action = (wmGenericCallback *)arg1; + Main *bmain = CTX_data_main(C); + + uiStyle *style = UI_style_get(); + uiBlock *block = UI_block_begin(C, ar, close_file_dialog_name, 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); + + /* Title */ + 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_INFO); + } + else { + uiItemL(layout, "This file has not been saved yet.", ICON_INFO); + } + + /* Image Saving */ + 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); + + /* Buttons */ +#ifdef _WIN32 + const bool windows_layout = true; +#else + const bool windows_layout = false; +#endif + + uiLayout *split = uiLayoutSplit(layout, 0.0f, true); + + if (windows_layout) { + /* Windows standard layout. */ + uiLayout *col = uiLayoutColumn(split, false); + uiItemS(col); + + col = uiLayoutColumn(split, false); + wm_block_file_close_save_button(block, post_action); + + col = uiLayoutColumn(split, false); + wm_block_file_close_discard_button(block, post_action); + + col = uiLayoutColumn(split, false); + wm_block_file_close_cancel_button(block, post_action); + } + else { + /* macOS and Linux standard layout. */ + uiLayout *col = uiLayoutColumn(split, false); + wm_block_file_close_discard_button(block, post_action); + + col = uiLayoutColumn(split, false); + uiItemS(col); + + col = uiLayoutColumn(split, false); + wm_block_file_close_cancel_button(block, post_action); + + col = uiLayoutColumn(split, false); + wm_block_file_close_save_button(block, post_action); + } + + UI_block_bounds_set_centered(block, 10); + return block; +} + +static void free_post_file_close_action(void *arg) +{ + wmGenericCallback *action = (wmGenericCallback *)arg; + WM_generic_callback_free(action); +} + +void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action) +{ + if (!UI_popup_block_name_exists(C, close_file_dialog_name)) { + UI_popup_block_invoke( + C, block_create__close_file_dialog, post_action, free_post_file_close_action); + } + else { + WM_generic_callback_free(post_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); +} + /** \} */ diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index b12bb89ea9f..e117a1bcdfe 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -62,7 +62,7 @@ wmGesture *WM_gesture_new(bContext *C, const wmEvent *event, int type) gesture->type = type; gesture->event_type = event->type; gesture->winrct = ar->winrct; - gesture->userdata_free = true; /* Free if userdata is set. */ + gesture->user_data.use_free = true; /* Free if userdata is set. */ gesture->modal_state = GESTURE_MODAL_NOP; if (ELEM(type, @@ -106,9 +106,7 @@ void WM_gesture_end(bContext *C, wmGesture *gesture) } BLI_remlink(&win->gesture, gesture); MEM_freeN(gesture->customdata); - if (gesture->userdata && gesture->userdata_free) { - MEM_freeN(gesture->userdata); - } + WM_generic_user_data_free(&gesture->user_data); MEM_freeN(gesture); } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 97ba9190351..04a3115992f 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -52,6 +52,7 @@ #include "BLO_writefile.h" #include "BLO_undofile.h" +#include "BKE_blendfile.h" #include "BKE_blender.h" #include "BKE_blender_undo.h" #include "BKE_context.h" @@ -254,11 +255,15 @@ void WM_init(bContext *C, int argc, const char **argv) /* get the default database, plus a wm */ bool is_factory_startup = true; + const bool use_data = true; + const bool use_userdef = true; + wm_homefile_read(C, NULL, G.factory_startup, false, - true, + use_data, + use_userdef, NULL, WM_init_state_app_template_get(), &is_factory_startup); @@ -469,6 +474,14 @@ void WM_exit_ext(bContext *C, const bool do_python) WM_event_remove_handlers(C, &win->modalhandlers); ED_screen_exit(C, win, WM_window_get_active_screen(win)); } + + if (!G.background) { + if ((U.pref_flag & USER_PREF_FLAG_SAVE) && ((G.f & G_FLAG_USERPREF_NO_SAVE_ON_EXIT) == 0)) { + if (U.runtime.is_dirty) { + BKE_blendfile_userdef_write_all(NULL); + } + } + } } BLI_timer_free(); diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index bae9a5de1e6..a4ee735d911 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -310,6 +310,7 @@ bool WM_keyconfig_remove(wmWindowManager *wm, wmKeyConfig *keyconf) if (BLI_findindex(&wm->keyconfigs, keyconf) != -1) { if (STREQLEN(U.keyconfigstr, keyconf->idname, sizeof(U.keyconfigstr))) { BLI_strncpy(U.keyconfigstr, wm->defaultconf->idname, sizeof(U.keyconfigstr)); + U.runtime.is_dirty = true; WM_keyconfig_update_tag(NULL, NULL); } @@ -360,6 +361,9 @@ void WM_keyconfig_set_active(wmWindowManager *wm, const char *idname) WM_keyconfig_update(wm); BLI_strncpy(U.keyconfigstr, idname, sizeof(U.keyconfigstr)); + if (wm->initialized & WM_KEYCONFIG_IS_INITIALIZED) { + U.runtime.is_dirty = true; + } WM_keyconfig_update_tag(NULL, NULL); WM_keyconfig_update(wm); @@ -1120,7 +1124,8 @@ const char *WM_key_event_string(const short type, const bool compact) if (platform == MACOS) { icon_glyph = "\xe2\x87\xa7"; } - return key_event_icon_or_text(font_id, IFACE_("Shift"), icon_glyph); + return key_event_icon_or_text( + font_id, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Shift"), icon_glyph); } case LEFTCTRLKEY: case RIGHTCTRLKEY: @@ -1407,7 +1412,6 @@ static wmKeyMapItem *wm_keymap_item_find_in_keymap(wmKeyMap *keymap, } IDP_FreeProperty(properties_default); - MEM_freeN(properties_default); } } } @@ -1581,7 +1585,6 @@ static wmKeyMapItem *wm_keymap_item_find(const bContext *C, } IDP_FreeProperty(properties_temp); - MEM_freeN(properties_temp); } } @@ -1620,7 +1623,6 @@ static wmKeyMapItem *wm_keymap_item_find(const bContext *C, } IDP_FreeProperty(properties_default); - MEM_freeN(properties_default); } } } @@ -2017,7 +2019,6 @@ void WM_keymap_item_restore_to_default(bContext *C, wmKeyMap *keymap, wmKeyMapIt if (orig->properties) { if (kmi->properties) { IDP_FreeProperty(kmi->properties); - MEM_freeN(kmi->properties); kmi->properties = NULL; } diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index ece57f5a63b..3ad7247d993 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -94,7 +94,9 @@ void WM_operator_properties_filesel(wmOperatorType *ot, } if (flag & WM_FILESEL_FILES) { - RNA_def_collection_runtime(ot->srna, "files", &RNA_OperatorFileListElement, "Files", ""); + prop = RNA_def_collection_runtime( + ot->srna, "files", &RNA_OperatorFileListElement, "Files", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } if (action == FILE_SAVE) { @@ -344,11 +346,11 @@ void WM_operator_properties_gesture_box(wmOperatorType *ot) void WM_operator_properties_select_operation(wmOperatorType *ot) { static const EnumPropertyItem select_mode_items[] = { - {SEL_OP_SET, "SET", 0, "New", ""}, - {SEL_OP_ADD, "ADD", 0, "Add", ""}, - {SEL_OP_SUB, "SUB", 0, "Subtract", ""}, - {SEL_OP_XOR, "XOR", 0, "Difference", ""}, - {SEL_OP_AND, "AND", 0, "Intersect", ""}, + {SEL_OP_SET, "SET", ICON_SELECT_SET, "Set", "Set a new selection"}, + {SEL_OP_ADD, "ADD", ICON_SELECT_EXTEND, "Extend", "Extend existing selection"}, + {SEL_OP_SUB, "SUB", ICON_SELECT_SUBTRACT, "Subtract", "Subtract existing selection"}, + {SEL_OP_XOR, "XOR", ICON_SELECT_DIFFERENCE, "Difference", "Inverts existing selection"}, + {SEL_OP_AND, "AND", ICON_SELECT_INTERSECT, "Intersect", "Intersect existing selection"}, {0, NULL, 0, NULL, NULL}, }; PropertyRNA *prop = RNA_def_enum(ot->srna, "mode", select_mode_items, SEL_OP_SET, "Mode", ""); @@ -359,9 +361,9 @@ void WM_operator_properties_select_operation(wmOperatorType *ot) void WM_operator_properties_select_operation_simple(wmOperatorType *ot) { static const EnumPropertyItem select_mode_items[] = { - {SEL_OP_SET, "SET", 0, "New", ""}, - {SEL_OP_ADD, "ADD", 0, "Add", ""}, - {SEL_OP_SUB, "SUB", 0, "Subtract", ""}, + {SEL_OP_SET, "SET", ICON_SELECT_SET, "Set", "Set a new selection"}, + {SEL_OP_ADD, "ADD", ICON_SELECT_EXTEND, "Extend", "Extend existing selection"}, + {SEL_OP_SUB, "SUB", ICON_SELECT_SUBTRACT, "Subtract", "Subtract existing selection"}, {0, NULL, 0, NULL, NULL}, }; PropertyRNA *prop = RNA_def_enum(ot->srna, "mode", select_mode_items, SEL_OP_SET, "Mode", ""); diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c index 3585cffc615..179b4402200 100644 --- a/source/blender/windowmanager/intern/wm_operator_type.c +++ b/source/blender/windowmanager/intern/wm_operator_type.c @@ -156,7 +156,6 @@ void WM_operatortype_remove_ptr(wmOperatorType *ot) if (ot->last_properties) { IDP_FreeProperty(ot->last_properties); - MEM_freeN(ot->last_properties); } if (ot->macro.first) { @@ -194,7 +193,6 @@ static void operatortype_ghash_free_cb(wmOperatorType *ot) { if (ot->last_properties) { IDP_FreeProperty(ot->last_properties); - MEM_freeN(ot->last_properties); } if (ot->macro.first) { @@ -279,7 +277,6 @@ void WM_operatortype_last_properties_clear_all(void) if (ot->last_properties) { IDP_FreeProperty(ot->last_properties); - MEM_freeN(ot->last_properties); ot->last_properties = NULL; } } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index ec803c9bba7..4a99c2de6e7 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -691,7 +691,6 @@ void WM_operator_properties_free(PointerRNA *ptr) if (properties) { IDP_FreeProperty(properties); - MEM_freeN(properties); ptr->data = NULL; /* just in case */ } } @@ -870,7 +869,7 @@ int WM_enum_search_invoke_previews(bContext *C, wmOperator *op, short prv_cols, search_menu.prv_cols = prv_cols; search_menu.prv_rows = prv_rows; - UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu); + UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu, NULL); return OPERATOR_INTERFACE; } @@ -879,13 +878,17 @@ int WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(eve { static struct EnumSearchMenu search_menu; search_menu.op = op; - UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu); + UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu, NULL); return OPERATOR_INTERFACE; } /* Can't be used as an invoke directly, needs message arg (can be NULL) */ -int WM_operator_confirm_message_ex( - bContext *C, wmOperator *op, const char *title, const int icon, const char *message) +int WM_operator_confirm_message_ex(bContext *C, + wmOperator *op, + const char *title, + const int icon, + const char *message, + const short opcontext) { uiPopupMenu *pup; uiLayout *layout; @@ -900,8 +903,7 @@ int WM_operator_confirm_message_ex( pup = UI_popup_menu_begin(C, title, icon); layout = UI_popup_menu_layout(pup); - uiItemFullO_ptr( - layout, op->type, message, ICON_NONE, properties, WM_OP_EXEC_REGION_WIN, 0, NULL); + uiItemFullO_ptr(layout, op->type, message, ICON_NONE, properties, opcontext, 0, NULL); UI_popup_menu_end(C, pup); return OPERATOR_INTERFACE; @@ -909,7 +911,8 @@ int WM_operator_confirm_message_ex( int WM_operator_confirm_message(bContext *C, wmOperator *op, const char *message) { - return WM_operator_confirm_message_ex(C, op, IFACE_("OK?"), ICON_QUESTION, message); + return WM_operator_confirm_message_ex( + C, op, IFACE_("OK?"), ICON_QUESTION, message, WM_OP_EXEC_REGION_WIN); } int WM_operator_confirm(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) @@ -1398,7 +1401,7 @@ int WM_operator_redo_popup(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - UI_popup_block_invoke(C, wm_block_create_redo, op); + UI_popup_block_invoke(C, wm_block_create_redo, op, NULL); return OPERATOR_CANCELLED; } @@ -1710,7 +1713,7 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) { - UI_popup_block_invoke(C, wm_block_create_splash, NULL); + UI_popup_block_invoke(C, wm_block_create_splash, NULL, NULL); return OPERATOR_FINISHED; } @@ -1816,7 +1819,7 @@ static int wm_search_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEv data.size[0] = UI_searchbox_size_x() * 2; data.size[1] = UI_searchbox_size_y(); - UI_popup_block_invoke(C, wm_block_search_menu, &data); + UI_popup_block_invoke(C, wm_block_search_menu, &data, NULL); return OPERATOR_INTERFACE; } @@ -3082,8 +3085,7 @@ static void redraw_timer_step(bContext *C, { if (type == eRTDrawRegion) { if (ar) { - ED_region_do_draw(C, ar); - ar->do_draw = false; + wm_draw_region_test(C, sa, ar); } } else if (type == eRTDrawRegionSwap) { @@ -3107,8 +3109,7 @@ static void redraw_timer_step(bContext *C, for (ar_iter = sa_iter->regionbase.first; ar_iter; ar_iter = ar_iter->next) { if (ar_iter->visible) { CTX_wm_region_set(C, ar_iter); - ED_region_do_draw(C, ar_iter); - ar_iter->do_draw = false; + wm_draw_region_test(C, sa_iter, ar_iter); } } } @@ -3153,6 +3154,7 @@ static int redraw_timer_exec(bContext *C, wmOperator *op) wmWindow *win = CTX_wm_window(C); ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); + wmWindowManager *wm = CTX_wm_manager(C); double time_start, time_delta; const int type = RNA_enum_get(op->ptr, "type"); const int iter = RNA_int_get(op->ptr, "iterations"); @@ -3166,6 +3168,8 @@ static int redraw_timer_exec(bContext *C, wmOperator *op) time_start = PIL_check_seconds_timer(); + wm_window_make_drawable(wm, win); + for (a = 0; a < iter; a++) { redraw_timer_step(C, bmain, scene, depsgraph, win, sa, ar, type, cfra); iter_steps += 1; @@ -3504,6 +3508,8 @@ void wm_operatortypes_register(void) WM_operatortype_append(WM_OT_read_factory_settings); WM_operatortype_append(WM_OT_save_homefile); WM_operatortype_append(WM_OT_save_userpref); + WM_operatortype_append(WM_OT_read_userpref); + WM_operatortype_append(WM_OT_read_factory_userpref); WM_operatortype_append(WM_OT_userpref_autoexec_path_add); WM_operatortype_append(WM_OT_userpref_autoexec_path_remove); WM_operatortype_append(WM_OT_window_fullscreen_toggle); diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 844316bc925..7cc44bcad99 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -1063,7 +1063,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) break; } - case GHOST_kEventQuit: + case GHOST_kEventQuitRequest: case GHOST_kEventWindowClose: { ps->go = false; break; diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index f429415bee9..3ea58d8c4e5 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -574,6 +574,20 @@ void WM_toolsystem_refresh_active(bContext *C) } } } + + BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_DOIT); + + LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { + if (workspace->id.tag & LIB_TAG_DOIT) { + workspace->id.tag &= ~LIB_TAG_DOIT; + /* Refresh to ensure data is initialized. + * This is needed because undo can load a state which no longer has the underlying DNA data + * needed for the tool (un-initialized paint-slots for eg), see: T64339. */ + for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) { + toolsystem_refresh_ref(C, workspace, tref); + } + } + } } void WM_toolsystem_refresh_screen_area(WorkSpace *workspace, ViewLayer *view_layer, ScrArea *sa) @@ -712,7 +726,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) case SPACE_IMAGE: switch (tkey->mode) { case SI_MODE_PAINT: - return "builtin_brush.draw"; + return "builtin_brush.Draw"; } break; case SPACE_NODE: { diff --git a/source/blender/windowmanager/intern/wm_utils.c b/source/blender/windowmanager/intern/wm_utils.c new file mode 100644 index 00000000000..c0ee1ec44db --- /dev/null +++ b/source/blender/windowmanager/intern/wm_utils.c @@ -0,0 +1,71 @@ +/* + * 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. + */ + +/** \file + * \ingroup wm + * + * Generic helper utilies that aren't assosiated with a particular area. + */ + +#include "WM_types.h" +#include "WM_api.h" + +#include "MEM_guardedalloc.h" + +/* -------------------------------------------------------------------- */ +/** \name Generic Callback + * \{ */ + +void WM_generic_callback_free(wmGenericCallback *callback) +{ + if (callback->free_user_data) { + callback->free_user_data(callback->user_data); + } + MEM_freeN(callback); +} + +static void do_nothing(struct bContext *UNUSED(C), void *UNUSED(user_data)) +{ +} + +wmGenericCallback *WM_generic_callback_steal(wmGenericCallback *callback) +{ + wmGenericCallback *new_callback = MEM_dupallocN(callback); + callback->exec = do_nothing; + callback->free_user_data = NULL; + callback->user_data = NULL; + return new_callback; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Generic User Data + * \{ */ + +void WM_generic_user_data_free(wmGenericUserData *wm_userdata) +{ + if (wm_userdata->data && wm_userdata->use_free) { + if (wm_userdata->free_fn) { + wm_userdata->free_fn(wm_userdata->data); + } + else { + MEM_freeN(wm_userdata->data); + } + } +} + +/** \} */ diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index e98067d78cc..7ae572e5685 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); - } - } - else if (GHOST_confirmQuit(win->ghostwin)) { - wm_exit_schedule_delayed(C); - } + wmGenericCallback *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 { @@ -672,6 +524,7 @@ void WM_window_set_dpi(wmWindow *win) U.dpi = dpi / pixelsize; U.virtual_pixel = (pixelsize == 1) ? VIRTUAL_PIXEL_NATIVE : VIRTUAL_PIXEL_DOUBLE; U.dpi_fac = ((U.pixelsize * (float)U.dpi) / 72.0f); + U.inv_dpi_fac = 1.0f / U.dpi_fac; /* Set user preferences globals for drawing, and for forward compatibility. */ U.widget_unit = (U.pixelsize * U.dpi * 20 + 36) / 72; @@ -1264,8 +1117,25 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr GHOST_TEventType type = GHOST_GetEventType(evt); int time = GHOST_GetEventTime(evt); - if (type == GHOST_kEventQuit) { - WM_exit(C); + if (type == GHOST_kEventQuitRequest) { + /* Find an active window to display quit dialog in. */ + GHOST_WindowHandle ghostwin = GHOST_GetEventWindow(evt); + wmWindow *win; + + if (ghostwin && GHOST_ValidWindow(g_system, ghostwin)) { + win = GHOST_GetWindowUserData(ghostwin); + } + else { + win = wm->winactive; + } + + /* Display quit dialog or quit immediately. */ + if (win) { + wm_quit_with_optional_confirmation_prompt(C, win); + } + else { + wm_exit_schedule_delayed(C); + } } else { GHOST_WindowHandle ghostwin = GHOST_GetEventWindow(evt); diff --git a/source/blender/windowmanager/wm_draw.h b/source/blender/windowmanager/wm_draw.h index ede2b3191b9..0a07eb998cf 100644 --- a/source/blender/windowmanager/wm_draw.h +++ b/source/blender/windowmanager/wm_draw.h @@ -38,6 +38,7 @@ typedef struct wmDrawBuffer { } wmDrawBuffer; struct ARegion; +struct ScrArea; struct bContext; struct wmWindow; @@ -45,6 +46,7 @@ struct wmWindow; void wm_draw_update(struct bContext *C); void wm_draw_region_clear(struct wmWindow *win, struct ARegion *ar); void wm_draw_region_blend(struct ARegion *ar, int view, bool blend); +void wm_draw_region_test(struct bContext *C, struct ScrArea *sa, struct ARegion *ar); struct GPUTexture *wm_draw_region_texture(struct ARegion *ar, int view); diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index c60df27e24b..0aa4357a8f4 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -25,6 +25,7 @@ #define __WM_FILES_H__ struct Main; +struct wmGenericCallback; struct wmOperatorType; /* wm_files.c */ @@ -33,16 +34,22 @@ void wm_homefile_read(struct bContext *C, struct ReportList *reports, bool use_factory_settings, bool use_empty_data, + bool use_data, bool use_userdef, const char *filepath_startup_override, const char *app_template_override, bool *r_is_factory_startup); void wm_file_read_report(bContext *C, struct Main *bmain); +void wm_close_file_dialog(bContext *C, struct wmGenericCallback *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); void WM_OT_save_userpref(struct wmOperatorType *ot); +void WM_OT_read_userpref(struct wmOperatorType *ot); +void WM_OT_read_factory_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); |