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:
Diffstat (limited to 'source/blender/windowmanager')
-rw-r--r--source/blender/windowmanager/CMakeLists.txt17
-rw-r--r--source/blender/windowmanager/WM_api.h57
-rw-r--r--source/blender/windowmanager/WM_keymap.h23
-rw-r--r--source/blender/windowmanager/WM_types.h56
-rw-r--r--source/blender/windowmanager/intern/wm.c61
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c7
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c8
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c152
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c353
-rw-r--r--source/blender/windowmanager/intern/wm_files.c465
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c44
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c24
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c883
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c71
-rw-r--r--source/blender/windowmanager/intern/wm_jobs.c10
-rw-r--r--source/blender/windowmanager/intern/wm_keymap.c93
-rw-r--r--source/blender/windowmanager/intern/wm_operator_props.c109
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c1048
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c5
-rw-r--r--source/blender/windowmanager/intern/wm_stereo.c26
-rw-r--r--source/blender/windowmanager/intern/wm_tooltip.c106
-rw-r--r--source/blender/windowmanager/intern/wm_window.c282
-rw-r--r--source/blender/windowmanager/wm.h18
-rw-r--r--source/blender/windowmanager/wm_event_system.h8
-rw-r--r--source/blender/windowmanager/wm_event_types.h2
-rw-r--r--source/blender/windowmanager/wm_files.h5
-rw-r--r--source/blender/windowmanager/wm_window.h3
27 files changed, 2575 insertions, 1361 deletions
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index b6245a8c0d1..50f99251489 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -39,6 +39,7 @@ set(INC
../nodes
../render/extern/include
../../gameengine/BlenderRoutines
+ ../../../intern/clog
../../../intern/ghost
../../../intern/guardedalloc
../../../intern/glew-mx
@@ -60,12 +61,14 @@ set(SRC
intern/wm_files.c
intern/wm_files_link.c
intern/wm_gesture.c
+ intern/wm_gesture_ops.c
intern/wm_init_exit.c
intern/wm_jobs.c
intern/wm_keymap.c
intern/wm_operator_props.c
intern/wm_operators.c
intern/wm_subwindow.c
+ intern/wm_tooltip.c
intern/wm_window.c
intern/wm_stereo.c
@@ -100,16 +103,6 @@ if(WITH_OPENCOLLADA)
add_definitions(-DWITH_COLLADA)
endif()
-if(WITH_CODEC_QUICKTIME)
- list(APPEND INC
- ../quicktime
- )
- list(APPEND INC_SYS
- ${QUICKTIME_INCLUDE_DIRS}
- )
- add_definitions(-DWITH_QUICKTIME)
-endif()
-
if(WITH_CODEC_FFMPEG)
list(APPEND INC_SYS
${FFMPEG_INCLUDE_DIRS}
@@ -128,10 +121,6 @@ if(WITH_PYTHON)
../python
)
add_definitions(-DWITH_PYTHON)
-
- if(WITH_PYTHON_SECURITY)
- add_definitions(-DWITH_PYTHON_SECURITY)
- endif()
endif()
if(WITH_GAMEENGINE)
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 0f66ca98fbe..662272f3446 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -102,7 +102,8 @@ enum {
};
struct wmWindow *WM_window_open(struct bContext *C, const struct rcti *rect);
-struct wmWindow *WM_window_open_temp(struct bContext *C, const struct rcti *rect_init, int type);
+struct wmWindow *WM_window_open_temp(struct bContext *C, int x, int y, int sizex, int sizey, int type);
+void WM_window_set_dpi(wmWindow *win);
/* returns true if draw method is triple buffer */
bool WM_is_draw_triple(struct wmWindow *win);
@@ -115,7 +116,7 @@ void WM_file_autoexec_init(const char *filepath);
bool WM_file_read(struct bContext *C, const char *filepath, struct ReportList *reports);
void WM_autosave_init(struct wmWindowManager *wm);
void WM_recover_last_session(struct bContext *C, struct ReportList *reports);
-void WM_file_tag_modified(const struct bContext *C);
+void WM_file_tag_modified(void);
void WM_lib_reload(struct Library *lib, struct bContext *C, struct ReportList *reports);
@@ -185,9 +186,9 @@ enum {
struct wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes);
/* mouse */
-void WM_event_add_mousemove(struct bContext *C);
-bool WM_modal_tweak_exit(const struct wmEvent *event, int tweak_event);
-bool WM_event_is_absolute(const struct wmEvent *event);
+void WM_event_add_mousemove(const struct bContext *C);
+bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event);
+bool WM_event_is_last_mousemove(const struct wmEvent *event);
#ifdef WITH_INPUT_NDOF
/* 3D mouse */
@@ -204,11 +205,11 @@ void WM_report_banner_show(void);
void WM_report(ReportType type, const char *message);
void WM_reportf(ReportType type, const char *format, ...) ATTR_PRINTF_FORMAT(2, 3);
-void wm_event_add_ex(
+struct wmEvent *wm_event_add_ex(
struct wmWindow *win, const struct wmEvent *event_to_add,
const struct wmEvent *event_to_add_after)
ATTR_NONNULL(1, 2);
-void wm_event_add(
+struct wmEvent *wm_event_add(
struct wmWindow *win, const struct wmEvent *event_to_add)
ATTR_NONNULL(1, 2);
@@ -228,6 +229,7 @@ void WM_operator_view3d_unit_defaults(struct bContext *C, struct wmOperator *op
int WM_operator_smooth_viewtx_get(const struct wmOperator *op);
int WM_menu_invoke_ex(struct bContext *C, struct wmOperator *op, int opcontext);
int WM_menu_invoke (struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
+void WM_menu_name_call(struct bContext *C, const char *menu_name, short context);
int WM_enum_search_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
/* invoke callback, confirm menu + exec */
int WM_operator_confirm (struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
@@ -252,6 +254,7 @@ int WM_operator_confirm_message(struct bContext *C, struct wmOperator *o
/* operator api */
void WM_operator_free (struct wmOperator *op);
+void WM_operator_free_all_after(wmWindowManager *wm, struct wmOperator *op);
void WM_operator_type_set(struct wmOperator *op, struct wmOperatorType *ot);
void WM_operator_stack_clear(struct wmWindowManager *wm);
void WM_operator_handlers_clear(wmWindowManager *wm, struct wmOperatorType *ot);
@@ -276,6 +279,7 @@ int WM_operator_call (struct bContext *C, struct wmOperator *op);
int WM_operator_call_notest(struct bContext *C, struct wmOperator *op);
int WM_operator_repeat (struct bContext *C, struct wmOperator *op);
bool WM_operator_repeat_check(const struct bContext *C, struct wmOperator *op);
+bool WM_operator_is_repeat(const struct bContext *C, const struct wmOperator *op);
int WM_operator_name_call_ptr(struct bContext *C, struct wmOperatorType *ot, short context, struct PointerRNA *properties);
int WM_operator_name_call(struct bContext *C, const char *opstring, short context, struct PointerRNA *properties);
int WM_operator_call_py(struct bContext *C, struct wmOperatorType *ot, short context, struct PointerRNA *properties, struct ReportList *reports, const bool is_undo);
@@ -305,9 +309,18 @@ void WM_operator_properties_filesel(
void WM_operator_properties_border(struct wmOperatorType *ot);
void WM_operator_properties_border_to_rcti(struct wmOperator *op, struct rcti *rect);
void WM_operator_properties_border_to_rctf(struct wmOperator *op, rctf *rect);
-void WM_operator_properties_gesture_border(struct wmOperatorType *ot, bool extend);
-void WM_operator_properties_mouse_select(struct wmOperatorType *ot);
+void WM_operator_properties_gesture_border_ex(struct wmOperatorType *ot, bool deselect, bool extend);
+void WM_operator_properties_gesture_border(struct wmOperatorType *ot);
+void WM_operator_properties_gesture_border_select(struct wmOperatorType *ot);
+void WM_operator_properties_gesture_border_zoom(struct wmOperatorType *ot);
+void WM_operator_properties_gesture_lasso_ex(struct wmOperatorType *ot, bool deselect, bool extend);
+void WM_operator_properties_gesture_lasso(struct wmOperatorType *ot);
+void WM_operator_properties_gesture_lasso_select(struct wmOperatorType *ot);
void WM_operator_properties_gesture_straightline(struct wmOperatorType *ot, int cursor);
+void WM_operator_properties_gesture_circle_ex(struct wmOperatorType *ot, bool deselect);
+void WM_operator_properties_gesture_circle(struct wmOperatorType *ot);
+void WM_operator_properties_gesture_circle_select(struct wmOperatorType *ot);
+void WM_operator_properties_mouse_select(struct wmOperatorType *ot);
void WM_operator_properties_select_all(struct wmOperatorType *ot);
void WM_operator_properties_select_action(struct wmOperatorType *ot, int default_action);
void WM_operator_properties_select_action_simple(struct wmOperatorType *ot, int default_action);
@@ -351,6 +364,7 @@ bool WM_operator_pystring_abbreviate(char *str, int str_len_max);
char *WM_prop_pystring_assign(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, int index);
void WM_operator_bl_idname(char *to, const char *from);
void WM_operator_py_idname(char *to, const char *from);
+bool WM_operator_py_idname_ok_or_report(struct ReportList *reports, const char *classname, const char *idname);
/* *************** uilist types ******************** */
void WM_uilisttype_init(void);
@@ -365,11 +379,12 @@ struct MenuType *WM_menutype_find(const char *idname, bool quiet);
bool WM_menutype_add(struct MenuType *mt);
void WM_menutype_freelink(struct MenuType *mt);
void WM_menutype_free(void);
+bool WM_menutype_poll(struct bContext *C, struct MenuType *mt);
- /* default operator callbacks for border/circle/lasso */
-int WM_border_select_invoke (struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
-int WM_border_select_modal (struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
-void WM_border_select_cancel(struct bContext *C, struct wmOperator *op);
+/* wm_gesture_ops.c */
+int WM_gesture_border_invoke (struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
+int WM_gesture_border_modal (struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
+void WM_gesture_border_cancel(struct bContext *C, struct wmOperator *op);
int WM_gesture_circle_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
int WM_gesture_circle_modal(struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
void WM_gesture_circle_cancel(struct bContext *C, struct wmOperator *op);
@@ -493,6 +508,11 @@ void WM_progress_set(struct wmWindow *win, float progress);
void WM_progress_clear(struct wmWindow *win);
/* Draw (for screenshot) */
+void *WM_draw_cb_activate(
+ struct wmWindow *win,
+ void(*draw)(const struct wmWindow *, void *),
+ void *customdata);
+void WM_draw_cb_exit(struct wmWindow *win, void *handle);
void WM_redraw_windows(struct bContext *C);
void WM_main_playanim(int argc, const char **argv);
@@ -518,6 +538,17 @@ bool WM_event_is_tablet(const struct wmEvent *event);
bool WM_event_is_ime_switch(const struct wmEvent *event);
#endif
+/* wm_tooltip.c */
+typedef struct ARegion *(*wmTooltipInitFn)(struct bContext *, struct ARegion *, bool *);
+
+void WM_tooltip_timer_init(
+ struct bContext *C, struct wmWindow *win, struct ARegion *ar,
+ wmTooltipInitFn init);
+void WM_tooltip_timer_clear(struct bContext *C, struct wmWindow *win);
+void WM_tooltip_clear(struct bContext *C, struct wmWindow *win);
+void WM_tooltip_init(struct bContext *C, struct wmWindow *win);
+void WM_tooltip_refresh(struct bContext *C, struct wmWindow *win);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/windowmanager/WM_keymap.h b/source/blender/windowmanager/WM_keymap.h
index a6413369a8f..b4d4d55479d 100644
--- a/source/blender/windowmanager/WM_keymap.h
+++ b/source/blender/windowmanager/WM_keymap.h
@@ -68,7 +68,7 @@ wmKeyMapItem *WM_keymap_add_menu_pie(struct wmKeyMap *keymap, const char *idname
int val, int modifier, int keymodifier);
bool WM_keymap_remove_item(struct wmKeyMap *keymap, struct wmKeyMapItem *kmi);
-int WM_keymap_item_to_string(wmKeyMapItem *kmi, const bool compact, const int len, char *r_str);
+int WM_keymap_item_to_string(wmKeyMapItem *kmi, const bool compact, char *result, const int result_len);
wmKeyMap *WM_keymap_list_find(ListBase *lb, const char *idname, int spaceid, int regionid);
wmKeyMap *WM_keymap_find(struct wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid);
@@ -76,21 +76,24 @@ wmKeyMap *WM_keymap_find_all(const struct bContext *C, const char *idname, int s
wmKeyMap *WM_keymap_active(struct wmWindowManager *wm, struct wmKeyMap *keymap);
wmKeyMap *WM_keymap_guess_opname(const struct bContext *C, const char *opname);
bool WM_keymap_remove(struct wmKeyConfig *keyconfig, struct wmKeyMap *keymap);
-
+bool WM_keymap_poll(struct bContext *C, struct wmKeyMap *keymap);
+
wmKeyMapItem *WM_keymap_item_find_id(struct wmKeyMap *keymap, int id);
int WM_keymap_item_compare(struct wmKeyMapItem *k1, struct wmKeyMapItem *k2);
/* Modal Keymap */
int WM_modalkeymap_items_to_string(
- struct wmKeyMap *km, const int propvalue, const bool compact, const int len, char *r_str);
+ struct wmKeyMap *km, const int propvalue, const bool compact,
+ char *result, const int result_len);
int WM_modalkeymap_operator_items_to_string(
- struct wmOperatorType *ot, const int propvalue, const bool compact, const int len, char *r_str);
+ struct wmOperatorType *ot, const int propvalue, const bool compact,
+ char *result, const int result_len);
char *WM_modalkeymap_operator_items_to_string_buf(
struct wmOperatorType *ot, const int propvalue, const bool compact,
- const int max_len, int *r_available_len, char **r_str);
+ const int max_len, int *r_available_len, char **r_result);
-wmKeyMap *WM_modalkeymap_add(struct wmKeyConfig *keyconf, const char *idname, struct EnumPropertyItem *items);
+wmKeyMap *WM_modalkeymap_add(struct wmKeyConfig *keyconf, const char *idname, const struct EnumPropertyItem *items);
wmKeyMap *WM_modalkeymap_get(struct wmKeyConfig *keyconf, const char *idname);
wmKeyMapItem *WM_modalkeymap_add_item(struct wmKeyMap *km, int type, int val, int modifier, int keymodifier, int value);
wmKeyMapItem *WM_modalkeymap_add_item_str(struct wmKeyMap *km, int type, int val, int modifier, int keymodifier, const char *value);
@@ -109,14 +112,16 @@ int WM_keymap_map_type_get(struct wmKeyMapItem *kmi);
const char *WM_key_event_string(const short type, const bool compact);
int WM_keymap_item_raw_to_string(
const short shift, const short ctrl, const short alt, const short oskey, const short keymodifier,
- const short val, const short type, const bool compact, const int len, char *r_str);
+ const short val, const short type, const bool compact,
+ char *result, const int result_len);
wmKeyMapItem *WM_key_event_operator(
const struct bContext *C, const char *opname, int opcontext,
struct IDProperty *properties, const bool is_hotkey,
struct wmKeyMap **r_keymap);
-char *WM_key_event_operator_string(
+char *WM_key_event_operator_string(
const struct bContext *C, const char *opname, int opcontext,
- struct IDProperty *properties, const bool is_strict, int len, char *r_str);
+ struct IDProperty *properties, const bool is_strict,
+ char *result, const int result_len);
const char *WM_bool_as_string(bool test);
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index cd46e24264d..8c94c2ff043 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -409,11 +409,17 @@ typedef struct wmNotifier {
typedef struct wmGesture {
struct wmGesture *next, *prev;
int event_type; /* event->type */
- int mode; /* for modal callback */
int type; /* gesture type define */
int swinid; /* initial subwindow id where it started */
int points; /* optional, amount of points stored */
- int size; /* optional, maximum amount of points stored */
+ int points_alloc; /* optional, maximum amount of points stored */
+ int modal_state;
+
+ /* For modal operators which may be running idle, waiting for an event to activate the gesture.
+ * Typically this is set when the user is click-dragging the gesture (border and circle select for eg). */
+ uint is_active : 1;
+ /* Use for gestures that support both immediate or delayed activation. */
+ uint wait_for_input : 1;
void *customdata;
/* customdata for border is a recti */
@@ -423,6 +429,7 @@ typedef struct wmGesture {
/* free pointer to use for operator allocs (if set, its freed on exit)*/
void *userdata;
+ bool userdata_free;
} wmGesture;
/* ************** wmEvent ************************ */
@@ -454,8 +461,9 @@ typedef struct wmEvent {
short keymodifier; /* rawkey modifier */
/* set in case a KM_PRESS went by unhandled */
- short check_click;
-
+ char check_click;
+ char is_motion_absolute;
+
/* keymap item, set by handler (weak?) */
const char *keymap_idname;
@@ -500,6 +508,10 @@ typedef struct wmNDOFMotionData {
} wmNDOFMotionData;
#endif /* WITH_INPUT_NDOF */
+typedef enum { /* Timer flags */
+ WM_TIMER_NO_FREE_CUSTOM_DATA = 1 << 0, /* Do not attempt to free customdata pointer even if non-NULL. */
+} wmTimerFlags;
+
typedef struct wmTimer {
struct wmTimer *next, *prev;
@@ -507,6 +519,7 @@ typedef struct wmTimer {
double timestep; /* set by timer user */
int event_type; /* set by timer user, goes to event system */
+ wmTimerFlags flags; /* Various flags controlling timer options, see below. */
void *customdata; /* set by timer user, to allow custom values */
double duration; /* total running time in seconds */
@@ -515,7 +528,7 @@ typedef struct wmTimer {
double ltime; /* internal, last time timer was activated */
double ntime; /* internal, next time we want to activate the timer */
double stime; /* internal, when the timer started */
- int sleep; /* internal, put timers to sleep when needed */
+ bool sleep; /* internal, put timers to sleep when needed */
} wmTimer;
typedef struct wmOperatorType {
@@ -542,7 +555,15 @@ typedef struct wmOperatorType {
* canceled due to some external reason, cancel is called
* - see defines below for return values */
int (*invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT;
+
+ /* Called when a modal operator is canceled (not used often).
+ * Internal cleanup can be done here if needed. */
void (*cancel)(struct bContext *, struct wmOperator *);
+
+ /* Modal is used for operators which continuously run, eg:
+ * fly mode, knife tool, circle select are all examples of modal operators.
+ * Modal operators can handle events which would normally access other operators,
+ * they keep running until they don't return `OPERATOR_RUNNING_MODAL`. */
int (*modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT;
/* verify if the operator can be executed in the current context, note
@@ -674,6 +695,23 @@ typedef struct wmDropBox {
} wmDropBox;
+/**
+ * Struct to store tool-tip timer and possible creation if the time is reached.
+ * Allows UI code to call #WM_tooltip_timer_init without each user having to handle the timer.
+ */
+typedef struct wmTooltipState {
+ /** Create tooltip on this event. */
+ struct wmTimer *timer;
+ /** The region the tooltip is created in. */
+ struct ARegion *region_from;
+ /** The tooltip region. */
+ struct ARegion *region;
+ /** Create the tooltip region (assign to 'region'). */
+ struct ARegion *(*init)(struct bContext *, struct ARegion *, bool *r_exit_on_event);
+ /** Exit on any event, not needed for buttons since their highlight state is used. */
+ bool exit_on_event;
+} wmTooltipState;
+
/* *************** migrated stuff, clean later? ************** */
typedef struct RecentFile {
@@ -681,6 +719,14 @@ typedef struct RecentFile {
char *filepath;
} RecentFile;
+/* Logging */
+struct CLG_LogRef;
+/* wm_init_exit.c */
+extern struct CLG_LogRef *WM_LOG_OPERATORS;
+extern struct CLG_LogRef *WM_LOG_HANDLERS;
+extern struct CLG_LogRef *WM_LOG_EVENTS;
+extern struct CLG_LogRef *WM_LOG_KEYMAPS;
+
#ifdef __cplusplus
}
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index b76a1f1d422..8be7555b34a 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -61,6 +61,7 @@
#include "wm.h"
#include "ED_screen.h"
+#include "BKE_undo_system.h"
#ifdef WITH_PYTHON
#include "BPY_extern.h"
@@ -107,6 +108,17 @@ void WM_operator_free(wmOperator *op)
MEM_freeN(op);
}
+void WM_operator_free_all_after(wmWindowManager *wm, struct wmOperator *op)
+{
+ op = op->next;
+ while (op != NULL) {
+ wmOperator *op_next = op->next;
+ BLI_remlink(&wm->operators, op);
+ WM_operator_free(op);
+ op = op_next;
+ }
+}
+
/**
* Use with extreme care!,
* properties, customdata etc - must be compatible.
@@ -149,18 +161,23 @@ static void wm_reports_free(wmWindowManager *wm)
void wm_operator_register(bContext *C, wmOperator *op)
{
wmWindowManager *wm = CTX_wm_manager(C);
- int tot;
+ int tot = 0;
BLI_addtail(&wm->operators, op);
- tot = BLI_listbase_count(&wm->operators);
-
- while (tot > MAX_OP_REGISTERED) {
- wmOperator *opt = wm->operators.first;
- BLI_remlink(&wm->operators, opt);
- WM_operator_free(opt);
- tot--;
+
+ /* only count registered operators */
+ while (op) {
+ wmOperator *op_prev = op->prev;
+ if (op->type->flag & OPTYPE_REGISTER) {
+ tot += 1;
+ }
+ if (tot > MAX_OP_REGISTERED) {
+ BLI_remlink(&wm->operators, op);
+ WM_operator_free(op);
+ }
+ op = op_prev;
}
-
+
/* so the console is redrawn */
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL);
WM_event_add_notifier(C, NC_WM | ND_HISTORY, NULL);
@@ -321,6 +338,14 @@ void WM_menutype_free(void)
menutypes_hash = NULL;
}
+bool WM_menutype_poll(bContext *C, MenuType *mt)
+{
+ if (mt->poll != NULL) {
+ return mt->poll(C, mt);
+ }
+ return true;
+}
+
/* ****************************************** */
void WM_keymap_init(bContext *C)
@@ -414,7 +439,7 @@ void wm_clear_default_size(bContext *C)
/* on startup, it adds all data, for matching */
void wm_add_default(bContext *C)
{
- wmWindowManager *wm = BKE_libblock_alloc(CTX_data_main(C), ID_WM, "WinMan");
+ wmWindowManager *wm = BKE_libblock_alloc(CTX_data_main(C), ID_WM, "WinMan", 0);
wmWindow *win;
bScreen *screen = CTX_wm_screen(C); /* XXX from file read hrmf */
@@ -461,25 +486,33 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
WM_drag_free_list(&wm->drags);
wm_reports_free(wm);
-
+
+ if (wm->undo_stack) {
+ BKE_undosys_stack_destroy(wm->undo_stack);
+ wm->undo_stack = NULL;
+ }
+
if (C && CTX_wm_manager(C) == wm) CTX_wm_manager_set(C, NULL);
}
void wm_close_and_free_all(bContext *C, ListBase *wmlist)
{
- Main *bmain = CTX_data_main(C);
wmWindowManager *wm;
-
+
while ((wm = wmlist->first)) {
wm_close_and_free(C, wm);
BLI_remlink(wmlist, wm);
- BKE_libblock_free_data(bmain, &wm->id);
+ BKE_libblock_free_data(&wm->id, true);
MEM_freeN(wm);
}
}
void WM_main(bContext *C)
{
+ /* Single refresh before handling events.
+ * This ensures we don't run operators before the depsgraph has been evaluated. */
+ wm_event_do_refresh_wm_and_depsgraph(C);
+
while (1) {
/* get events from ghost, handle window events, add to window queues */
diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c
index d4c3928bd6c..ad5e83ceda7 100644
--- a/source/blender/windowmanager/intern/wm_cursors.c
+++ b/source/blender/windowmanager/intern/wm_cursors.c
@@ -213,13 +213,10 @@ void WM_cursor_grab_enable(wmWindow *win, bool wrap, bool hide, int bounds[4])
}
if ((G.debug & G_DEBUG) == 0) {
if (win->ghostwin) {
- const GHOST_TabletData *tabletdata = GHOST_GetTabletData(win->ghostwin);
-
/* Note: There is no tabletdata on Windows if no tablet device is connected. */
- if (!tabletdata)
- GHOST_SetCursorGrab(win->ghostwin, mode, bounds, NULL);
- else if (tabletdata->Active == GHOST_kTabletModeNone)
+ if (win->eventstate->is_motion_absolute == false) {
GHOST_SetCursorGrab(win->ghostwin, mode, bounds, NULL);
+ }
win->grabcursor = mode;
}
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index 3a53906a8e8..dd01efdb4c4 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -185,7 +185,7 @@ void WM_drag_free_list(struct ListBase *lb)
}
}
-static const char *dropbox_active(bContext *C, ListBase *handlers, wmDrag *drag, wmEvent *event)
+static const char *dropbox_active(bContext *C, ListBase *handlers, wmDrag *drag, const wmEvent *event)
{
wmEventHandler *handler = handlers->first;
for (; handler; handler = handler->next) {
@@ -203,7 +203,7 @@ static const char *dropbox_active(bContext *C, ListBase *handlers, wmDrag *drag,
}
/* return active operator name when mouse is in box */
-static const char *wm_dropbox_active(bContext *C, wmDrag *drag, wmEvent *event)
+static const char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
ScrArea *sa = CTX_wm_area(C);
@@ -223,7 +223,7 @@ static const char *wm_dropbox_active(bContext *C, wmDrag *drag, wmEvent *event)
}
-static void wm_drop_operator_options(bContext *C, wmDrag *drag, wmEvent *event)
+static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
const int winsize_x = WM_window_pixels_x(win);
@@ -253,7 +253,7 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, wmEvent *event)
}
/* called in inner handler loop, region context */
-void wm_drags_check_ops(bContext *C, wmEvent *event)
+void wm_drags_check_ops(bContext *C, const wmEvent *event)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmDrag *drag;
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 77ffa46b990..e1f21699057 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -128,10 +128,10 @@ static bool wm_area_test_invalid_backbuf(ScrArea *sa)
if (sa->spacetype == SPACE_VIEW3D)
return (((View3D *)sa->spacedata.first)->flag & V3D_INVALID_BACKBUF) != 0;
else
- return 1;
+ return true;
}
-static void wm_region_test_render_do_draw(bScreen *screen, ScrArea *sa, ARegion *ar)
+static void wm_region_test_render_do_draw(const bScreen *screen, ScrArea *sa, ARegion *ar)
{
/* tag region for redraw from render engine preview running inside of it */
if (sa->spacetype == SPACE_VIEW3D) {
@@ -157,6 +157,46 @@ static void wm_region_test_render_do_draw(bScreen *screen, ScrArea *sa, ARegion
/********************** draw all **************************/
/* - reference method, draw all each time */
+typedef struct WindowDrawCB {
+ struct WindowDrawCB *next, *prev;
+
+ void(*draw)(const struct wmWindow *, void *);
+ void *customdata;
+
+} WindowDrawCB;
+
+void *WM_draw_cb_activate(
+ wmWindow *win,
+ void(*draw)(const struct wmWindow *, void *),
+ void *customdata)
+{
+ WindowDrawCB *wdc = MEM_callocN(sizeof(*wdc), "WindowDrawCB");
+
+ BLI_addtail(&win->drawcalls, wdc);
+ wdc->draw = draw;
+ wdc->customdata = customdata;
+
+ return wdc;
+}
+
+void WM_draw_cb_exit(wmWindow *win, void *handle)
+{
+ for (WindowDrawCB *wdc = win->drawcalls.first; wdc; wdc = wdc->next) {
+ if (wdc == (WindowDrawCB *)handle) {
+ BLI_remlink(&win->drawcalls, wdc);
+ MEM_freeN(wdc);
+ return;
+ }
+ }
+}
+
+static void wm_draw_callbacks(wmWindow *win)
+{
+ for (WindowDrawCB *wdc = win->drawcalls.first; wdc; wdc = wdc->next) {
+ wdc->draw(win, wdc->customdata);
+ }
+}
+
static void wm_method_draw_full(bContext *C, wmWindow *win)
{
bScreen *screen = win->screen;
@@ -181,8 +221,9 @@ static void wm_method_draw_full(bContext *C, wmWindow *win)
CTX_wm_area_set(C, NULL);
}
- ED_screen_draw(win);
- win->screen->do_draw = false;
+ ED_screen_draw_edges(win);
+ screen->do_draw = false;
+ wm_draw_callbacks(win);
/* draw overlapping regions */
for (ar = screen->regionbase.first; ar; ar = ar->next) {
@@ -318,17 +359,19 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange)
/* after area regions so we can do area 'overlay' drawing */
if (screen->do_draw) {
- ED_screen_draw(win);
- win->screen->do_draw = false;
+ ED_screen_draw_edges(win);
+ screen->do_draw = false;
+ wm_draw_callbacks(win);
if (exchange)
screen->swap = WIN_FRONT_OK;
}
else if (exchange) {
if (screen->swap == WIN_FRONT_OK) {
- ED_screen_draw(win);
- win->screen->do_draw = false;
+ ED_screen_draw_edges(win);
+ screen->do_draw = false;
screen->swap = WIN_BOTH_OK;
+ wm_draw_callbacks(win);
}
else if (screen->swap == WIN_BACK_OK)
screen->swap = WIN_FRONT_OK;
@@ -514,7 +557,6 @@ static void wm_draw_region_blend(wmWindow *win, ARegion *ar, wmDrawTriple *tripl
static void wm_method_draw_triple(bContext *C, wmWindow *win)
{
wmWindowManager *wm = CTX_wm_manager(C);
- wmDrawTriple *triple;
wmDrawData *dd, *dd_next, *drawdata = win->drawdata.first;
bScreen *screen = win->screen;
ScrArea *sa;
@@ -554,7 +596,7 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win)
MEM_freeN(dd);
}
- triple = drawdata->triple;
+ wmDrawTriple *triple = drawdata->triple;
/* draw marked area regions */
for (sa = screen->areabase.first; sa; sa = sa->next) {
@@ -562,7 +604,6 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win)
for (ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->swinid && ar->do_draw) {
-
if (ar->overlap == false) {
CTX_wm_region_set(C, ar);
ED_region_do_draw(C, ar);
@@ -622,8 +663,9 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win)
}
/* after area regions so we can do area 'overlay' drawing */
- ED_screen_draw(win);
+ ED_screen_draw_edges(win);
win->screen->do_draw = false;
+ wm_draw_callbacks(win);
/* draw floating regions (menus) */
for (ar = screen->regionbase.first; ar; ar = ar->next) {
@@ -645,7 +687,7 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win)
}
}
-static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, StereoViews sview)
+static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoViews sview)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmDrawData *drawdata;
@@ -792,9 +834,10 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, StereoVi
}
/* after area regions so we can do area 'overlay' drawing */
- ED_screen_draw(win);
+ ED_screen_draw_edges(win);
if (sview == STEREO_RIGHT_ID)
win->screen->do_draw = false;
+ wm_draw_callbacks(win);
/* draw floating regions (menus) */
for (ar = screen->regionbase.first; ar; ar = ar->next) {
@@ -826,11 +869,12 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, StereoVi
/* quick test to prevent changing window drawable */
static bool wm_draw_update_test_window(wmWindow *win)
{
+ const bScreen *screen = win->screen;
ScrArea *sa;
ARegion *ar;
bool do_draw = false;
- for (ar = win->screen->regionbase.first; ar; ar = ar->next) {
+ for (ar = screen->regionbase.first; ar; ar = ar->next) {
if (ar->do_draw_overlay) {
wm_tag_redraw_overlay(win, ar);
ar->do_draw_overlay = false;
@@ -839,9 +883,9 @@ static bool wm_draw_update_test_window(wmWindow *win)
do_draw = true;
}
- for (sa = win->screen->areabase.first; sa; sa = sa->next) {
+ for (sa = screen->areabase.first; sa; sa = sa->next) {
for (ar = sa->regionbase.first; ar; ar = ar->next) {
- wm_region_test_render_do_draw(win->screen, sa, ar);
+ wm_region_test_render_do_draw(screen, sa, ar);
if (ar->swinid && ar->do_draw)
do_draw = true;
@@ -849,43 +893,31 @@ static bool wm_draw_update_test_window(wmWindow *win)
}
if (do_draw)
- return 1;
+ return true;
- if (win->screen->do_refresh)
- return 1;
- if (win->screen->do_draw)
- return 1;
- if (win->screen->do_draw_gesture)
- return 1;
- if (win->screen->do_draw_paintcursor)
- return 1;
- if (win->screen->do_draw_drag)
- return 1;
+ if (screen->do_refresh)
+ return true;
+ if (screen->do_draw)
+ return true;
+ if (screen->do_draw_gesture)
+ return true;
+ if (screen->do_draw_paintcursor)
+ return true;
+ if (screen->do_draw_drag)
+ return true;
- return 0;
+ return false;
}
static int wm_automatic_draw_method(wmWindow *win)
{
- /* Ideally all cards would work well with triple buffer, since if it works
- * well gives the least redraws and is considerably faster at partial redraw
- * for sculpting or drawing overlapping menus. For typically lower end cards
- * copy to texture is slow though and so we use overlap instead there. */
-
+ /* We assume all supported GPUs now support triple buffer well. */
if (win->drawmethod == USER_DRAW_AUTOMATIC) {
- /* Windows software driver darkens color on each redraw */
- if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_WIN, GPU_DRIVER_SOFTWARE))
- return USER_DRAW_OVERLAP_FLIP;
- else if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_UNIX, GPU_DRIVER_SOFTWARE))
- return USER_DRAW_OVERLAP;
- /* drawing lower color depth again degrades colors each time */
- else if (GPU_color_depth() < 24)
- return USER_DRAW_OVERLAP;
- else
- return USER_DRAW_TRIPLE;
+ return USER_DRAW_TRIPLE;
}
- else
+ else {
return win->drawmethod;
+ }
}
bool WM_is_draw_triple(wmWindow *win)
@@ -916,7 +948,6 @@ void wm_draw_update(bContext *C)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win;
- int drawmethod;
#ifdef WITH_OPENSUBDIV
BKE_subsurf_free_unused_buffers();
@@ -926,15 +957,14 @@ void wm_draw_update(bContext *C)
for (win = wm->windows.first; win; win = win->next) {
#ifdef WIN32
- if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
- GHOST_TWindowState state = GHOST_GetWindowState(win->ghostwin);
-
- if (state == GHOST_kWindowStateMinimized) {
- /* do not update minimized windows, it gives issues on intel drivers (see [#33223])
- * anyway, it seems logical to skip update for invisible windows
- */
- continue;
- }
+ GHOST_TWindowState state = GHOST_GetWindowState(win->ghostwin);
+
+ if (state == GHOST_kWindowStateMinimized) {
+ /* do not update minimized windows, gives issues on Intel (see T33223)
+ * and AMD (see T50856). it seems logical to skip update for invisible
+ * window anyway.
+ */
+ continue;
}
#endif
if (win->drawmethod != U.wmdrawmethod) {
@@ -943,16 +973,18 @@ void wm_draw_update(bContext *C)
}
if (wm_draw_update_test_window(win)) {
+ bScreen *screen = win->screen;
+
CTX_wm_window_set(C, win);
/* sets context window+screen */
wm_window_make_drawable(wm, win);
/* notifiers for screen redraw */
- if (win->screen->do_refresh)
+ if (screen->do_refresh)
ED_screen_refresh(wm, win);
- drawmethod = wm_automatic_draw_method(win);
+ int drawmethod = wm_automatic_draw_method(win);
if (win->drawfail)
wm_method_draw_overlap_all(C, win, 0);
@@ -973,9 +1005,9 @@ void wm_draw_update(bContext *C)
}
}
- win->screen->do_draw_gesture = false;
- win->screen->do_draw_paintcursor = false;
- win->screen->do_draw_drag = false;
+ screen->do_draw_gesture = false;
+ screen->do_draw_paintcursor = false;
+ screen->do_draw_drag = false;
wm_window_swap_buffers(win);
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 9571dfd75d6..bc4dc5217cb 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -43,6 +43,8 @@
#include "MEM_guardedalloc.h"
+#include "CLG_log.h"
+
#include "GHOST_C-api.h"
#include "BLI_blenlib.h"
@@ -65,6 +67,7 @@
#include "ED_screen.h"
#include "ED_view3d.h"
#include "ED_util.h"
+#include "ED_undo.h"
#include "RNA_access.h"
@@ -83,6 +86,9 @@
#include "RNA_enum_types.h"
+/* Motion in pixels allowed before we don't consider single/double click. */
+#define WM_EVENT_CLICK_WIGGLE_ROOM 2
+
static void wm_notifier_clear(wmNotifier *note);
static void update_tablet_data(wmWindow *win, wmEvent *event);
@@ -91,7 +97,7 @@ static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA
/* ************ event management ************** */
-void wm_event_add_ex(wmWindow *win, const wmEvent *event_to_add, const wmEvent *event_to_add_after)
+wmEvent *wm_event_add_ex(wmWindow *win, const wmEvent *event_to_add, const wmEvent *event_to_add_after)
{
wmEvent *event = MEM_mallocN(sizeof(wmEvent), "wmEvent");
@@ -99,6 +105,13 @@ void wm_event_add_ex(wmWindow *win, const wmEvent *event_to_add, const wmEvent *
update_tablet_data(win, event);
+ if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ /* We could have a preference to support relative tablet motion (we can't detect that). */
+ event->is_motion_absolute = (
+ (event->tablet_data != NULL) &&
+ (event->tablet_data->Active != GHOST_kTabletModeNone));
+ }
+
if (event_to_add_after == NULL) {
BLI_addtail(&win->queue, event);
}
@@ -106,11 +119,12 @@ void wm_event_add_ex(wmWindow *win, const wmEvent *event_to_add, const wmEvent *
/* note, strictly speaking this breaks const-correctness, however we're only changing 'next' member */
BLI_insertlinkafter(&win->queue, (void *)event_to_add_after, event);
}
+ return event;
}
-void wm_event_add(wmWindow *win, const wmEvent *event_to_add)
+wmEvent *wm_event_add(wmWindow *win, const wmEvent *event_to_add)
{
- wm_event_add_ex(win, event_to_add, NULL);
+ return wm_event_add_ex(win, event_to_add, NULL);
}
void wm_event_free(wmEvent *event)
@@ -263,13 +277,56 @@ static void wm_notifier_clear(wmNotifier *note)
memset(((char *)note) + sizeof(Link), 0, sizeof(*note) - sizeof(Link));
}
+/**
+ * Was part of #wm_event_do_notifiers, split out so it can be called once before entering the #WM_main loop.
+ * This ensures operators don't run before the UI and depsgraph are initialized.
+ */
+void wm_event_do_refresh_wm_and_depsgraph(bContext *C)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ uint64_t win_combine_v3d_datamask = 0;
+
+ /* combine datamasks so 1 win doesn't disable UV's in another [#26448] */
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ win_combine_v3d_datamask |= ED_view3d_screen_datamask(win->screen);
+ }
+
+ /* cached: editor refresh callbacks now, they get context */
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ ScrArea *sa;
+
+ CTX_wm_window_set(C, win);
+ for (sa = win->screen->areabase.first; sa; sa = sa->next) {
+ if (sa->do_refresh) {
+ CTX_wm_area_set(C, sa);
+ ED_area_do_refresh(C, sa);
+ }
+ }
+
+ /* XXX make lock in future, or separated derivedmesh users in scene */
+ if (G.is_rendering == false) {
+ /* depsgraph & animation: update tagged datablocks */
+ Main *bmain = CTX_data_main(C);
+
+ /* copied to set's in scene_update_tagged_recursive() */
+ win->screen->scene->customdata_mask = win_combine_v3d_datamask;
+
+ /* XXX, hack so operators can enforce datamasks [#26482], gl render */
+ win->screen->scene->customdata_mask |= win->screen->scene->customdata_mask_modal;
+
+ BKE_scene_update_tagged(bmain->eval_ctx, bmain, win->screen->scene);
+ }
+ }
+
+ CTX_wm_window_set(C, NULL);
+}
+
/* called in mainloop */
void wm_event_do_notifiers(bContext *C)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmNotifier *note, *next;
wmWindow *win;
- uint64_t win_combine_v3d_datamask = 0;
if (wm == NULL)
return;
@@ -299,13 +356,11 @@ void wm_event_do_notifiers(bContext *C)
ED_screen_set(C, note->reference); // XXX hrms, think this over!
- if (G.debug & G_DEBUG_EVENTS)
- printf("%s: screen set %p\n", __func__, note->reference);
+ CLOG_INFO(WM_LOG_EVENTS, 1, "screen set %p", note->reference);
}
else if (note->data == ND_SCREENDELETE) {
ED_screen_delete(C, note->reference); // XXX hrms, think this over!
- if (G.debug & G_DEBUG_EVENTS)
- printf("%s: screen delete %p\n", __func__, note->reference);
+ CLOG_INFO(WM_LOG_EVENTS, 1, "screen delete %p", note->reference);
}
}
}
@@ -373,39 +428,7 @@ void wm_event_do_notifiers(bContext *C)
MEM_freeN(note);
}
- /* combine datamasks so 1 win doesn't disable UV's in another [#26448] */
- for (win = wm->windows.first; win; win = win->next) {
- win_combine_v3d_datamask |= ED_view3d_screen_datamask(win->screen);
- }
-
- /* cached: editor refresh callbacks now, they get context */
- for (win = wm->windows.first; win; win = win->next) {
- ScrArea *sa;
-
- CTX_wm_window_set(C, win);
- for (sa = win->screen->areabase.first; sa; sa = sa->next) {
- if (sa->do_refresh) {
- CTX_wm_area_set(C, sa);
- ED_area_do_refresh(C, sa);
- }
- }
-
- /* XXX make lock in future, or separated derivedmesh users in scene */
- if (G.is_rendering == false) {
- /* depsgraph & animation: update tagged datablocks */
- Main *bmain = CTX_data_main(C);
-
- /* copied to set's in scene_update_tagged_recursive() */
- win->screen->scene->customdata_mask = win_combine_v3d_datamask;
-
- /* XXX, hack so operators can enforce datamasks [#26482], gl render */
- win->screen->scene->customdata_mask |= win->screen->scene->customdata_mask_modal;
-
- BKE_scene_update_tagged(bmain->eval_ctx, bmain, win->screen->scene);
- }
- }
-
- CTX_wm_window_set(C, NULL);
+ wm_event_do_refresh_wm_and_depsgraph(C);
}
static int wm_event_always_pass(const wmEvent *event)
@@ -523,14 +546,6 @@ int WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context)
return wm_operator_call_internal(C, ot, NULL, NULL, context, true);
}
-static void wm_operator_print(bContext *C, wmOperator *op)
-{
- /* context is needed for enum function */
- char *buf = WM_operator_pystring(C, op, false, true);
- puts(buf);
- MEM_freeN(buf);
-}
-
/**
* Sets the active region for this space from the context.
*
@@ -610,9 +625,14 @@ void WM_report_banner_show(void)
wm_reports->reporttimer->customdata = rti;
}
-bool WM_event_is_absolute(const wmEvent *event)
+bool WM_event_is_last_mousemove(const wmEvent *event)
{
- return (event->tablet_data != NULL);
+ while ((event = event->next)) {
+ if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ return false;
+ }
+ }
+ return true;
}
#ifdef WITH_INPUT_NDOF
@@ -684,12 +704,9 @@ static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool ca
CTX_wm_region_set(C, ar_prev);
}
}
-
+
if (retval & OPERATOR_FINISHED) {
- if (G.debug & G_DEBUG_WM) {
- /* todo - this print may double up, might want to check more flags then the FINISHED */
- wm_operator_print(C, op);
- }
+ CLOG_STR_INFO_N(WM_LOG_OPERATORS, 1, WM_operator_pystring(C, op, false, true));
if (caller_owns_reports == false) {
BKE_reports_print(op->reports, RPT_DEBUG); /* print out reports to console. */
@@ -715,15 +732,21 @@ static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool ca
*/
static bool wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
{
- return wm && (wm->op_undo_depth == 0) && (ot->flag & OPTYPE_REGISTER);
+ /* Check undo flag here since undo operators are also added to the list,
+ * to support checking if the same operator is run twice. */
+ return wm && (wm->op_undo_depth == 0) && (ot->flag & (OPTYPE_REGISTER | OPTYPE_UNDO));
}
-static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat)
+static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat, const bool store)
{
wmWindowManager *wm = CTX_wm_manager(C);
op->customdata = NULL;
+ if (store) {
+ WM_operator_last_properties_store(op);
+ }
+
/* we don't want to do undo pushes for operators that are being
* called from operators that already do an undo push. usually
* this will happen for python operators that call C operators */
@@ -769,14 +792,22 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons
return retval;
if (op->type->exec) {
- if (op->type->flag & OPTYPE_UNDO)
+ if (op->type->flag & OPTYPE_UNDO) {
wm->op_undo_depth++;
+ }
+ if (repeat) {
+ op->flag |= OP_IS_REPEAT;
+ }
retval = op->type->exec(C, op);
OPERATOR_RETVAL_CHECK(retval);
+ if (repeat) {
+ op->flag &= ~OP_IS_REPEAT;
+ }
- if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
+ if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
wm->op_undo_depth--;
+ }
}
/* XXX Disabled the repeat check to address part 2 of #31840.
@@ -786,12 +817,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons
wm_operator_reports(C, op, retval, false);
if (retval & OPERATOR_FINISHED) {
- if (store) {
- if (wm->op_undo_depth == 0) { /* not called by py script */
- WM_operator_last_properties_store(op);
- }
- }
- wm_operator_finished(C, op, repeat);
+ wm_operator_finished(C, op, repeat, store && wm->op_undo_depth == 0);
}
else if (repeat == 0) {
/* warning: modal from exec is bad practice, but avoid crashing. */
@@ -878,6 +904,20 @@ bool WM_operator_repeat_check(const bContext *UNUSED(C), wmOperator *op)
return false;
}
+bool WM_operator_is_repeat(const bContext *C, const wmOperator *op)
+{
+ /* may be in the operators list or not */
+ wmOperator *op_prev;
+ if (op->prev == NULL && op->next == NULL) {
+ wmWindowManager *wm = CTX_wm_manager(C);
+ op_prev = wm->operators.last;
+ }
+ else {
+ op_prev = op->prev;
+ }
+ return (op_prev && (op->type == op_prev->type));
+}
+
static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot,
PointerRNA *properties, ReportList *reports)
{
@@ -991,9 +1031,7 @@ bool WM_operator_last_properties_init(wmOperator *op)
IDProperty *replaceprops = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
PropertyRNA *iterprop;
- if (G.debug & G_DEBUG_WM) {
- printf("%s: loading previous properties for '%s'\n", __func__, op->type->idname);
- }
+ CLOG_INFO(WM_LOG_OPERATORS, 1, "loading previous properties for '%s'", op->type->idname);
iterprop = RNA_struct_iterator_property(op->type->srna);
@@ -1038,9 +1076,7 @@ bool WM_operator_last_properties_store(wmOperator *op)
}
if (op->properties) {
- if (G.debug & G_DEBUG_WM) {
- printf("%s: storing properties for '%s'\n", __func__, op->type->idname);
- }
+ CLOG_INFO(WM_LOG_OPERATORS, 1, "storing properties for '%s'", op->type->idname);
op->type->last_properties = IDP_CopyProperty(op->properties);
return true;
}
@@ -1089,6 +1125,9 @@ void WM_operator_last_properties_alloc(struct wmOperator *op, const char *idname
}
}
+/**
+ * Also used for exec when 'event' is NULL.
+ */
static int wm_operator_invoke(
bContext *C, wmOperatorType *ot, wmEvent *event,
PointerRNA *properties, ReportList *reports, const bool poll_only)
@@ -1104,18 +1143,21 @@ static int wm_operator_invoke(
wmOperator *op = wm_operator_create(wm, ot, properties, reports); /* if reports == NULL, they'll be initialized */
const bool is_nested_call = (wm->op_undo_depth != 0);
- op->flag |= OP_IS_INVOKE;
+ if (event != NULL) {
+ op->flag |= OP_IS_INVOKE;
+ }
/* initialize setting from previous run */
if (!is_nested_call) { /* not called by py script */
WM_operator_last_properties_init(op);
}
- if ((G.debug & G_DEBUG_HANDLERS) && ((event == NULL) || (event->type != MOUSEMOVE))) {
- printf("%s: handle evt %d win %d op %s\n",
- __func__, event ? event->type : 0, CTX_wm_screen(C)->subwinactive, ot->idname);
+ if ((event == NULL) || (event->type != MOUSEMOVE)) {
+ CLOG_INFO(WM_LOG_HANDLERS, 2,
+ "handle evt %d win %d op %s",
+ event ? event->type : 0, CTX_wm_screen(C)->subwinactive, ot->idname);
}
-
+
if (op->type->invoke && event) {
wm_region_mouse_co(C, event);
@@ -1140,9 +1182,9 @@ static int wm_operator_invoke(
}
else {
/* debug, important to leave a while, should never happen */
- printf("%s: invalid operator call '%s'\n", __func__, ot->idname);
+ CLOG_ERROR(WM_LOG_OPERATORS, "invalid operator call '%s'", op->idname);
}
-
+
/* Note, if the report is given as an argument then assume the caller will deal with displaying them
* currently python only uses this */
if (!(retval & OPERATOR_HANDLED) && (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED))) {
@@ -1154,10 +1196,8 @@ static int wm_operator_invoke(
/* do nothing, wm_operator_exec() has been called somewhere */
}
else if (retval & OPERATOR_FINISHED) {
- if (!is_nested_call) { /* not called by py script */
- WM_operator_last_properties_store(op);
- }
- wm_operator_finished(C, op, 0);
+ const bool store = !is_nested_call;
+ wm_operator_finished(C, op, false, store);
}
else if (retval & OPERATOR_RUNNING_MODAL) {
/* take ownership of reports (in case python provided own) */
@@ -1252,6 +1292,8 @@ static int wm_operator_call_internal(
switch (context) {
case WM_OP_INVOKE_DEFAULT:
case WM_OP_INVOKE_REGION_WIN:
+ case WM_OP_INVOKE_REGION_PREVIEW:
+ case WM_OP_INVOKE_REGION_CHANNELS:
case WM_OP_INVOKE_AREA:
case WM_OP_INVOKE_SCREEN:
/* window is needed for invoke, cancel operator */
@@ -1378,6 +1420,19 @@ int WM_operator_name_call(bContext *C, const char *opstring, short context, Poin
}
/**
+ * Call an existent menu. The menu can be created in C or Python.
+ */
+void WM_menu_name_call(bContext *C, const char *menu_name, short context)
+{
+ wmOperatorType *ot = WM_operatortype_find("WM_OT_call_menu", false);
+ PointerRNA ptr;
+ WM_operator_properties_create_ptr(&ptr, ot);
+ RNA_string_set(&ptr, "name", menu_name);
+ WM_operator_name_call_ptr(C, ot, context, &ptr);
+ WM_operator_properties_free(&ptr);
+}
+
+/**
* Similar to #WM_operator_name_call called with #WM_OP_EXEC_DEFAULT context.
*
* - #wmOperatorType is used instead of operator name since python already has the operator type.
@@ -1404,8 +1459,10 @@ int WM_operator_call_py(
if (is_undo && op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
wm->op_undo_depth--;
}
- else
- printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
+ else {
+ CLOG_WARN(WM_LOG_OPERATORS, "\"%s\" operator has no exec function, Python cannot call it", op->type->name);
+ }
+
#endif
/* not especially nice using undo depth here, its used so py never
@@ -1449,8 +1506,9 @@ static void wm_handler_op_context(bContext *C, wmEventHandler *handler, const wm
if (sa == NULL) {
/* when changing screen layouts with running modal handlers (like render display), this
* is not an error to print */
- if (handler->op == NULL)
- printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
+ if (handler->op == NULL) {
+ CLOG_ERROR(WM_LOG_HANDLERS, "internal error: handler (%s) has invalid area", handler->op->type->idname);
+ }
}
else {
ARegion *ar;
@@ -1733,7 +1791,7 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
/* important to run 'wm_operator_finished' before NULLing the context members */
if (retval & OPERATOR_FINISHED) {
- wm_operator_finished(C, op, 0);
+ wm_operator_finished(C, op, false, true);
handler->op = NULL;
}
else if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
@@ -1763,10 +1821,9 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
//retval &= ~OPERATOR_PASS_THROUGH;
}
}
-
}
else {
- printf("%s: error '%s' missing modal\n", __func__, op->idname);
+ CLOG_ERROR(WM_LOG_HANDLERS, "missing modal '%s'", op->idname);
}
}
else {
@@ -2042,7 +2099,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
PRINT("%s: checking '%s' ...", __func__, keymap->idname);
- if (!keymap->poll || keymap->poll(C)) {
+ if (WM_keymap_poll(C, keymap)) {
PRINT("pass\n");
@@ -2057,19 +2114,15 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
if (action & WM_HANDLER_BREAK) {
/* not always_pass here, it denotes removed handler */
-
- if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS))
- printf("%s: handled! '%s'\n", __func__, kmi->idname);
-
+ CLOG_INFO(WM_LOG_HANDLERS, 2, "handled! '%s'", kmi->idname);
break;
}
else {
if (action & WM_HANDLER_HANDLED) {
- if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS))
- printf("%s: handled - and pass on! '%s'\n", __func__, kmi->idname);
+ CLOG_INFO(WM_LOG_HANDLERS, 2, "handled - and pass on! '%s'", kmi->idname);
}
else {
- PRINT("%s: un-handled '%s'\n", __func__, kmi->idname);
+ CLOG_INFO(WM_LOG_HANDLERS, 2, "un-handled '%s'", kmi->idname);
}
}
}
@@ -2190,15 +2243,20 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
(win->eventstate->prevval == KM_PRESS) &&
(win->eventstate->check_click == true))
{
- event->val = KM_CLICK;
-
- if (G.debug & (G_DEBUG_HANDLERS)) {
- printf("%s: handling CLICK\n", __func__);
- }
+ if ((abs(event->x - win->eventstate->prevclickx)) <= WM_EVENT_CLICK_WIGGLE_ROOM &&
+ (abs(event->y - win->eventstate->prevclicky)) <= WM_EVENT_CLICK_WIGGLE_ROOM)
+ {
+ event->val = KM_CLICK;
- action |= wm_handlers_do_intern(C, event, handlers);
+ CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK");
+
+ action |= wm_handlers_do_intern(C, event, handlers);
- event->val = KM_RELEASE;
+ event->val = KM_RELEASE;
+ }
+ else {
+ win->eventstate->check_click = 0;
+ }
}
else if (event->val == KM_DBL_CLICK) {
event->val = KM_PRESS;
@@ -2412,13 +2470,14 @@ void wm_event_do_handlers(bContext *C)
if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
printf("\n%s: Handling event\n", __func__);
+
WM_event_print(event);
}
/* take care of pie event filter */
if (wm_event_pie_filter(win, event)) {
- if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
- printf("\n%s: event filtered due to pie button pressed\n", __func__);
+ if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed");
}
BLI_remlink(&win->queue, event);
wm_event_free(event);
@@ -2427,6 +2486,13 @@ void wm_event_do_handlers(bContext *C)
CTX_wm_window_set(C, win);
+ /* Clear tool-tip on mouse move. */
+ if (win->screen->tool_tip && win->screen->tool_tip->exit_on_event) {
+ if (ISMOUSE(event->type)) {
+ WM_tooltip_clear(C, win);
+ }
+ }
+
/* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
CTX_wm_area_set(C, area_event_inside(C, &event->x));
CTX_wm_region_set(C, region_event_inside(C, &event->x));
@@ -2443,7 +2509,17 @@ void wm_event_do_handlers(bContext *C)
/* fileread case */
if (CTX_wm_window(C) == NULL)
return;
-
+
+ /* check for a tooltip */
+ {
+ bScreen *screen = CTX_wm_window(C)->screen;
+ if (screen->tool_tip && screen->tool_tip->timer) {
+ if ((event->type == TIMER) && (event->customdata == screen->tool_tip->timer)) {
+ WM_tooltip_init(C, win);
+ }
+ }
+ }
+
/* check dragging, creates new event or frees, adds draw tag */
wm_event_drag_test(wm, win, event);
@@ -2685,7 +2761,7 @@ wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap
wmEventHandler *handler;
if (!keymap) {
- printf("%s: called with NULL keymap\n", __func__);
+ CLOG_WARN(WM_LOG_HANDLERS, "called with NULL keymap");
return NULL;
}
@@ -2853,7 +2929,7 @@ static void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
}
#endif
-void WM_event_add_mousemove(bContext *C)
+void WM_event_add_mousemove(const bContext *C)
{
wmWindow *window = CTX_wm_window(C);
@@ -2862,7 +2938,7 @@ void WM_event_add_mousemove(bContext *C)
/* for modal callbacks, check configuration for how to interpret exit with tweaks */
-bool WM_modal_tweak_exit(const wmEvent *event, int tweak_event)
+bool WM_event_is_modal_tweak_exit(const wmEvent *event, int tweak_event)
{
/* if the release-confirm userpref setting is enabled,
* tweak events can be canceled when mouse is released
@@ -3147,8 +3223,9 @@ static bool wm_event_is_double_click(wmEvent *event, const wmEvent *event_state)
(event_state->prevval == KM_RELEASE) &&
(event->val == KM_PRESS))
{
- if ((ISMOUSE(event->type) == false) || ((ABS(event->x - event_state->prevclickx)) <= 2 &&
- (ABS(event->y - event_state->prevclicky)) <= 2))
+ if ((ISMOUSE(event->type) == false) ||
+ ((abs(event->x - event_state->prevclickx)) <= WM_EVENT_CLICK_WIGGLE_ROOM &&
+ (abs(event->y - event_state->prevclicky)) <= WM_EVENT_CLICK_WIGGLE_ROOM))
{
if ((PIL_check_seconds_timer() - event_state->prevclicktime) * 1000 < U.dbl_click_time) {
return true;
@@ -3159,7 +3236,7 @@ static bool wm_event_is_double_click(wmEvent *event, const wmEvent *event_state)
return false;
}
-static void wm_event_add_mousemove(wmWindow *win, const wmEvent *event)
+static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event)
{
wmEvent *event_last = win->queue.last;
@@ -3169,16 +3246,13 @@ static void wm_event_add_mousemove(wmWindow *win, const wmEvent *event)
if (event_last && event_last->type == MOUSEMOVE)
event_last->type = INBETWEEN_MOUSEMOVE;
- wm_event_add(win, event);
-
- {
- wmEvent *event_new = win->queue.last;
- if (event_last == NULL) {
- event_last = win->eventstate;
- }
-
- copy_v2_v2_int(&event_new->prevx, &event_last->x);
+ wmEvent *event_new = wm_event_add(win, event);
+ if (event_last == NULL) {
+ event_last = win->eventstate;
}
+
+ copy_v2_v2_int(&event_new->prevx, &event_last->x);
+ return event_new;
}
/* windows store own event queues, no bContext here */
@@ -3205,9 +3279,14 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
GHOST_TEventCursorData *cd = customdata;
copy_v2_v2_int(&event.x, &cd->x);
+ wm_stereo3d_mouse_offset_apply(win, &event.x);
+
event.type = MOUSEMOVE;
- wm_event_add_mousemove(win, &event);
- copy_v2_v2_int(&evt->x, &event.x);
+ {
+ wmEvent *event_new = wm_event_add_mousemove(win, &event);
+ copy_v2_v2_int(&evt->x, &event_new->x);
+ evt->is_motion_absolute = event_new->is_motion_absolute;
+ }
/* also add to other window if event is there, this makes overdraws disappear nicely */
/* it remaps mousecoord to other window in event */
@@ -3219,8 +3298,11 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
copy_v2_v2_int(&oevent.x, &event.x);
oevent.type = MOUSEMOVE;
- wm_event_add_mousemove(owin, &oevent);
- copy_v2_v2_int(&oevt->x, &oevent.x);
+ {
+ wmEvent *event_new = wm_event_add_mousemove(owin, &oevent);
+ copy_v2_v2_int(&oevt->x, &event_new->x);
+ oevt->is_motion_absolute = event_new->is_motion_absolute;
+ }
}
break;
@@ -3300,8 +3382,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
/* double click test */
if (wm_event_is_double_click(&event, evt)) {
- if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS))
- printf("%s Send double click\n", __func__);
+ CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click");
event.val = KM_DBL_CLICK;
}
if (event.val == KM_PRESS) {
@@ -3355,7 +3436,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
/* ghost should do this already for key up */
if (event.utf8_buf[0]) {
- printf("%s: ghost on your platform is misbehaving, utf8 events on key up!\n", __func__);
+ CLOG_ERROR(WM_LOG_EVENTS, "ghost on your platform is misbehaving, utf8 events on key up!");
}
event.utf8_buf[0] = '\0';
}
@@ -3368,8 +3449,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
if (event.utf8_buf[0]) {
if (BLI_str_utf8_size(event.utf8_buf) == -1) {
- printf("%s: ghost detected an invalid unicode character '%d'!\n",
- __func__, (int)(unsigned char)event.utf8_buf[0]);
+ CLOG_ERROR(WM_LOG_EVENTS,
+ "ghost detected an invalid unicode character '%d'",
+ (int)(unsigned char)event.utf8_buf[0]);
event.utf8_buf[0] = '\0';
}
}
@@ -3418,8 +3500,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
/* double click test */
/* if previous event was same type, and previous was release, and now it presses... */
if (wm_event_is_double_click(&event, evt)) {
- if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS))
- printf("%s Send double click\n", __func__);
+ CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click");
evt->val = event.val = KM_DBL_CLICK;
}
@@ -3489,9 +3570,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
attach_ndof_data(&event, customdata);
wm_event_add(win, &event);
- if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS))
- printf("%s sending NDOF_MOTION, prev = %d %d\n", __func__, event.x, event.y);
-
+ CLOG_INFO(WM_LOG_HANDLERS, 1, "sending NDOF_MOTION, prev = %d %d", event.x, event.y);
break;
}
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 05d63869074..da5ebd7abcd 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -62,6 +62,8 @@
#include "BLT_translation.h"
+#include "BLF_api.h"
+
#include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */
#include "DNA_object_types.h"
#include "DNA_space_types.h"
@@ -71,7 +73,6 @@
#include "DNA_windowmanager_types.h"
#include "BKE_appdir.h"
-#include "BKE_utildefines.h"
#include "BKE_autoexec.h"
#include "BKE_blender.h"
#include "BKE_blendfile.h"
@@ -86,9 +87,11 @@
#include "BKE_sound.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
+#include "BKE_undo_system.h"
#include "BLO_readfile.h"
#include "BLO_writefile.h"
+#include "BLO_undofile.h" /* to save from an undo memfile */
#include "RNA_access.h"
#include "RNA_define.h"
@@ -102,6 +105,7 @@
#include "ED_screen.h"
#include "ED_view3d.h"
#include "ED_util.h"
+#include "ED_undo.h"
#include "GHOST_C-api.h"
#include "GHOST_Path-api.h"
@@ -316,10 +320,8 @@ static void wm_window_match_do(bContext *C, ListBase *oldwmlist)
}
/* in case UserDef was read, we re-initialize all, and do versioning */
-static void wm_init_userdef(bContext *C, const bool from_memory)
+static void wm_init_userdef(Main *bmain, const bool read_userdef_from_memory)
{
- Main *bmain = CTX_data_main(C);
-
/* versioning is here */
UI_init_userdef();
@@ -327,23 +329,23 @@ static void wm_init_userdef(bContext *C, const bool from_memory)
BKE_sound_init(bmain);
/* needed so loading a file from the command line respects user-pref [#26156] */
- BKE_BIT_TEST_SET(G.fileflags, U.flag & USER_FILENOUI, G_FILE_NO_UI);
+ SET_FLAG_FROM_TEST(G.fileflags, U.flag & USER_FILENOUI, G_FILE_NO_UI);
/* set the python auto-execute setting from user prefs */
/* enabled by default, unless explicitly enabled in the command line which overrides */
if ((G.f & G_SCRIPT_OVERRIDE_PREF) == 0) {
- BKE_BIT_TEST_SET(G.f, (U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0, G_SCRIPT_AUTOEXEC);
+ SET_FLAG_FROM_TEST(G.f, (U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0, G_SCRIPT_AUTOEXEC);
}
/* avoid re-saving for every small change to our prefs, allow overrides */
- if (from_memory) {
+ if (read_userdef_from_memory) {
BLO_update_defaults_userpref_blend();
}
/* update tempdir from user preferences */
BKE_tempdir_init(U.tempdir);
- BKE_blender_userdef_refresh();
+ BLF_antialias_set((U.text_render & USER_TEXT_DISABLE_AA) == 0);
}
@@ -451,7 +453,7 @@ void wm_file_read_report(bContext *C)
* Logic shared between #WM_file_read & #wm_homefile_read,
* updates to make after reading a file.
*/
-static void wm_file_read_post(bContext *C, bool is_startup_file)
+static void wm_file_read_post(bContext *C, const bool is_startup_file, const bool use_userdef)
{
bool addons_loaded = false;
wmWindowManager *wm = CTX_wm_manager(C);
@@ -470,9 +472,14 @@ static void wm_file_read_post(bContext *C, bool is_startup_file)
if (is_startup_file) {
/* possible python hasn't been initialized */
if (CTX_py_init_get(C)) {
- /* sync addons, these may have changed from the defaults */
- BPY_execute_string(C, "__import__('addon_utils').reset_all()");
-
+ if (use_userdef) {
+ /* Only run when we have a template path found. */
+ if (BKE_appdir_app_template_any()) {
+ BPY_execute_string(C, "__import__('bl_app_template_utils').reset()");
+ }
+ /* sync addons, these may have changed from the defaults */
+ BPY_execute_string(C, "__import__('addon_utils').reset_all()");
+ }
BPY_python_reset(C);
addons_loaded = true;
}
@@ -482,6 +489,8 @@ static void wm_file_read_post(bContext *C, bool is_startup_file)
BPY_python_reset(C);
addons_loaded = true;
}
+#else
+ UNUSED_VARS(use_userdef);
#endif /* WITH_PYTHON */
WM_operatortype_last_properties_clear_all();
@@ -516,9 +525,13 @@ static void wm_file_read_post(bContext *C, bool is_startup_file)
}
if (!G.background) {
-// undo_editmode_clear();
- BKE_undo_reset();
- BKE_undo_write(C, "original"); /* save current state */
+ 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, CTX_data_main(C));
}
}
@@ -554,7 +567,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
/* confusing this global... */
G.relbase_valid = 1;
- retval = BKE_blendfile_read(C, filepath, reports);
+ retval = BKE_blendfile_read(C, filepath, reports, 0);
/* when loading startup.blend's, we can be left with a blank path */
if (G.main->name[0]) {
G.save_over = 1;
@@ -577,7 +590,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(C, false);
+ wm_init_userdef(G.main, false);
}
if (retval != BKE_BLENDFILE_READ_FAIL) {
@@ -586,14 +599,10 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
}
}
- wm_file_read_post(C, false);
+ wm_file_read_post(C, false, false);
success = true;
}
-#if 0
- else if (retval == BKE_READ_EXOTIC_OK_OTHER)
- BKE_undo_write(C, "Import file");
-#endif
else if (retval == BKE_READ_EXOTIC_FAIL_OPEN) {
BKE_reportf(reports, RPT_ERROR, "Cannot read file '%s': %s", filepath,
errno ? strerror(errno) : TIP_("unable to open the file"));
@@ -629,35 +638,50 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
/**
- * called on startup, (context entirely filled with NULLs)
- * or called for 'New File'
- * both startup.blend and userpref.blend are checked
- * the optional parameter custom_file points to an alternative startup page
- * custom_file can be NULL
+ * Called on startup, (context entirely filled with NULLs)
+ * or called for 'New File' both startup.blend and userpref.blend are checked.
+ *
+ * \param use_factory_settings: Ignore on-disk startup file, use bundled ``datatoc_startup_blend`` instead.
+ * Used for "Restore Factory Settings".
+ * \param use_userdef: Load factory settings as well as startup file.
+ * Disabled for "File New" we don't want to reload preferences.
+ * \param filepath_startup_override: Optional path pointing to an alternative blend file (may be NULL).
+ * \param app_template_override: Template to use instead of the template defined in user-preferences.
+ * When not-null, this is written into the user preferences.
*/
-int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const char *custom_file)
+int wm_homefile_read(
+ bContext *C, ReportList *reports,
+ bool use_factory_settings, bool use_empty_data, bool use_userdef,
+ const char *filepath_startup_override, const char *app_template_override)
{
ListBase wmbase;
- char startstr[FILE_MAX];
- char prefstr[FILE_MAX];
- int success = 0;
+ bool success = false;
+
+ char filepath_startup[FILE_MAX];
+ char filepath_userdef[FILE_MAX];
+
+ /* When 'app_template' is set: '{BLENDER_USER_CONFIG}/{app_template}' */
+ char app_template_system[FILE_MAX];
+ /* When 'app_template' is set: '{BLENDER_SYSTEM_SCRIPTS}/startup/bl_app_templates_system/{app_template}' */
+ char app_template_config[FILE_MAX];
/* Indicates whether user preferences were really load from memory.
*
- * This is used for versioning code, and for this we can not rely on from_memory
+ * This is used for versioning code, and for this we can not rely on use_factory_settings
* passed via argument. This is because there might be configuration folder
* exists but it might not have userpref.blend and in this case we fallback to
* reading home file from memory.
*
* And in this case versioning code is to be run.
*/
- bool read_userdef_from_memory = true;
+ bool read_userdef_from_memory = false;
+ eBLOReadSkip skip_flags = use_userdef ? 0 : BLO_READ_SKIP_USERDEF;
/* options exclude eachother */
- BLI_assert((from_memory && custom_file) == 0);
+ BLI_assert((use_factory_settings && filepath_startup_override) == 0);
if ((G.f & G_SCRIPT_OVERRIDE_PREF) == 0) {
- BKE_BIT_TEST_SET(G.f, (U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0, G_SCRIPT_AUTOEXEC);
+ SET_FLAG_FROM_TEST(G.f, (U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0, G_SCRIPT_AUTOEXEC);
}
BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_PRE);
@@ -665,77 +689,166 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c
UI_view2d_zoom_cache_reset();
G.relbase_valid = 0;
- if (!from_memory) {
- const char * const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
- if (custom_file) {
- BLI_strncpy(startstr, custom_file, FILE_MAX);
- if (cfgdir) {
- BLI_make_file_string(G.main->name, prefstr, cfgdir, BLENDER_USERPREF_FILE);
+ /* put aside screens to match with persistent windows later */
+ wm_window_match_init(C, &wmbase);
+
+ filepath_startup[0] = '\0';
+ filepath_userdef[0] = '\0';
+ app_template_system[0] = '\0';
+ app_template_config[0] = '\0';
+
+ const char * const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
+ if (!use_factory_settings) {
+ if (cfgdir) {
+ BLI_path_join(filepath_startup, sizeof(filepath_startup), cfgdir, BLENDER_STARTUP_FILE, NULL);
+ if (use_userdef) {
+ BLI_path_join(filepath_userdef, sizeof(filepath_startup), cfgdir, BLENDER_USERPREF_FILE, NULL);
}
- else {
- prefstr[0] = '\0';
+ }
+ else {
+ use_factory_settings = true;
+ }
+
+ if (filepath_startup_override) {
+ BLI_strncpy(filepath_startup, filepath_startup_override, FILE_MAX);
+ }
+ }
+
+ /* load preferences before startup.blend */
+ if (use_userdef) {
+ if (!use_factory_settings && BLI_exists(filepath_userdef)) {
+ UserDef *userdef = BKE_blendfile_userdef_read(filepath_userdef, NULL);
+ if (userdef != NULL) {
+ BKE_blender_userdef_data_set_and_free(userdef);
+ userdef = NULL;
+
+ skip_flags |= BLO_READ_SKIP_USERDEF;
+ printf("Read prefs: %s\n", filepath_userdef);
}
}
- else if (cfgdir) {
- BLI_make_file_string(G.main->name, startstr, cfgdir, BLENDER_STARTUP_FILE);
- BLI_make_file_string(G.main->name, prefstr, cfgdir, BLENDER_USERPREF_FILE);
+ }
+
+ const char *app_template = NULL;
+
+ if (filepath_startup_override != NULL) {
+ /* pass */
+ }
+ else if (app_template_override) {
+ /* This may be clearing the current template by setting to an empty string. */
+ app_template = app_template_override;
+ }
+ else if (!use_factory_settings && U.app_template[0]) {
+ app_template = U.app_template;
+ }
+
+ if ((app_template != NULL) && (app_template[0] != '\0')) {
+ BKE_appdir_app_template_id_search(app_template, app_template_system, sizeof(app_template_system));
+
+ /* Insert template name into startup file. */
+
+ /* note that the path is being set even when 'use_factory_settings == true'
+ * this is done so we can load a templates factory-settings */
+ if (!use_factory_settings) {
+ BLI_path_join(app_template_config, sizeof(app_template_config), cfgdir, app_template, NULL);
+ BLI_path_join(filepath_startup, sizeof(filepath_startup), app_template_config, BLENDER_STARTUP_FILE, NULL);
+ if (BLI_access(filepath_startup, R_OK) != 0) {
+ filepath_startup[0] = '\0';
+ }
}
else {
- startstr[0] = '\0';
- prefstr[0] = '\0';
- from_memory = 1;
+ filepath_startup[0] = '\0';
+ }
+
+ if (filepath_startup[0] == '\0') {
+ BLI_path_join(filepath_startup, sizeof(filepath_startup), app_template_system, BLENDER_STARTUP_FILE, NULL);
}
}
-
- /* put aside screens to match with persistent windows later */
- wm_window_match_init(C, &wmbase);
-
- if (!from_memory) {
- if (BLI_access(startstr, R_OK) == 0) {
- success = (BKE_blendfile_read(C, startstr, NULL) != BKE_BLENDFILE_READ_FAIL);
+
+ if (!use_factory_settings || (filepath_startup[0] != '\0')) {
+ if (BLI_access(filepath_startup, R_OK) == 0) {
+ success = (BKE_blendfile_read(C, filepath_startup, NULL, skip_flags) != BKE_BLENDFILE_READ_FAIL);
}
if (BLI_listbase_is_empty(&U.themes)) {
if (G.debug & G_DEBUG)
- printf("\nNote: No (valid) '%s' found, fall back to built-in default.\n\n", startstr);
- success = 0;
+ printf("\nNote: No (valid) '%s' found, fall back to built-in default.\n\n", filepath_startup);
+ success = false;
}
}
- if (success == 0 && custom_file && reports) {
- BKE_reportf(reports, RPT_ERROR, "Could not read '%s'", custom_file);
- /*We can not return from here because wm is already reset*/
+ if (success == false && filepath_startup_override && reports) {
+ /* We can not return from here because wm is already reset */
+ BKE_reportf(reports, RPT_ERROR, "Could not read '%s'", filepath_startup_override);
}
- if (success == 0) {
- success = BKE_blendfile_read_from_memory(C, datatoc_startup_blend, datatoc_startup_blend_size, NULL, true);
+ if (success == false) {
+ success = BKE_blendfile_read_from_memory(
+ C, datatoc_startup_blend, datatoc_startup_blend_size,
+ NULL, skip_flags, true);
+ if (success) {
+ if (use_userdef) {
+ if ((skip_flags & BLO_READ_SKIP_USERDEF) == 0) {
+ read_userdef_from_memory = true;
+ }
+ }
+ }
if (BLI_listbase_is_empty(&wmbase)) {
wm_clear_default_size(C);
}
- BKE_tempdir_init(U.tempdir);
+ }
-#ifdef WITH_PYTHON_SECURITY
- /* use alternative setting for security nuts
- * otherwise we'd need to patch the binary blob - startup.blend.c */
- U.flag |= USER_SCRIPT_AUTOEXEC_DISABLE;
-#endif
+ if (use_empty_data) {
+ BKE_blendfile_read_make_empty(C);
}
-
- /* check new prefs only after startup.blend was finished */
- if (!from_memory && BLI_exists(prefstr)) {
- int done = BKE_blendfile_read_userdef(prefstr, NULL);
- if (done != BKE_BLENDFILE_READ_FAIL) {
- read_userdef_from_memory = false;
- printf("Read new prefs: %s\n", prefstr);
+
+ /* Load template preferences,
+ * unlike regular preferences we only use some of the settings,
+ * see: BKE_blender_userdef_set_app_template */
+ if (app_template_system[0] != '\0') {
+ char temp_path[FILE_MAX];
+ temp_path[0] = '\0';
+ if (!use_factory_settings) {
+ BLI_path_join(temp_path, sizeof(temp_path), app_template_config, BLENDER_USERPREF_FILE, NULL);
+ if (BLI_access(temp_path, R_OK) != 0) {
+ temp_path[0] = '\0';
+ }
+ }
+
+ if (temp_path[0] == '\0') {
+ BLI_path_join(temp_path, sizeof(temp_path), app_template_system, BLENDER_USERPREF_FILE, NULL);
+ }
+
+ if (use_userdef) {
+ UserDef *userdef_template = NULL;
+ /* just avoids missing file warning */
+ if (BLI_exists(temp_path)) {
+ userdef_template = BKE_blendfile_userdef_read(temp_path, NULL);
+ }
+ if (userdef_template == NULL) {
+ /* we need to have preferences load to overwrite preferences from previous template */
+ userdef_template = BKE_blendfile_userdef_read_from_memory(
+ datatoc_startup_blend, datatoc_startup_blend_size, NULL);
+ read_userdef_from_memory = true;
+ }
+ if (userdef_template) {
+ BKE_blender_userdef_app_template_data_set_and_free(userdef_template);
+ userdef_template = NULL;
+ }
}
}
-
+
+ if (app_template_override) {
+ 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;
- /* check userdef before open window, keymaps etc */
- wm_init_userdef(C, read_userdef_from_memory);
+ if (use_userdef) {
+ /* check userdef before open window, keymaps etc */
+ wm_init_userdef(CTX_data_main(C), read_userdef_from_memory);
+ }
/* match the read WM with current WM */
wm_window_match_do(C, &wmbase);
@@ -743,14 +856,19 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c
G.main->name[0] = '\0';
- /* When loading factory settings, the reset solid OpenGL lights need to be applied. */
- if (!G.background) GPU_default_lights();
-
- /* XXX */
- G.save_over = 0; // start with save preference untitled.blend
- G.fileflags &= ~G_FILE_AUTOPLAY; /* disable autoplay in startup.blend... */
+ if (use_userdef) {
+ /* When loading factory settings, the reset solid OpenGL lights need to be applied. */
+ if (!G.background) {
+ GPU_default_lights();
+ }
+ }
+
+ /* start with save preference untitled.blend */
+ G.save_over = 0;
+ /* disable auto-play in startup.blend... */
+ G.fileflags &= ~G_FILE_AUTOPLAY;
- wm_file_read_post(C, true);
+ wm_file_read_post(C, true, use_userdef);
return true;
}
@@ -918,14 +1036,14 @@ static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, BlendThumbnail **t
ibuf = ED_view3d_draw_offscreen_imbuf_simple(
scene, scene->camera,
BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
- IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, 0, false, NULL,
+ IB_rect, V3D_OFSDRAW_NONE, OB_SOLID, R_ALPHAPREMUL, 0, NULL,
NULL, NULL, err_out);
}
else {
ibuf = ED_view3d_draw_offscreen_imbuf(
scene, v3d, ar,
BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
- IB_rect, false, R_ALPHAPREMUL, 0, false, NULL,
+ IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL,
NULL, NULL, err_out);
}
@@ -998,7 +1116,7 @@ static int wm_file_write(bContext *C, const char *filepath, int fileflags, Repor
BKE_reportf(reports, RPT_ERROR, "Cannot save blend file, path '%s' is not writable", filepath);
return ret;
}
-
+
/* note: used to replace the file extension (to ensure '.blend'),
* no need to now because the operator ensures,
* its handy for scripts to save to a predefined name without blender editing it */
@@ -1054,8 +1172,8 @@ static int wm_file_write(bContext *C, const char *filepath, int fileflags, Repor
G.save_over = 1; /* disable untitled.blend convention */
}
- BKE_BIT_TEST_SET(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS);
- BKE_BIT_TEST_SET(G.fileflags, fileflags & G_FILE_AUTOPLAY, G_FILE_AUTOPLAY);
+ SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS);
+ SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_AUTOPLAY, G_FILE_AUTOPLAY);
/* prevent background mode scripts from clobbering history */
if (do_history) {
@@ -1156,7 +1274,10 @@ void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *UNUSED(w
if (U.uiflag & USER_GLOBALUNDO) {
/* fast save of last undobuffer, now with UI */
- BKE_undo_save_file(filepath);
+ struct MemFile *memfile = ED_undosys_stack_memfile_get_active(wm->undo_stack);
+ if (memfile) {
+ BLO_memfile_write_file(memfile, filepath);
+ }
}
else {
/* save as regular blend file */
@@ -1240,13 +1361,13 @@ void wm_open_init_use_scripts(wmOperator *op, bool use_prefs)
/** \} */
-void WM_file_tag_modified(const bContext *C)
+void WM_file_tag_modified(void)
{
- wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindowManager *wm = G.main->wm.first;
if (wm->file_saved) {
wm->file_saved = 0;
/* notifier that data changed, for save-over warning or header */
- WM_event_add_notifier(C, NC_WM | ND_DATACHANGED, NULL);
+ WM_main_add_notifier(NC_WM | ND_DATACHANGED, NULL);
}
}
@@ -1264,6 +1385,13 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
char filepath[FILE_MAX];
int fileflags;
+ const char *app_template = U.app_template[0] ? U.app_template : NULL;
+ const char * const cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, app_template);
+ if (cfgdir == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Unable to create user config path");
+ return OPERATOR_CANCELLED;
+ }
+
BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE);
/* check current window and close it if temp */
@@ -1273,7 +1401,8 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
/* 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);
+ BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_STARTUP_FILE, NULL);
+
printf("trying to save homefile at %s ", filepath);
ED_editors_flush_edits(C, false);
@@ -1351,21 +1480,56 @@ 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;
/* 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 ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL))) {
+ bool ok_write;
+ BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL);
+ printf("trying to save userpref at %s ", filepath);
- if (BKE_blendfile_write_userdef(filepath, op->reports) == 0) {
- printf("fail\n");
- return OPERATOR_CANCELLED;
+ if (U.app_template[0]) {
+ ok_write = BKE_blendfile_userdef_write_app_template(filepath, op->reports);
+ }
+ else {
+ ok_write = BKE_blendfile_userdef_write(filepath, op->reports);
+ }
+
+ if (ok_write) {
+ printf("ok\n");
+ }
+ else {
+ printf("fail\n");
+ ok = false;
+ }
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "Unable to create userpref path");
}
- printf("ok\n");
+ if (U.app_template[0]) {
+ 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);
+ printf("trying to save app-template userpref at %s ", filepath);
+ if (BKE_blendfile_userdef_write(filepath, op->reports) != 0) {
+ printf("ok\n");
+ }
+ else {
+ printf("fail\n");
+ ok = false;
+ }
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "Unable to create app-template userpref path");
+ ok = false;
+ }
+ }
- return OPERATOR_FINISHED;
+ return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
void WM_OT_save_userpref(wmOperatorType *ot)
@@ -1400,17 +1564,18 @@ void WM_OT_read_history(wmOperatorType *ot)
static int wm_homefile_read_exec(bContext *C, wmOperator *op)
{
- const bool from_memory = (STREQ(op->type->idname, "WM_OT_read_factory_settings"));
+ const bool use_factory_settings = (STREQ(op->type->idname, "WM_OT_read_factory_settings"));
+ bool use_userdef = false;
char filepath_buf[FILE_MAX];
const char *filepath = NULL;
- if (!from_memory) {
+ if (!use_factory_settings) {
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);
+ SET_FLAG_FROM_TEST(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);
@@ -1424,9 +1589,36 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op)
else {
/* always load UI for factory settings (prefs will re-init) */
G.fileflags &= ~G_FILE_NO_UI;
+ /* Always load preferences with factory settings. */
+ use_userdef = true;
+ }
+
+ char app_template_buf[sizeof(U.app_template)];
+ const char *app_template;
+ 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");
+
+ 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. */
+ use_userdef = true;
+ }
+ else {
+ app_template = NULL;
}
- return wm_homefile_read(C, op->reports, from_memory, filepath) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ if (wm_homefile_read(C, op->reports, use_factory_settings, use_empty_data, use_userdef, filepath, app_template)) {
+ if (use_splash) {
+ WM_init_splash(C);
+ }
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
}
void WM_OT_read_homefile(wmOperatorType *ot)
@@ -1449,17 +1641,36 @@ void WM_OT_read_homefile(wmOperatorType *ot)
"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);
+
/* 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 user preferences";
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);
+
/* omit poll to run in background mode */
}
@@ -1847,20 +2058,24 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
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);
+ SET_FLAG_FROM_TEST(
+ fileflags, RNA_boolean_get(op->ptr, "compress"),
+ G_FILE_COMPRESS);
+ SET_FLAG_FROM_TEST(
+ fileflags, RNA_boolean_get(op->ptr, "relative_remap"),
+ G_FILE_RELATIVE_REMAP);
+ SET_FLAG_FROM_TEST(
+ 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);
+ SET_FLAG_FROM_TEST(
+ 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
@@ -1870,6 +2085,10 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL);
+ if (RNA_boolean_get(op->ptr, "exit")) {
+ wm_exit_schedule_delayed(C);
+ }
+
return OPERATOR_FINISHED;
}
@@ -1941,11 +2160,13 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *U
char path[FILE_MAX];
RNA_string_get(op->ptr, "filepath", path);
- if (BLI_exists(path)) {
+ if (RNA_boolean_get(op->ptr, "check_existing") && 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);
+ /* Without this there is no feedback the file was saved. */
+ BKE_reportf(op->reports, RPT_INFO, "Saved \"%s\"", BLI_path_basename(path));
}
}
else {
@@ -1967,12 +2188,16 @@ void WM_OT_save_mainfile(wmOperatorType *ot)
ot->check = blend_save_check;
/* omit window poll so this can work in background mode */
+ PropertyRNA *prop;
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");
+
+ prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "Exit Blender after saving");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/** \} */
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index 3b733f9558c..fb611290aa5 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -157,7 +157,7 @@ typedef struct WMLinkAppendData {
LinkNodePair items;
int num_libraries;
int num_items;
- short flag;
+ int flag; /* Combines eFileSel_Params_Flag from DNA_space_types.h and BLO_LibLinkFlags from BLO_readfile.h */
/* Internal 'private' data */
MemArena *memarena;
@@ -211,9 +211,7 @@ static WMLinkAppendDataItem *wm_link_append_data_item_add(
return item;
}
-static void wm_link_do(
- WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d,
- const bool use_placeholders, const bool force_indirect)
+static void wm_link_do(WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d)
{
Main *mainl;
BlendHandle *bh;
@@ -260,8 +258,7 @@ static void wm_link_do(
continue;
}
- new_id = BLO_library_link_named_part_ex(
- mainl, &bh, item->idcode, item->name, flag, scene, v3d, use_placeholders, force_indirect);
+ new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d);
if (new_id) {
/* If the link is successful, clear item's libs 'todo' flags.
@@ -282,10 +279,9 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
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 path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX_LIBEXTRA], 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);
@@ -322,7 +318,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- flag = wm_link_append_flag(op);
+ short flag = wm_link_append_flag(op);
/* sanity checks for flag */
if (scene && scene->id.lib) {
@@ -332,6 +328,8 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
scene = NULL;
}
+ /* We need to add nothing from BLO_LibLinkFlags to flag here. */
+
/* from here down, no error returns */
if (scene && RNA_boolean_get(op->ptr, "autoselect")) {
@@ -402,10 +400,16 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
BLI_BITMAP_ENABLE(item->libraries, 0);
}
+ if (lapp_data->num_items == 0) {
+ /* Early out in case there is nothing to link. */
+ wm_link_append_data_free(lapp_data);
+ return OPERATOR_CANCELLED;
+ }
+
/* 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), false, false);
+ wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C));
/* BKE_main_unlock(bmain); */
@@ -446,7 +450,9 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
/* recreate dependency graph to include new objects */
- DAG_scene_relations_rebuild(bmain, scene);
+ if (scene) {
+ 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();
@@ -589,10 +595,15 @@ static void lib_relocate_do(
}
}
+ if (lapp_data->num_items == 0) {
+ /* Early out in case there is nothing to do. */
+ return;
+ }
+
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
/* We do not want any instanciation here! */
- wm_link_do(lapp_data, reports, bmain, NULL, NULL, do_reload, do_reload);
+ wm_link_do(lapp_data, reports, bmain, NULL, NULL);
BKE_main_lock(bmain);
@@ -608,7 +619,8 @@ static void lib_relocate_do(
}
/* Note that in reload case, we also want to replace indirect usages. */
- const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE | (do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE);
+ const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE | ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE |
+ (do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE);
for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) {
WMLinkAppendDataItem *item = itemlink->link;
ID *old_id = item->customdata;
@@ -755,7 +767,7 @@ void WM_lib_reload(Library *lib, bContext *C, ReportList *reports)
return;
}
- WMLinkAppendData *lapp_data = wm_link_append_data_new(0);
+ WMLinkAppendData *lapp_data = wm_link_append_data_new(BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT);
wm_link_append_data_library_add(lapp_data, lib->filepath);
@@ -866,6 +878,10 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload)
}
}
+ if (do_reload) {
+ lapp_data->flag |= BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT;
+ }
+
lib_relocate_do(bmain, scene, lib, lapp_data, op->reports, do_reload);
wm_link_append_data_free(lapp_data);
diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c
index 46203333eb5..1a4381a1275 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -41,7 +41,7 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
-#include "BLI_lasso.h"
+#include "BLI_lasso_2d.h"
#include "BKE_context.h"
@@ -71,6 +71,8 @@ wmGesture *WM_gesture_new(bContext *C, const wmEvent *event, int type)
gesture->type = type;
gesture->event_type = event->type;
gesture->swinid = ar->swinid; /* means only in area-region context! */
+ gesture->userdata_free = true; /* Free if userdata is set. */
+ gesture->modal_state = GESTURE_MODAL_NOP;
wm_subwindow_origin_get(window, gesture->swinid, &sx, &sy);
@@ -83,11 +85,7 @@ wmGesture *WM_gesture_new(bContext *C, const wmEvent *event, int type)
rect->xmin = event->x - sx;
rect->ymin = event->y - sy;
if (type == WM_GESTURE_CIRCLE) {
-#ifdef GESTURE_MEMORY
- rect->xmax = circle_select_size;
-#else
- rect->xmax = 25; // XXX temp
-#endif
+ /* caller is responsible for initializing 'xmax' to radius. */
}
else {
rect->xmax = event->x - sx;
@@ -96,11 +94,11 @@ wmGesture *WM_gesture_new(bContext *C, const wmEvent *event, int type)
}
else if (ELEM(type, WM_GESTURE_LINES, WM_GESTURE_LASSO)) {
short *lasso;
- gesture->customdata = lasso = MEM_callocN(2 * sizeof(short) * WM_LASSO_MIN_POINTS, "lasso points");
+ gesture->points_alloc = 1024;
+ gesture->customdata = lasso = MEM_mallocN(sizeof(short[2]) * gesture->points_alloc, "lasso points");
lasso[0] = event->x - sx;
lasso[1] = event->y - sy;
gesture->points = 1;
- gesture->size = WM_LASSO_MIN_POINTS;
}
return gesture;
@@ -114,7 +112,7 @@ void WM_gesture_end(bContext *C, wmGesture *gesture)
win->tweak = NULL;
BLI_remlink(&win->gesture, gesture);
MEM_freeN(gesture->customdata);
- if (gesture->userdata) {
+ if (gesture->userdata && gesture->userdata_free) {
MEM_freeN(gesture->userdata);
}
MEM_freeN(gesture);
@@ -137,7 +135,7 @@ int wm_gesture_evaluate(wmGesture *gesture)
int dx = BLI_rcti_size_x(rect);
int dy = BLI_rcti_size_y(rect);
if (abs(dx) + abs(dy) > U.tweak_threshold) {
- int theta = iroundf(4.0f * atan2f((float)dy, (float)dx) / (float)M_PI);
+ int theta = round_fl_to_int(4.0f * atan2f((float)dy, (float)dx) / (float)M_PI);
int val = EVT_GESTURE_W;
if (theta == 0) val = EVT_GESTURE_E;
@@ -376,10 +374,12 @@ void wm_gesture_draw(wmWindow *win)
else if (gt->type == WM_GESTURE_CIRCLE)
wm_gesture_draw_circle(gt);
else if (gt->type == WM_GESTURE_CROSS_RECT) {
- if (gt->mode == 1)
+ if (gt->is_active) {
wm_gesture_draw_rect(gt);
- else
+ }
+ else {
wm_gesture_draw_cross(win, gt);
+ }
}
else if (gt->type == WM_GESTURE_LINES)
wm_gesture_draw_lasso(win, gt, false);
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
new file mode 100644
index 00000000000..a554727cacd
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -0,0 +1,883 @@
+/*
+ * ***** 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_gesture_ops.c
+ * \ingroup wm
+ *
+ * Default operator callbacks for use with gestures (border/circle/lasso/straightline).
+ * Operators themselves are defined elsewhere.
+ *
+ * - Keymaps are in ``wm_operators.c``.
+ * - Property definitions are in ``wm_operator_props.c``.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_windowmanager_types.h"
+
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+
+#include "WM_types.h"
+#include "WM_api.h"
+
+#include "wm.h"
+#include "wm_event_types.h"
+#include "wm_event_system.h"
+#include "wm_subwindow.h"
+
+#include "ED_screen.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Gesture Utilities
+ *
+ * Border gesture has two types:
+ * -# #WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border.
+ * -# #WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends.
+ *
+ * It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type).
+ *
+ * \{ */
+
+static void gesture_modal_end(bContext *C, wmOperator *op)
+{
+ wmGesture *gesture = op->customdata;
+
+ WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */
+ op->customdata = NULL;
+
+ ED_area_tag_redraw(CTX_wm_area(C));
+
+ if (RNA_struct_find_property(op->ptr, "cursor")) {
+ WM_cursor_modal_restore(CTX_wm_window(C));
+ }
+}
+
+static void gesture_modal_state_to_operator(wmOperator *op, int modal_state)
+{
+ PropertyRNA *prop;
+
+ switch (modal_state) {
+ case GESTURE_MODAL_SELECT:
+ case GESTURE_MODAL_DESELECT:
+ if ((prop = RNA_struct_find_property(op->ptr, "deselect"))) {
+ RNA_property_boolean_set(op->ptr, prop, (modal_state == GESTURE_MODAL_DESELECT));
+ }
+ break;
+ case GESTURE_MODAL_IN:
+ case GESTURE_MODAL_OUT:
+ if ((prop = RNA_struct_find_property(op->ptr, "zoom_out"))) {
+ RNA_property_boolean_set(op->ptr, prop, (modal_state == GESTURE_MODAL_OUT));
+ }
+ break;
+ }
+}
+
+static int gesture_modal_state_from_operator(wmOperator *op)
+{
+ PropertyRNA *prop;
+
+ if ((prop = RNA_struct_find_property(op->ptr, "deselect"))) {
+ if (RNA_property_is_set(op->ptr, prop)) {
+ return RNA_property_boolean_get(op->ptr, prop) ? GESTURE_MODAL_DESELECT : GESTURE_MODAL_SELECT;
+ }
+ }
+ if ((prop = RNA_struct_find_property(op->ptr, "zoom_out"))) {
+ if (RNA_property_is_set(op->ptr, prop)) {
+ return RNA_property_boolean_get(op->ptr, prop) ? GESTURE_MODAL_OUT : GESTURE_MODAL_IN;
+ }
+ }
+ return GESTURE_MODAL_NOP;
+}
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/** \name Border Gesture
+ *
+ * Border gesture has two types:
+ * -# #WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border.
+ * -# #WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends.
+ *
+ * It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type).
+ *
+ * \{ */
+
+static bool gesture_border_apply_rect(wmOperator *op)
+{
+ wmGesture *gesture = op->customdata;
+ rcti *rect = gesture->customdata;
+
+ if (rect->xmin == rect->xmax || rect->ymin == rect->ymax)
+ return 0;
+
+
+ /* operator arguments and storage. */
+ RNA_int_set(op->ptr, "xmin", min_ii(rect->xmin, rect->xmax));
+ RNA_int_set(op->ptr, "ymin", min_ii(rect->ymin, rect->ymax));
+ RNA_int_set(op->ptr, "xmax", max_ii(rect->xmin, rect->xmax));
+ RNA_int_set(op->ptr, "ymax", max_ii(rect->ymin, rect->ymax));
+
+ return 1;
+}
+
+static bool gesture_border_apply(bContext *C, wmOperator *op)
+{
+ wmGesture *gesture = op->customdata;
+
+ int retval;
+
+ if (!gesture_border_apply_rect(op)) {
+ return 0;
+ }
+
+ gesture_modal_state_to_operator(op, gesture->modal_state);
+
+ retval = op->type->exec(C, op);
+ OPERATOR_RETVAL_CHECK(retval);
+
+ return 1;
+}
+
+int WM_gesture_border_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int modal_state = gesture_modal_state_from_operator(op);
+
+ if (ISTWEAK(event->type) || (modal_state != GESTURE_MODAL_NOP)) {
+ op->customdata = WM_gesture_new(C, event, WM_GESTURE_RECT);
+ }
+ else {
+ op->customdata = WM_gesture_new(C, event, WM_GESTURE_CROSS_RECT);
+ }
+
+ /* Starting with the mode starts immediately, like having 'wait_for_input' disabled (some tools use this). */
+ if (modal_state == GESTURE_MODAL_NOP) {
+ wmGesture *gesture = op->customdata;
+ gesture->wait_for_input = true;
+ }
+ else {
+ wmGesture *gesture = op->customdata;
+ gesture->modal_state = modal_state;
+ }
+
+ /* add modal handler */
+ WM_event_add_modal_handler(C, op);
+
+ wm_gesture_tag_redraw(C);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+int WM_gesture_border_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ wmGesture *gesture = op->customdata;
+ rcti *rect = gesture->customdata;
+ int sx, sy;
+
+ if (event->type == MOUSEMOVE) {
+ wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
+
+ if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) {
+ rect->xmin = rect->xmax = event->x - sx;
+ rect->ymin = rect->ymax = event->y - sy;
+ }
+ else {
+ rect->xmax = event->x - sx;
+ rect->ymax = event->y - sy;
+ }
+ gesture_border_apply_rect(op);
+
+ wm_gesture_tag_redraw(C);
+ }
+ else if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case GESTURE_MODAL_BEGIN:
+ if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) {
+ gesture->is_active = true;
+ wm_gesture_tag_redraw(C);
+ }
+ break;
+ case GESTURE_MODAL_SELECT:
+ case GESTURE_MODAL_DESELECT:
+ case GESTURE_MODAL_IN:
+ case GESTURE_MODAL_OUT:
+ if (gesture->wait_for_input) {
+ gesture->modal_state = event->val;
+ }
+ if (gesture_border_apply(C, op)) {
+ gesture_modal_end(C, op);
+ return OPERATOR_FINISHED;
+ }
+ gesture_modal_end(C, op);
+ return OPERATOR_CANCELLED;
+
+ case GESTURE_MODAL_CANCEL:
+ gesture_modal_end(C, op);
+ return OPERATOR_CANCELLED;
+ }
+
+ }
+#ifdef WITH_INPUT_NDOF
+ else if (event->type == NDOF_MOTION) {
+ return OPERATOR_PASS_THROUGH;
+ }
+#endif
+
+#if 0
+ /* Allow view navigation??? */
+ else {
+ return OPERATOR_PASS_THROUGH;
+ }
+#endif
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void WM_gesture_border_cancel(bContext *C, wmOperator *op)
+{
+ gesture_modal_end(C, op);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/** \name Circle Gesture
+ *
+ * Currently only used for selection or modal paint stuff,
+ * calls ``exec`` while hold mouse, exits on release (with no difference between cancel and confirm).
+ *
+ * \{ */
+
+static void gesture_circle_apply(bContext *C, wmOperator *op);
+
+int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int modal_state = gesture_modal_state_from_operator(op);
+
+ op->customdata = WM_gesture_new(C, event, WM_GESTURE_CIRCLE);
+ wmGesture *gesture = op->customdata;
+ rcti *rect = gesture->customdata;
+
+ /* Default or previously stored value. */
+ rect->xmax = RNA_int_get(op->ptr, "radius");
+
+ /* Starting with the mode starts immediately, like having 'wait_for_input' disabled (some tools use this). */
+ if (modal_state == GESTURE_MODAL_NOP) {
+ gesture->wait_for_input = true;
+ }
+ else {
+ gesture->is_active = true;
+ gesture->modal_state = modal_state;
+ gesture_circle_apply(C, op);
+ }
+
+ /* add modal handler */
+ WM_event_add_modal_handler(C, op);
+
+ wm_gesture_tag_redraw(C);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void gesture_circle_apply(bContext *C, wmOperator *op)
+{
+ wmGesture *gesture = op->customdata;
+ rcti *rect = gesture->customdata;
+
+ if (gesture->modal_state == GESTURE_MODAL_NOP) {
+ return;
+ }
+
+ /* operator arguments and storage. */
+ RNA_int_set(op->ptr, "x", rect->xmin);
+ RNA_int_set(op->ptr, "y", rect->ymin);
+ RNA_int_set(op->ptr, "radius", rect->xmax);
+
+ gesture_modal_state_to_operator(op, gesture->modal_state);
+
+ if (op->type->exec) {
+ int retval;
+ retval = op->type->exec(C, op);
+ OPERATOR_RETVAL_CHECK(retval);
+ }
+}
+
+int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ wmGesture *gesture = op->customdata;
+ rcti *rect = gesture->customdata;
+ int sx, sy;
+
+ if (event->type == MOUSEMOVE) {
+ wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
+
+ rect->xmin = event->x - sx;
+ rect->ymin = event->y - sy;
+
+ wm_gesture_tag_redraw(C);
+
+ if (gesture->is_active) {
+ gesture_circle_apply(C, op);
+ }
+ }
+ else if (event->type == EVT_MODAL_MAP) {
+ bool is_circle_size = false;
+ bool is_finished = false;
+ float fac;
+
+ switch (event->val) {
+ case GESTURE_MODAL_CIRCLE_SIZE:
+ fac = 0.3f * (event->y - event->prevy);
+ if (fac > 0)
+ rect->xmax += ceil(fac);
+ else
+ rect->xmax += floor(fac);
+ if (rect->xmax < 1) rect->xmax = 1;
+ is_circle_size = true;
+ break;
+ case GESTURE_MODAL_CIRCLE_ADD:
+ rect->xmax += 2 + rect->xmax / 10;
+ is_circle_size = true;
+ break;
+ case GESTURE_MODAL_CIRCLE_SUB:
+ rect->xmax -= 2 + rect->xmax / 10;
+ if (rect->xmax < 1) rect->xmax = 1;
+ is_circle_size = true;
+ break;
+ case GESTURE_MODAL_SELECT:
+ case GESTURE_MODAL_DESELECT:
+ case GESTURE_MODAL_NOP:
+ {
+ if (gesture->wait_for_input) {
+ gesture->modal_state = event->val;
+ }
+ if (event->val == GESTURE_MODAL_NOP) {
+ /* Single action, click-drag & release to exit. */
+ if (gesture->wait_for_input == false) {
+ is_finished = true;
+ }
+ }
+ else {
+ /* apply first click */
+ gesture_circle_apply(C, op);
+ gesture->is_active = true;
+ wm_gesture_tag_redraw(C);
+ }
+ break;
+ }
+ case GESTURE_MODAL_CANCEL:
+ case GESTURE_MODAL_CONFIRM:
+ is_finished = true;
+ }
+
+ if (is_finished) {
+ gesture_modal_end(C, op);
+ return OPERATOR_FINISHED; /* use finish or we don't get an undo */
+ }
+
+ if (is_circle_size) {
+ wm_gesture_tag_redraw(C);
+
+ /* So next use remembers last seen size, even if we didn't apply it. */
+ RNA_int_set(op->ptr, "radius", rect->xmax);
+ }
+ }
+#ifdef WITH_INPUT_NDOF
+ else if (event->type == NDOF_MOTION) {
+ return OPERATOR_PASS_THROUGH;
+ }
+#endif
+
+#if 0
+ /* Allow view navigation??? */
+ /* note, this gives issues:
+ * 1) other modal ops run on top (border select),
+ * 2) middlemouse is used now 3) tablet/trackpad? */
+ else {
+ return OPERATOR_PASS_THROUGH;
+ }
+#endif
+
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void WM_gesture_circle_cancel(bContext *C, wmOperator *op)
+{
+ gesture_modal_end(C, op);
+}
+
+#if 0
+/* template to copy from */
+void WM_OT_circle_gesture(wmOperatorType *ot)
+{
+ ot->name = "Circle Gesture";
+ ot->idname = "WM_OT_circle_gesture";
+ ot->description = "Enter rotate mode with a circular gesture";
+
+ ot->invoke = WM_gesture_circle_invoke;
+ ot->modal = WM_gesture_circle_modal;
+ ot->poll = WM_operator_winactive;
+
+ /* properties */
+ WM_operator_properties_gesture_circle(ot);
+
+}
+#endif
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/** \name Tweak Gesture
+ * \{ */
+
+static void gesture_tweak_modal(bContext *C, const wmEvent *event)
+{
+ wmWindow *window = CTX_wm_window(C);
+ wmGesture *gesture = window->tweak;
+ rcti *rect = gesture->customdata;
+ int sx, sy, val;
+
+ switch (event->type) {
+ case MOUSEMOVE:
+ case INBETWEEN_MOUSEMOVE:
+
+ wm_subwindow_origin_get(window, gesture->swinid, &sx, &sy);
+
+ rect->xmax = event->x - sx;
+ rect->ymax = event->y - sy;
+
+ if ((val = wm_gesture_evaluate(gesture))) {
+ wmEvent tevent;
+
+ wm_event_init_from_window(window, &tevent);
+ /* We want to get coord from start of drag, not from point where it becomes a tweak event, see T40549 */
+ tevent.x = rect->xmin + sx;
+ tevent.y = rect->ymin + sy;
+ if (gesture->event_type == LEFTMOUSE)
+ tevent.type = EVT_TWEAK_L;
+ else if (gesture->event_type == RIGHTMOUSE)
+ tevent.type = EVT_TWEAK_R;
+ else
+ tevent.type = EVT_TWEAK_M;
+ tevent.val = val;
+ /* mouse coords! */
+
+ /* important we add immediately after this event, so future mouse releases
+ * (which may be in the queue already), are handled in order, see T44740 */
+ wm_event_add_ex(window, &tevent, event);
+
+ WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */
+ }
+
+ break;
+
+ case LEFTMOUSE:
+ case RIGHTMOUSE:
+ case MIDDLEMOUSE:
+ if (gesture->event_type == event->type) {
+ WM_gesture_end(C, gesture);
+
+ /* when tweak fails we should give the other keymap entries a chance */
+
+ /* XXX, assigning to readonly, BAD JUJU! */
+ ((wmEvent *)event)->val = KM_RELEASE;
+ }
+ break;
+ default:
+ if (!ISTIMER(event->type) && event->type != EVENT_NONE) {
+ WM_gesture_end(C, gesture);
+ }
+ break;
+ }
+}
+
+/* standard tweak, called after window handlers passed on event */
+void wm_tweakevent_test(bContext *C, const wmEvent *event, int action)
+{
+ wmWindow *win = CTX_wm_window(C);
+
+ if (win->tweak == NULL) {
+ if (CTX_wm_region(C)) {
+ if (event->val == KM_PRESS) {
+ if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
+ win->tweak = WM_gesture_new(C, event, WM_GESTURE_TWEAK);
+ }
+ }
+ }
+ }
+ else {
+ /* no tweaks if event was handled */
+ if ((action & WM_HANDLER_BREAK)) {
+ WM_gesture_end(C, win->tweak);
+ }
+ else
+ gesture_tweak_modal(C, event);
+ }
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/** \name Lasso Gesture
+ * \{ */
+
+int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ PropertyRNA *prop;
+
+ op->customdata = WM_gesture_new(C, event, WM_GESTURE_LASSO);
+
+ /* add modal handler */
+ WM_event_add_modal_handler(C, op);
+
+ wm_gesture_tag_redraw(C);
+
+ if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
+ WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+int WM_gesture_lines_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ PropertyRNA *prop;
+
+ op->customdata = WM_gesture_new(C, event, WM_GESTURE_LINES);
+
+ /* add modal handler */
+ WM_event_add_modal_handler(C, op);
+
+ wm_gesture_tag_redraw(C);
+
+ if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
+ WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+
+static void gesture_lasso_apply(bContext *C, wmOperator *op)
+{
+ wmGesture *gesture = op->customdata;
+ PointerRNA itemptr;
+ float loc[2];
+ int i;
+ const short *lasso = gesture->customdata;
+
+ /* operator storage as path. */
+
+ RNA_collection_clear(op->ptr, "path");
+ for (i = 0; i < gesture->points; i++, lasso += 2) {
+ loc[0] = lasso[0];
+ loc[1] = lasso[1];
+ RNA_collection_add(op->ptr, "path", &itemptr);
+ RNA_float_set_array(&itemptr, "loc", loc);
+ }
+
+ gesture_modal_end(C, op);
+
+ if (op->type->exec) {
+ int retval = op->type->exec(C, op);
+ OPERATOR_RETVAL_CHECK(retval);
+ }
+}
+
+int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ wmGesture *gesture = op->customdata;
+ int sx, sy;
+
+ switch (event->type) {
+ case MOUSEMOVE:
+ case INBETWEEN_MOUSEMOVE:
+
+ wm_gesture_tag_redraw(C);
+
+ wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
+
+ if (gesture->points == gesture->points_alloc) {
+ gesture->points_alloc *= 2;
+ gesture->customdata = MEM_reallocN(gesture->customdata, sizeof(short[2]) * gesture->points_alloc);
+ }
+
+ {
+ int x, y;
+ short *lasso = gesture->customdata;
+
+ lasso += (2 * gesture->points - 2);
+ x = (event->x - sx - lasso[0]);
+ y = (event->y - sy - lasso[1]);
+
+ /* make a simple distance check to get a smoother lasso
+ * add only when at least 2 pixels between this and previous location */
+ if ((x * x + y * y) > 4) {
+ lasso += 2;
+ lasso[0] = event->x - sx;
+ lasso[1] = event->y - sy;
+ gesture->points++;
+ }
+ }
+ break;
+
+ case LEFTMOUSE:
+ case MIDDLEMOUSE:
+ case RIGHTMOUSE:
+ if (event->val == KM_RELEASE) { /* key release */
+ gesture_lasso_apply(C, op);
+ return OPERATOR_FINISHED;
+ }
+ break;
+ case ESCKEY:
+ gesture_modal_end(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ return OPERATOR_RUNNING_MODAL;
+}
+
+int WM_gesture_lines_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ return WM_gesture_lasso_modal(C, op, event);
+}
+
+void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
+{
+ gesture_modal_end(C, op);
+}
+
+void WM_gesture_lines_cancel(bContext *C, wmOperator *op)
+{
+ gesture_modal_end(C, op);
+}
+
+/**
+ * helper function, we may want to add options for conversion to view space
+ *
+ * caller must free.
+ */
+const int (*WM_gesture_lasso_path_to_array(bContext *UNUSED(C), wmOperator *op, int *mcords_tot))[2]
+{
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "path");
+ int (*mcords)[2] = NULL;
+ BLI_assert(prop != NULL);
+
+ if (prop) {
+ const int len = RNA_property_collection_length(op->ptr, prop);
+
+ if (len) {
+ int i = 0;
+ mcords = MEM_mallocN(sizeof(int) * 2 * len, __func__);
+
+ RNA_PROP_BEGIN (op->ptr, itemptr, prop)
+ {
+ float loc[2];
+
+ RNA_float_get_array(&itemptr, "loc", loc);
+ mcords[i][0] = (int)loc[0];
+ mcords[i][1] = (int)loc[1];
+ i++;
+ }
+ RNA_PROP_END;
+ }
+ *mcords_tot = len;
+ }
+ else {
+ *mcords_tot = 0;
+ }
+
+ /* cast for 'const' */
+ return (const int (*)[2])mcords;
+}
+
+#if 0
+/* template to copy from */
+
+static int gesture_lasso_exec(bContext *C, wmOperator *op)
+{
+ RNA_BEGIN (op->ptr, itemptr, "path")
+ {
+ float loc[2];
+
+ RNA_float_get_array(&itemptr, "loc", loc);
+ printf("Location: %f %f\n", loc[0], loc[1]);
+ }
+ RNA_END;
+
+ return OPERATOR_FINISHED;
+}
+
+void WM_OT_lasso_gesture(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ ot->name = "Lasso Gesture";
+ ot->idname = "WM_OT_lasso_gesture";
+ ot->description = "Select objects within the lasso as you move the pointer";
+
+ ot->invoke = WM_gesture_lasso_invoke;
+ ot->modal = WM_gesture_lasso_modal;
+ ot->exec = gesture_lasso_exec;
+
+ ot->poll = WM_operator_winactive;
+
+ prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
+}
+#endif
+
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/** \name Straight Line Gesture
+ * \{ */
+
+static bool gesture_straightline_apply(bContext *C, wmOperator *op)
+{
+ wmGesture *gesture = op->customdata;
+ rcti *rect = gesture->customdata;
+
+ if (rect->xmin == rect->xmax && rect->ymin == rect->ymax)
+ return 0;
+
+ /* operator arguments and storage. */
+ RNA_int_set(op->ptr, "xstart", rect->xmin);
+ RNA_int_set(op->ptr, "ystart", rect->ymin);
+ RNA_int_set(op->ptr, "xend", rect->xmax);
+ RNA_int_set(op->ptr, "yend", rect->ymax);
+
+ if (op->type->exec) {
+ int retval = op->type->exec(C, op);
+ OPERATOR_RETVAL_CHECK(retval);
+ }
+
+ return 1;
+}
+
+int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ PropertyRNA *prop;
+
+ op->customdata = WM_gesture_new(C, event, WM_GESTURE_STRAIGHTLINE);
+
+ if (ISTWEAK(event->type)) {
+ wmGesture *gesture = op->customdata;
+ gesture->is_active = true;
+ }
+
+ /* add modal handler */
+ WM_event_add_modal_handler(C, op);
+
+ wm_gesture_tag_redraw(C);
+
+ if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
+ WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ wmGesture *gesture = op->customdata;
+ rcti *rect = gesture->customdata;
+ int sx, sy;
+
+ if (event->type == MOUSEMOVE) {
+ wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
+
+ if (gesture->is_active == false) {
+ rect->xmin = rect->xmax = event->x - sx;
+ rect->ymin = rect->ymax = event->y - sy;
+ }
+ else {
+ rect->xmax = event->x - sx;
+ rect->ymax = event->y - sy;
+ gesture_straightline_apply(C, op);
+ }
+
+ wm_gesture_tag_redraw(C);
+ }
+ else if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case GESTURE_MODAL_BEGIN:
+ if (gesture->is_active == false) {
+ gesture->is_active = true;
+ wm_gesture_tag_redraw(C);
+ }
+ break;
+ case GESTURE_MODAL_SELECT:
+ if (gesture_straightline_apply(C, op)) {
+ gesture_modal_end(C, op);
+ return OPERATOR_FINISHED;
+ }
+ gesture_modal_end(C, op);
+ return OPERATOR_CANCELLED;
+
+ case GESTURE_MODAL_CANCEL:
+ gesture_modal_end(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void WM_gesture_straightline_cancel(bContext *C, wmOperator *op)
+{
+ gesture_modal_end(C, op);
+}
+
+#if 0
+/* template to copy from */
+void WM_OT_straightline_gesture(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ ot->name = "Straight Line Gesture";
+ ot->idname = "WM_OT_straightline_gesture";
+ ot->description = "Draw a straight line as you move the pointer";
+
+ ot->invoke = WM_gesture_straightline_invoke;
+ ot->modal = WM_gesture_straightline_modal;
+ ot->exec = gesture_straightline_exec;
+
+ ot->poll = WM_operator_winactive;
+
+ WM_operator_properties_gesture_straightline(ot, 0);
+}
+#endif
+
+/** \} */
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index c11c398c616..52682523212 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -40,6 +40,8 @@
#include "MEM_guardedalloc.h"
+#include "CLG_log.h"
+
#include "DNA_genfile.h"
#include "DNA_scene_types.h"
#include "DNA_userdef_types.h"
@@ -53,6 +55,7 @@
#include "BLI_utildefines.h"
#include "BLO_writefile.h"
+#include "BLO_undofile.h"
#include "BKE_blender.h"
#include "BKE_blender_undo.h"
@@ -110,6 +113,7 @@
#include "ED_space_api.h"
#include "ED_screen.h"
#include "ED_util.h"
+#include "ED_undo.h"
#include "UI_interface.h"
#include "BLF_api.h"
@@ -127,6 +131,11 @@
# include "BKE_subsurf.h"
#endif
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_OPERATORS, "wm.operator");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_HANDLERS, "wm.handler");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_EVENTS, "wm.event");
+CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_KEYMAPS, "wm.keymap");
+
static void wm_init_reports(bContext *C)
{
ReportList *reports = CTX_wm_reports(C);
@@ -142,11 +151,6 @@ static void wm_free_reports(bContext *C)
BKE_reports_clear(reports);
}
-static void wm_undo_kill_callback(bContext *C)
-{
- WM_jobs_kill_all_except(CTX_wm_manager(C), CTX_wm_screen(C));
-}
-
bool wm_start_with_console = false; /* used in creator.c */
/* only called once, for startup */
@@ -165,7 +169,7 @@ void WM_init(bContext *C, int argc, const char **argv)
WM_menutype_init();
WM_uilisttype_init();
- BKE_undo_callback_wm_kill_jobs_set(wm_undo_kill_callback);
+ ED_undosys_type_init();
BKE_library_callback_free_window_manager_set(wm_close_and_free); /* library.c */
BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference); /* library.c */
@@ -181,18 +185,15 @@ void WM_init(bContext *C, int argc, const char **argv)
ED_file_init(); /* for fsmenu */
ED_node_init_butfuncs();
- BLF_init(11, U.dpi); /* Please update source/gamengine/GamePlayer/GPG_ghost.cpp if you change this */
+ BLF_init(); /* Please update source/gamengine/GamePlayer/GPG_ghost.cpp if you change this */
BLT_lang_init();
- /* Enforce loading the UI for the initial homefile */
- G.fileflags &= ~G_FILE_NO_UI;
-
/* reports cant be initialized before the wm,
* but keep before file reading, since that may report errors */
wm_init_reports(C);
/* get the default database, plus a wm */
- wm_homefile_read(C, NULL, G.factory_startup, NULL);
+ wm_homefile_read(C, NULL, G.factory_startup, false, true, NULL, NULL);
BLT_lang_set(NULL);
@@ -437,6 +438,29 @@ static void wait_for_console_key(void)
}
#endif
+static int wm_exit_handler(bContext *C, const wmEvent *event, void *userdata)
+{
+ WM_exit(C);
+
+ UNUSED_VARS(event, userdata);
+ return WM_UI_HANDLER_BREAK;
+}
+
+/**
+ * Cause a delayed WM_exit() call to avoid leaking memory when trying to exit from within operators.
+ */
+void wm_exit_schedule_delayed(const bContext *C)
+{
+ /* What we do here is a little bit hacky, but quite simple and doesn't require bigger
+ * changes: Add a handler wrapping WM_exit() to cause a delayed call of it. */
+
+ wmWindow *win = CTX_wm_window(C);
+
+ /* Use modal UI handler for now. Could add separate WM handlers or so, but probably not worth it. */
+ WM_event_add_ui_handler(C, &win->modalhandlers, wm_exit_handler, NULL, NULL, 0);
+ WM_event_add_mousemove(C); /* ensure handler actually gets called */
+}
+
/**
* \note doesn't run exit() call #WM_exit() for that.
*/
@@ -444,8 +468,6 @@ void WM_exit_ext(bContext *C, const bool do_python)
{
wmWindowManager *wm = C ? CTX_wm_manager(C) : NULL;
- BKE_sound_exit();
-
/* first wrap up running stuff, we assume only the active WM is running */
/* modal handlers are on window level freed, others too? */
/* note; same code copied in wm_files.c */
@@ -453,7 +475,8 @@ void WM_exit_ext(bContext *C, const bool do_python)
wmWindow *win;
if (!G.background) {
- if ((U.uiflag2 & USER_KEEP_SESSION) || BKE_undo_is_valid(NULL)) {
+ struct MemFile *undo_memfile = wm->undo_stack ? ED_undosys_stack_memfile_get_active(wm->undo_stack) : NULL;
+ if ((U.uiflag2 & USER_KEEP_SESSION) || (undo_memfile != NULL)) {
/* save the undo state as quit.blend */
char filename[FILE_MAX];
bool has_edited;
@@ -464,7 +487,7 @@ void WM_exit_ext(bContext *C, const bool do_python)
has_edited = ED_editors_flush_edits(C, false);
if ((has_edited && BLO_write_file(CTX_data_main(C), filename, fileflags, NULL, NULL)) ||
- BKE_undo_save_file(filename))
+ (undo_memfile && BLO_memfile_write_file(undo_memfile, filename)))
{
printf("Saved session recovery to '%s'\n", filename);
}
@@ -487,11 +510,13 @@ void WM_exit_ext(bContext *C, const bool do_python)
wm_dropbox_free();
WM_menutype_free();
WM_uilisttype_free();
-
+
/* all non-screen and non-space stuff editors did, like editmode */
if (C)
ED_editors_exit(C);
+ ED_undosys_type_free();
+
// XXX
// BIF_GlobalReebFree();
// BIF_freeRetarget();
@@ -569,12 +594,10 @@ void WM_exit_ext(bContext *C, const bool do_python)
GPU_exit();
}
- BKE_undo_reset();
-
ED_file_exit(); /* for fsmenu */
UI_exit();
- BKE_blender_userdef_free();
+ BKE_blender_userdef_data_free(&U, false);
RNA_exit(); /* should be after BPY_python_end so struct python slots are cleared */
@@ -591,6 +614,12 @@ void WM_exit_ext(bContext *C, const bool do_python)
BLI_threadapi_exit();
+ /* No need to call this early, rather do it late so that other pieces of Blender using sound may exit cleanly,
+ * see also T50676. */
+ BKE_sound_exit();
+
+ CLG_exit();
+
BKE_blender_atexit();
if (MEM_get_memory_blocks_in_use() != 0) {
@@ -605,6 +634,10 @@ void WM_exit_ext(bContext *C, const bool do_python)
BKE_tempdir_session_purge();
}
+/**
+ * \brief Main exit function to close Blender ordinarily.
+ * \note Use #wm_exit_schedule_delayed() to close Blender from an operator. Might leak memory otherwise.
+ */
void WM_exit(bContext *C)
{
WM_exit_ext(C, 1);
diff --git a/source/blender/windowmanager/intern/wm_jobs.c b/source/blender/windowmanager/intern/wm_jobs.c
index 5580d3217a5..f9fe787ed2d 100644
--- a/source/blender/windowmanager/intern/wm_jobs.c
+++ b/source/blender/windowmanager/intern/wm_jobs.c
@@ -379,7 +379,7 @@ static void wm_jobs_test_suspend_stop(wmWindowManager *wm, wmJob *test)
}
}
- /* possible suspend ourselfs, waiting for other jobs, or de-suspend */
+ /* Possible suspend ourselves, waiting for other jobs, or de-suspend. */
test->suspended = suspend;
// if (suspend) printf("job suspended: %s\n", test->name);
}
@@ -418,8 +418,8 @@ void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
// printf("job started: %s\n", wm_job->name);
- BLI_init_threads(&wm_job->threads, do_job_thread, 1);
- BLI_insert_thread(&wm_job->threads, wm_job);
+ BLI_threadpool_init(&wm_job->threads, do_job_thread, 1);
+ BLI_threadpool_insert(&wm_job->threads, wm_job);
}
/* restarted job has timer already */
@@ -450,7 +450,7 @@ static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *wm_job)
wm_job->stop = true;
WM_job_main_thread_lock_release(wm_job);
- BLI_end_threads(&wm_job->threads);
+ BLI_threadpool_end(&wm_job->threads);
WM_job_main_thread_lock_acquire(wm_job);
if (wm_job->endjob)
@@ -601,7 +601,7 @@ void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt)
wm_job->running = false;
WM_job_main_thread_lock_release(wm_job);
- BLI_end_threads(&wm_job->threads);
+ BLI_threadpool_end(&wm_job->threads);
WM_job_main_thread_lock_acquire(wm_job);
if (wm_job->endnote)
diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c
index e201fa433d4..bcfc97a1e23 100644
--- a/source/blender/windowmanager/intern/wm_keymap.c
+++ b/source/blender/windowmanager/intern/wm_keymap.c
@@ -1,4 +1,5 @@
/*
+ *
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
@@ -39,6 +40,7 @@
#include "DNA_windowmanager_types.h"
#include "MEM_guardedalloc.h"
+#include "CLG_log.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
@@ -121,6 +123,13 @@ static void wm_keymap_item_properties_update_ot(wmKeyMapItem *kmi)
if (ot->srna != kmi->ptr->type) {
/* matches wm_keymap_item_properties_set but doesnt alloc new ptr */
WM_operator_properties_create_ptr(kmi->ptr, ot);
+ /* 'kmi->ptr->data' NULL'd above, keep using existing properties.
+ * Note: the operators property types may have changed,
+ * we will need a more comprehensive sanitize function to support this properly.
+ */
+ if (kmi->properties) {
+ kmi->ptr->data = kmi->properties;
+ }
WM_operator_properties_sanitize(kmi->ptr, 1);
}
}
@@ -391,6 +400,15 @@ bool WM_keymap_remove(wmKeyConfig *keyconf, wmKeyMap *keymap)
}
}
+
+bool WM_keymap_poll(bContext *C, wmKeyMap *keymap)
+{
+ if (keymap->poll != NULL) {
+ return keymap->poll(C);
+ }
+ return true;
+}
+
static void keymap_event_set(wmKeyMapItem *kmi, short type, short val, int modifier, short keymodifier)
{
kmi->type = type;
@@ -778,7 +796,7 @@ wmKeyMap *WM_keymap_find_all(const bContext *C, const char *idname, int spaceid,
/* modal maps get linked to a running operator, and filter the keys before sending to modal() callback */
-wmKeyMap *WM_modalkeymap_add(wmKeyConfig *keyconf, const char *idname, EnumPropertyItem *items)
+wmKeyMap *WM_modalkeymap_add(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
{
wmKeyMap *km = WM_keymap_find(keyconf, idname, 0, 0);
km->flag |= KEYMAP_MODAL;
@@ -870,11 +888,13 @@ wmKeyMapItem *WM_modalkeymap_find_propvalue(wmKeyMap *km, const int propvalue)
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
{
wmOperatorType *ot = WM_operatortype_find(opname, 0);
-
- if (ot)
+
+ if (ot) {
ot->modalkeymap = km;
- else
- printf("error: modalkeymap_assign, unknown operator %s\n", opname);
+ }
+ else {
+ CLOG_ERROR(WM_LOG_KEYMAPS, "unknown operator '%s'", opname);
+ }
}
static void wm_user_modal_keymap_set_items(wmWindowManager *wm, wmKeyMap *km)
@@ -912,7 +932,7 @@ static void wm_user_modal_keymap_set_items(wmWindowManager *wm, wmKeyMap *km)
const char *WM_key_event_string(const short type, const bool compact)
{
- EnumPropertyItem *it;
+ const EnumPropertyItem *it;
const int i = RNA_enum_from_value(rna_enum_event_type_items, (int)type);
if (i == -1) {
@@ -933,7 +953,7 @@ const char *WM_key_event_string(const short type, const bool compact)
int WM_keymap_item_raw_to_string(
const short shift, const short ctrl, const short alt, const short oskey,
const short keymodifier, const short val, const short type, const bool compact,
- const int len, char *r_str)
+ char *result, const int result_len)
{
#define ADD_SEP if (p != buf) *p++ = ' '; (void)0
@@ -993,20 +1013,23 @@ int WM_keymap_item_raw_to_string(
BLI_assert(p - buf < sizeof(buf));
/* We need utf8 here, otherwise we may 'cut' some unicode chars like arrows... */
- return BLI_strncpy_utf8_rlen(r_str, buf, len);
+ return BLI_strncpy_utf8_rlen(result, buf, result_len);
#undef ADD_SEP
}
-int WM_keymap_item_to_string(wmKeyMapItem *kmi, const bool compact, const int len, char *r_str)
+int WM_keymap_item_to_string(
+ wmKeyMapItem *kmi, const bool compact,
+ char *result, const int result_len)
{
return WM_keymap_item_raw_to_string(
- kmi->shift, kmi->ctrl, kmi->alt, kmi->oskey, kmi->keymodifier, kmi->val, kmi->type,
- compact, len, r_str);
+ kmi->shift, kmi->ctrl, kmi->alt, kmi->oskey, kmi->keymodifier, kmi->val, kmi->type,
+ compact, result, result_len);
}
int WM_modalkeymap_items_to_string(
- wmKeyMap *km, const int propvalue, const bool compact, const int len, char *r_str)
+ wmKeyMap *km, const int propvalue, const bool compact,
+ char *result, const int result_len)
{
int totlen = 0;
bool add_sep = false;
@@ -1016,17 +1039,17 @@ int WM_modalkeymap_items_to_string(
/* Find all shortcuts related to that propvalue! */
for (kmi = WM_modalkeymap_find_propvalue(km, propvalue);
- kmi && totlen < (len - 2);
+ kmi && totlen < (result_len - 2);
kmi = wm_modalkeymap_find_propvalue_iter(km, kmi, propvalue))
{
if (add_sep) {
- r_str[totlen++] = '/';
- r_str[totlen] = '\0';
+ result[totlen++] = '/';
+ result[totlen] = '\0';
}
else {
add_sep = true;
}
- totlen += WM_keymap_item_to_string(kmi, compact, len - totlen, &r_str[totlen]);
+ totlen += WM_keymap_item_to_string(kmi, compact, &result[totlen], result_len - totlen);
}
}
@@ -1034,25 +1057,26 @@ int WM_modalkeymap_items_to_string(
}
int WM_modalkeymap_operator_items_to_string(
- wmOperatorType *ot, const int propvalue, const bool compact, const int len, char *r_str)
+ wmOperatorType *ot, const int propvalue, const bool compact,
+ char *result, const int result_len)
{
- return WM_modalkeymap_items_to_string(ot->modalkeymap, propvalue, compact, len, r_str);
+ return WM_modalkeymap_items_to_string(ot->modalkeymap, propvalue, compact, result, result_len);
}
char *WM_modalkeymap_operator_items_to_string_buf(
wmOperatorType *ot, const int propvalue, const bool compact,
- const int max_len, int *r_available_len, char **r_str)
+ const int max_len, int *r_available_len, char **r_result)
{
- char *ret = *r_str;
+ char *ret = *r_result;
if (*r_available_len > 1) {
int used_len = WM_modalkeymap_operator_items_to_string(
- ot, propvalue, compact, min_ii(*r_available_len, max_len), ret) + 1;
+ ot, propvalue, compact, ret, min_ii(*r_available_len, max_len)) + 1;
*r_available_len -= used_len;
- *r_str += used_len;
+ *r_result += used_len;
if (*r_available_len == 0) {
- (*r_str)--; /* So that *str keeps pointing on a valid char, we'll stay on it anyway. */
+ (*r_result)--; /* So that *result keeps pointing on a valid char, we'll stay on it anyway. */
}
}
else {
@@ -1076,7 +1100,7 @@ static wmKeyMapItem *wm_keymap_item_find_handlers(
for (handler = handlers->first; handler; handler = handler->next) {
keymap = WM_keymap_active(wm, handler->keymap);
- if (keymap && (!keymap->poll || keymap->poll((bContext *)C))) {
+ if (keymap && WM_keymap_poll((bContext *)C, keymap)) {
for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
/* skip disabled keymap items [T38447] */
if (kmi->flag & KMI_INACTIVE)
@@ -1119,7 +1143,7 @@ static wmKeyMapItem *wm_keymap_item_find_handlers(
if (IDP_EqualsProperties_ex(properties, properties_default, is_strict)) {
char kmi_str[128];
- WM_keymap_item_to_string(kmi, false, sizeof(kmi_str), kmi_str);
+ WM_keymap_item_to_string(kmi, false, kmi_str, sizeof(kmi_str));
/* Note gievn properties could come from other things than menu entry... */
printf("%s: Some set values in menu entry match default op values, "
"this might not be desired!\n", opname);
@@ -1269,7 +1293,7 @@ static wmKeyMapItem *wm_keymap_item_find(
kmi = wm_keymap_item_find_props(C, opname, opcontext, properties_default, is_strict, is_hotkey, &km);
if (kmi) {
char kmi_str[128];
- WM_keymap_item_to_string(kmi, false, sizeof(kmi_str), kmi_str);
+ WM_keymap_item_to_string(kmi, false, kmi_str, sizeof(kmi_str));
printf("%s: Some set values in keymap entry match default op values, "
"this might not be desired!\n", opname);
printf("\tkm: '%s', kmi: '%s'\n", km->idname, kmi_str);
@@ -1295,13 +1319,14 @@ static wmKeyMapItem *wm_keymap_item_find(
char *WM_key_event_operator_string(
const bContext *C, const char *opname, int opcontext,
- IDProperty *properties, const bool is_strict, int len, char *r_str)
+ IDProperty *properties, const bool is_strict,
+ char *result, const int result_len)
{
wmKeyMapItem *kmi = wm_keymap_item_find(C, opname, opcontext, properties, false, is_strict, NULL);
-
+
if (kmi) {
- WM_keymap_item_to_string(kmi, false, len, r_str);
- return r_str;
+ WM_keymap_item_to_string(kmi, false, result, result_len);
+ return result;
}
return NULL;
@@ -1707,7 +1732,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
km = WM_keymap_find_all(C, "Mesh", 0, 0);
/* some mesh operators are active in object mode too, like add-prim */
- if (km && km->poll && km->poll((bContext *)C) == 0) {
+ if (km && !WM_keymap_poll((bContext *)C, km)) {
km = WM_keymap_find_all(C, "Object Mode", 0, 0);
}
}
@@ -1717,7 +1742,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
km = WM_keymap_find_all(C, "Curve", 0, 0);
/* some curve operators are active in object mode too, like add-prim */
- if (km && km->poll && km->poll((bContext *)C) == 0) {
+ if (km && !WM_keymap_poll((bContext *)C, km)) {
km = WM_keymap_find_all(C, "Object Mode", 0, 0);
}
}
@@ -1745,7 +1770,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
km = WM_keymap_find_all(C, "Metaball", 0, 0);
/* some mball operators are active in object mode too, like add-prim */
- if (km && km->poll && km->poll((bContext *)C) == 0) {
+ if (km && !WM_keymap_poll((bContext *)C, km)) {
km = WM_keymap_find_all(C, "Object Mode", 0, 0);
}
}
@@ -1797,7 +1822,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
* Mesh keymap is probably not ideal, but best place I could find to put those. */
if (sl->spacetype == SPACE_VIEW3D) {
km = WM_keymap_find_all(C, "Mesh", 0, 0);
- if (km && km->poll && !km->poll((bContext *)C)) {
+ if (km && !WM_keymap_poll((bContext *)C, km)) {
km = NULL;
}
}
diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c
index 18836f34c99..72e4ff1d83a 100644
--- a/source/blender/windowmanager/intern/wm_operator_props.c
+++ b/source/blender/windowmanager/intern/wm_operator_props.c
@@ -49,7 +49,7 @@ void WM_operator_properties_filesel(
{
PropertyRNA *prop;
- static EnumPropertyItem file_display_items[] = {
+ static const EnumPropertyItem file_display_items[] = {
{FILE_DEFAULTDISPLAY, "DEFAULT", 0, "Default", "Automatically determine display type for files"},
{FILE_SHORTDISPLAY, "LIST_SHORT", ICON_SHORTDISPLAY, "Short List", "Display files as short list"},
{FILE_LONGDISPLAY, "LIST_LONG", ICON_LONGDISPLAY, "Long List", "Display files as a detailed list"},
@@ -135,7 +135,7 @@ static void wm_operator_properties_select_action_ex(wmOperatorType *ot, int defa
void WM_operator_properties_select_action(wmOperatorType *ot, int default_action)
{
- static EnumPropertyItem select_actions[] = {
+ static const EnumPropertyItem select_actions[] = {
{SEL_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle selection for all elements"},
{SEL_SELECT, "SELECT", 0, "Select", "Select all elements"},
{SEL_DESELECT, "DESELECT", 0, "Deselect", "Deselect all elements"},
@@ -151,7 +151,7 @@ void WM_operator_properties_select_action(wmOperatorType *ot, int default_action
*/
void WM_operator_properties_select_action_simple(wmOperatorType *ot, int default_action)
{
- static EnumPropertyItem select_actions[] = {
+ static const EnumPropertyItem select_actions[] = {
{SEL_SELECT, "SELECT", 0, "Select", "Select all elements"},
{SEL_DESELECT, "DESELECT", 0, "Deselect", "Deselect all elements"},
{0, NULL, 0, NULL, NULL}
@@ -224,30 +224,69 @@ void WM_operator_properties_border_to_rctf(struct wmOperator *op, rctf *rect)
BLI_rctf_rcti_copy(rect, &rect_i);
}
-void WM_operator_properties_gesture_border(wmOperatorType *ot, bool extend)
+/**
+ * Use with #WM_gesture_border_invoke
+ */
+void WM_operator_properties_gesture_border_ex(wmOperatorType *ot, bool deselect, bool extend)
{
- RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
-
WM_operator_properties_border(ot);
+ if (deselect) {
+ RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Deselect rather than select items");
+ }
if (extend) {
RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection instead of deselecting everything first");
}
}
-void WM_operator_properties_mouse_select(wmOperatorType *ot)
+void WM_operator_properties_gesture_border_select(wmOperatorType *ot)
+{
+ WM_operator_properties_gesture_border_ex(ot, true, true);
+}
+void WM_operator_properties_gesture_border(wmOperatorType *ot)
+{
+ WM_operator_properties_gesture_border_ex(ot, false, false);
+}
+
+void WM_operator_properties_gesture_border_zoom(wmOperatorType *ot)
{
+ WM_operator_properties_border(ot);
+
PropertyRNA *prop;
+ prop = RNA_def_boolean(ot->srna, "zoom_out", false, "Zoom Out", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
- prop = RNA_def_boolean(ot->srna, "extend", false, "Extend",
- "Extend selection instead of deselecting everything first");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Remove from selection");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle Selection", "Toggle the selection");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+/**
+ * Use with #WM_gesture_lasso_invoke
+ */
+void WM_operator_properties_gesture_lasso_ex(wmOperatorType *ot, bool deselect, bool extend)
+{
+ PropertyRNA *prop;
+ prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
+ if (deselect) {
+ RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Deselect rather than select items");
+ }
+ if (extend) {
+ RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection instead of deselecting everything first");
+ }
}
+void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
+{
+ WM_operator_properties_gesture_lasso_ex(ot, false, false);
+}
+
+void WM_operator_properties_gesture_lasso_select(wmOperatorType *ot)
+{
+ WM_operator_properties_gesture_lasso_ex(ot, true, true);
+}
+
+/**
+ * Use with #WM_gesture_straightline_invoke
+ */
void WM_operator_properties_gesture_straightline(wmOperatorType *ot, int cursor)
{
PropertyRNA *prop;
@@ -269,6 +308,48 @@ void WM_operator_properties_gesture_straightline(wmOperatorType *ot, int cursor)
}
/**
+ * Use with #WM_gesture_circle_invoke
+ */
+void WM_operator_properties_gesture_circle_ex(wmOperatorType *ot, bool deselect)
+{
+ PropertyRNA *prop;
+ const int radius_default = 25;
+
+ prop = RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ prop = RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_int(ot->srna, "radius", radius_default, 1, INT_MAX, "Radius", "", 1, INT_MAX);
+
+ if (deselect) {
+ RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Deselect rather than select items");
+ }
+}
+
+void WM_operator_properties_gesture_circle(wmOperatorType *ot)
+{
+ WM_operator_properties_gesture_circle_ex(ot, false);
+}
+
+void WM_operator_properties_gesture_circle_select(wmOperatorType *ot)
+{
+ WM_operator_properties_gesture_circle_ex(ot, true);
+}
+
+void WM_operator_properties_mouse_select(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_boolean(ot->srna, "extend", false, "Extend",
+ "Extend selection instead of deselecting everything first");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Remove from selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle Selection", "Toggle the selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
+/**
* \param nth_can_disable: Enable if we want to be able to select no interval at all.
*/
void WM_operator_properties_checker_interval(wmOperatorType *ot, bool nth_can_disable)
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index cef5e7e4a8e..f46358f83cf 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -46,6 +46,8 @@
#include "MEM_guardedalloc.h"
+#include "CLG_log.h"
+
#include "DNA_ID.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
@@ -58,7 +60,7 @@
#include "PIL_time.h"
#include "BLI_blenlib.h"
-#include "BLI_dial.h"
+#include "BLI_dial_2d.h"
#include "BLI_dynstr.h" /*for WM_operator_pystring */
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -94,7 +96,7 @@
#include "ED_numinput.h"
#include "ED_screen.h"
-#include "ED_util.h"
+#include "ED_undo.h"
#include "ED_view3d.h"
#include "GPU_basic_shader.h"
@@ -115,7 +117,6 @@
#include "wm_event_system.h"
#include "wm_event_types.h"
#include "wm_files.h"
-#include "wm_subwindow.h"
#include "wm_window.h"
static GHash *global_ops_hash = NULL;
@@ -140,12 +141,12 @@ wmOperatorType *WM_operatortype_find(const char *idname, bool quiet)
}
if (!quiet) {
- printf("search for unknown operator '%s', '%s'\n", idname_bl, idname);
+ CLOG_INFO(WM_LOG_OPERATORS, 0, "search for unknown operator '%s', '%s'\n", idname_bl, idname);
}
}
else {
if (!quiet) {
- printf("search for empty operator\n");
+ CLOG_INFO(WM_LOG_OPERATORS, 0, "search for empty operator");
}
}
@@ -171,13 +172,12 @@ void WM_operatortype_append(void (*opfunc)(wmOperatorType *))
opfunc(ot);
if (ot->name == NULL) {
- fprintf(stderr, "ERROR: Operator %s has no name property!\n", ot->idname);
- ot->name = N_("Dummy Name");
+ CLOG_ERROR(WM_LOG_OPERATORS, "Operator '%s' has no name property", ot->idname);
}
/* XXX All ops should have a description but for now allow them not to. */
RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP);
- RNA_def_struct_identifier(ot->srna, ot->idname);
+ RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
}
@@ -193,7 +193,7 @@ void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void *
ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
opfunc(ot, userdata);
RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP);
- RNA_def_struct_identifier(ot->srna, ot->idname);
+ RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
}
@@ -256,7 +256,7 @@ static int wm_macro_exec(bContext *C, wmOperator *op)
}
}
else {
- printf("%s: '%s' cant exec macro\n", __func__, opm->type->idname);
+ CLOG_WARN(WM_LOG_OPERATORS, "'%s' cant exec macro", opm->type->idname);
}
}
@@ -301,8 +301,9 @@ static int wm_macro_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperator *opm = op->opm;
int retval = OPERATOR_FINISHED;
- if (opm == NULL)
- printf("%s: macro error, calling NULL modal()\n", __func__);
+ if (opm == NULL) {
+ CLOG_ERROR(WM_LOG_OPERATORS, "macro error, calling NULL modal()");
+ }
else {
retval = opm->type->modal(C, opm, event);
OPERATOR_RETVAL_CHECK(retval);
@@ -376,7 +377,7 @@ wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *nam
const char *i18n_context;
if (WM_operatortype_find(idname, true)) {
- printf("%s: macro error: operator %s exists\n", __func__, idname);
+ CLOG_ERROR(WM_LOG_OPERATORS, "operator %s exists, cannot create macro", idname);
return NULL;
}
@@ -398,7 +399,7 @@ wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *nam
ot->description = UNDOCUMENTED_OPERATOR_TIP;
RNA_def_struct_ui_text(ot->srna, ot->name, ot->description);
- RNA_def_struct_identifier(ot->srna, ot->idname);
+ RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
/* Use i18n context from ext.srna if possible (py operators). */
i18n_context = ot->ext.srna ? RNA_struct_translation_context(ot->ext.srna) : BLT_I18NCONTEXT_OPERATOR_DEFAULT;
RNA_def_struct_translation_context(ot->srna, i18n_context);
@@ -432,7 +433,7 @@ void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType *, void *),
opfunc(ot, userdata);
RNA_def_struct_ui_text(ot->srna, ot->name, ot->description);
- RNA_def_struct_identifier(ot->srna, ot->idname);
+ RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
}
@@ -573,6 +574,46 @@ void WM_operator_bl_idname(char *to, const char *from)
}
/**
+ * Sanity check to ensure #WM_operator_bl_idname won't fail.
+ * \returns true when there are no problems with \a idname, otherwise report an error.
+ */
+bool WM_operator_py_idname_ok_or_report(ReportList *reports, const char *classname, const char *idname)
+{
+ const char *ch = idname;
+ int dot = 0;
+ int i;
+ for (i = 0; *ch; i++, ch++) {
+ if ((*ch >= 'a' && *ch <= 'z') || (*ch >= '0' && *ch <= '9') || *ch == '_') {
+ /* pass */
+ }
+ else if (*ch == '.') {
+ dot++;
+ }
+ else {
+ BKE_reportf(reports, RPT_ERROR,
+ "Registering operator class: '%s', invalid bl_idname '%s', at position %d",
+ classname, idname, i);
+ return false;
+ }
+ }
+
+ if (i > (MAX_NAME - 3)) {
+ BKE_reportf(reports, RPT_ERROR, "Registering operator class: '%s', invalid bl_idname '%s', "
+ "is too long, maximum length is %d", classname, idname,
+ MAX_NAME - 3);
+ return false;
+ }
+
+ if (dot != 1) {
+ BKE_reportf(reports, RPT_ERROR,
+ "Registering operator class: '%s', invalid bl_idname '%s', must contain 1 '.' character",
+ classname, idname);
+ return false;
+ }
+ return true;
+}
+
+/**
* Print a string representation of the operator, with the args that it runs so python can run it again.
*
* When calling from an existing wmOperator, better to use simple version:
@@ -806,9 +847,19 @@ static char *wm_prop_pystring_from_context(bContext *C, PointerRNA *ptr, Propert
} \
} (void)0
+#define CTX_TEST_SPACE_TYPE(space_data_type, member_full, dataptr_cmp) \
+ { \
+ const char *ctx_member_full = member_full; \
+ if (space_data->spacetype == space_data_type && ptr->data == dataptr_cmp) { \
+ member_id = ctx_member_full; \
+ break; \
+ } \
+ } (void)0
+
switch (GS(((ID *)ptr->id.data)->name)) {
case ID_SCE:
{
+ CTX_TEST_PTR_DATA_TYPE(C, "active_gpencil_brush", RNA_GPencilBrush, ptr, CTX_data_active_gpencil_brush(C));
CTX_TEST_PTR_ID(C, "scene", ptr->id.data);
break;
}
@@ -843,12 +894,22 @@ static char *wm_prop_pystring_from_context(bContext *C, PointerRNA *ptr, Propert
{
CTX_TEST_PTR_ID(C, "screen", ptr->id.data);
- CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_Space, ptr, CTX_wm_space_data(C));
+ SpaceLink *space_data = CTX_wm_space_data(C);
+
+ CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_Space, ptr, space_data);
CTX_TEST_PTR_DATA_TYPE(C, "area", RNA_Area, ptr, CTX_wm_area(C));
CTX_TEST_PTR_DATA_TYPE(C, "region", RNA_Region, ptr, CTX_wm_region(C));
+ CTX_TEST_SPACE_TYPE(SPACE_IMAGE, "space_data.uv_editor", space_data);
+ CTX_TEST_SPACE_TYPE(SPACE_VIEW3D, "space_data.fx_settings", &(CTX_wm_view3d(C)->fx_settings));
+ CTX_TEST_SPACE_TYPE(SPACE_NLA, "space_data.dopesheet", CTX_wm_space_nla(C)->ads);
+ CTX_TEST_SPACE_TYPE(SPACE_IPO, "space_data.dopesheet", CTX_wm_space_graph(C)->ads);
+ CTX_TEST_SPACE_TYPE(SPACE_ACTION, "space_data.dopesheet", &(CTX_wm_space_action(C)->ads));
+ CTX_TEST_SPACE_TYPE(SPACE_FILE, "space_data.params", CTX_wm_space_file(C)->params);
break;
}
+ default:
+ break;
}
if (member_id) {
@@ -860,6 +921,7 @@ static char *wm_prop_pystring_from_context(bContext *C, PointerRNA *ptr, Propert
}
#undef CTX_TEST_PTR_ID
#undef CTX_TEST_PTR_ID_CAST
+#undef CTX_TEST_SPACE_TYPE
}
return ret;
@@ -900,7 +962,7 @@ void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
{
- wmOperatorType *ot = WM_operatortype_find(opstring, 0);
+ wmOperatorType *ot = WM_operatortype_find(opstring, false);
if (ot)
WM_operator_properties_create_ptr(ptr, ot);
@@ -1075,11 +1137,14 @@ int WM_menu_invoke_ex(bContext *C, wmOperator *op, int opcontext)
uiLayout *layout;
if (prop == NULL) {
- printf("%s: %s has no enum property set\n", __func__, op->type->idname);
+ CLOG_ERROR(WM_LOG_OPERATORS,
+ "'%s' has no enum property set",
+ op->type->idname);
}
else if (RNA_property_type(prop) != PROP_ENUM) {
- printf("%s: %s \"%s\" is not an enum property\n",
- __func__, op->type->idname, RNA_property_identifier(prop));
+ CLOG_ERROR(WM_LOG_OPERATORS,
+ "'%s', '%s' is not an enum property",
+ op->type->idname, RNA_property_identifier(prop));
}
else if (RNA_property_is_set(op->ptr, prop)) {
const int retval = op->type->exec(C, op);
@@ -1118,6 +1183,7 @@ static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg_op)
block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
+ search[0] = '\0';
#if 0 /* ok, this isn't so easy... */
uiDefBut(block, UI_BTYPE_LABEL, 0, RNA_struct_ui_name(op->type->srna), 10, 10, UI_searchbox_size_x(), UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
#endif
@@ -1162,7 +1228,7 @@ int WM_operator_confirm_message_ex(bContext *C, wmOperator *op,
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);
+ uiItemFullO_ptr(layout, op->type, message, ICON_NONE, properties, WM_OP_EXEC_REGION_WIN, 0, NULL);
UI_popup_menu_end(C, pup);
return OPERATOR_INTERFACE;
@@ -1345,13 +1411,13 @@ static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op)
if (op->type->flag & OPTYPE_MACRO) {
for (op = op->macro.first; op; op = op->next) {
- uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
+ uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'H', UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
if (op->next)
uiItemS(layout);
}
}
else {
- uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
+ uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'H', UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
}
UI_block_bounds_set_popup(block, 4, 0, 0);
@@ -1372,7 +1438,7 @@ static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
wmOpPopUp *data = arg1;
uiBlock *block = arg2;
- /* Explicitly set UI_RETURN_OK flag, otherwise the menu might be cancelled
+ /* Explicitly set UI_RETURN_OK flag, otherwise the menu might be canceled
* in case WM_operator_call_ex exits/reloads the current file (T49199). */
UI_popup_menu_retval_set(block, UI_RETURN_OK, true);
@@ -1402,20 +1468,6 @@ static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
}
}
-static void popup_check_cb(bContext *C, void *op_ptr, void *UNUSED(arg))
-{
- wmOperator *op = op_ptr;
- if (op->type->check) {
- if (op->type->check(C, op)) {
- /* check for popup and re-layout buttons */
- ARegion *ar_menu = CTX_wm_menu(C);
- if (ar_menu) {
- ED_region_tag_refresh_ui(ar_menu);
- }
- }
- }
-}
-
/* Dialogs are popups that require user verification (click OK) before exec */
static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData)
{
@@ -1434,9 +1486,7 @@ static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData)
layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style);
- UI_block_func_set(block, popup_check_cb, op, NULL);
-
- uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
+ uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'H', UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
/* clear so the OK button is left alone */
UI_block_func_set(block, NULL, NULL, NULL);
@@ -1474,10 +1524,8 @@ static uiBlock *wm_operator_ui_create(bContext *C, ARegion *ar, void *userData)
layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style);
- UI_block_func_set(block, popup_check_cb, op, NULL);
-
/* since ui is defined the auto-layout args are not used */
- uiLayoutOperatorButs(C, layout, op, NULL, 'V', 0);
+ uiTemplateOperatorPropertyButs(C, layout, op, NULL, 'V', 0);
UI_block_func_set(block, NULL, NULL, NULL);
@@ -1522,7 +1570,7 @@ int WM_operator_ui_popup(bContext *C, wmOperator *op, int width, int height)
data->width = width;
data->height = height;
data->free_op = true; /* if this runs and gets registered we may want not to free it */
- UI_popup_block_ex(C, wm_operator_ui_create, NULL, wm_operator_ui_popup_cancel, data);
+ UI_popup_block_ex(C, wm_operator_ui_create, NULL, wm_operator_ui_popup_cancel, data, op);
return OPERATOR_RUNNING_MODAL;
}
@@ -1552,7 +1600,7 @@ static int wm_operator_props_popup_ex(bContext *C, wmOperator *op,
if (!do_redo || !(U.uiflag & USER_GLOBALUNDO))
return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, UI_UNIT_Y);
- UI_popup_block_ex(C, wm_block_create_redo, NULL, wm_block_redo_cancel_cb, op);
+ UI_popup_block_ex(C, wm_block_create_redo, NULL, wm_block_redo_cancel_cb, op, op);
if (do_call)
wm_block_redo_cb(C, op, 0);
@@ -1594,7 +1642,7 @@ int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, int h
data->free_op = true; /* if this runs and gets registered we may want not to free it */
/* op is not executed until popup OK but is clicked */
- UI_popup_block_ex(C, wm_block_dialog_create, wm_operator_ui_popup_ok, wm_operator_ui_popup_cancel, data);
+ UI_popup_block_ex(C, wm_block_dialog_create, wm_operator_ui_popup_ok, wm_operator_ui_popup_cancel, data, op);
return OPERATOR_RUNNING_MODAL;
}
@@ -1761,6 +1809,37 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
ibuf = IMB_ibImageFromMemory((unsigned char *)datatoc_splash_png,
datatoc_splash_png_size, IB_rect, NULL, "<splash screen>");
}
+
+ /* overwrite splash with template image */
+ if (U.app_template[0] != '\0') {
+ ImBuf *ibuf_template = NULL;
+ char splash_filepath[FILE_MAX];
+ char template_directory[FILE_MAX];
+
+ if (BKE_appdir_app_template_id_search(
+ U.app_template,
+ template_directory, sizeof(template_directory)))
+ {
+ BLI_join_dirfile(
+ splash_filepath, sizeof(splash_filepath), template_directory,
+ (U.pixelsize == 2) ? "splash_2x.png" : "splash.png");
+ ibuf_template = IMB_loadiffname(splash_filepath, IB_rect, NULL);
+ if (ibuf_template) {
+ const int x_expect = ibuf->x;
+ const int y_expect = 230 * (int)U.pixelsize;
+ /* don't cover the header text */
+ if (ibuf_template->x == x_expect && ibuf_template->y == y_expect) {
+ memcpy(ibuf->rect, ibuf_template->rect, ibuf_template->x * ibuf_template->y * sizeof(char[4]));
+ }
+ else {
+ CLOG_ERROR(WM_LOG_OPERATORS,
+ "Splash expected %dx%d found %dx%d, ignoring: %s\n",
+ x_expect, y_expect, ibuf_template->x, ibuf_template->y, splash_filepath);
+ }
+ IMB_freeImBuf(ibuf_template);
+ }
+ }
+ }
#endif
block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
@@ -1786,13 +1865,13 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
if (version_suffix != NULL && version_suffix[0]) {
/* placed after the version number in the image,
* placing y is tricky to match baseline */
- int x = 260 - (2 * UI_DPI_WINDOW_FAC);
- int y = 242 + (4 * UI_DPI_WINDOW_FAC);
- int w = 240;
+ int x = 260 * U.pixelsize - (2 * UI_DPI_FAC);
+ int y = 242 * U.pixelsize + (4 * UI_DPI_FAC);
+ int w = 240 * U.pixelsize;
/* hack to have text draw 'text_sel' */
UI_block_emboss_set(block, UI_EMBOSS_NONE);
- but = uiDefBut(block, UI_BTYPE_LABEL, 0, version_suffix, x * U.pixelsize, y * U.pixelsize, w * U.pixelsize, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
+ but = uiDefBut(block, UI_BTYPE_LABEL, 0, version_suffix, x, y, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
/* XXX, set internal flag - UI_SELECT */
UI_but_flag_enable(but, 1);
UI_block_emboss_set(block, UI_EMBOSS);
@@ -1819,10 +1898,7 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
UI_block_emboss_set(block, UI_EMBOSS);
/* show the splash menu (containing interaction presets), using python */
if (mt) {
- Menu menu = {NULL};
- menu.layout = layout;
- menu.type = mt;
- mt->draw(C, &menu);
+ UI_menutype_draw(C, mt, layout);
// wmWindowManager *wm = CTX_wm_manager(C);
// uiItemM(layout, C, "USERPREF_MT_keyconfigs", U.keyconfigstr, ICON_NONE);
@@ -1849,13 +1925,11 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
"https://docs.blender.org/manual/en/dev/");
uiItemStringO(col, IFACE_("Blender Website"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org");
if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) {
- BLI_snprintf(url, sizeof(url), "http://www.blender.org/documentation/blender_python_api_%d_%d"
- STRINGIFY(BLENDER_VERSION_CHAR) "_release",
+ BLI_snprintf(url, sizeof(url), "https://docs.blender.org/api/%d.%d"STRINGIFY(BLENDER_VERSION_CHAR),
BLENDER_VERSION / 100, BLENDER_VERSION % 100);
}
else {
- BLI_snprintf(url, sizeof(url), "http://www.blender.org/documentation/blender_python_api_%d_%d_%d",
- BLENDER_VERSION / 100, BLENDER_VERSION % 100, BLENDER_SUBVERSION);
+ BLI_snprintf(url, sizeof(url), "https://docs.blender.org/api/master");
}
uiItemStringO(col, IFACE_("Python API Reference"), ICON_URL, "WM_OT_url_open", "url", url);
uiItemL(col, "", ICON_NONE);
@@ -1881,10 +1955,7 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
mt = WM_menutype_find("USERPREF_MT_splash_footer", false);
if (mt) {
- Menu menu = {NULL};
- menu.layout = uiLayoutColumn(layout, false);
- menu.type = mt;
- mt->draw(C, &menu);
+ UI_menutype_draw(C, mt, uiLayoutColumn(layout, false));
}
UI_block_bounds_set_centered(block, 0);
@@ -2095,22 +2166,29 @@ static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
ot->poll = WM_operator_winactive;
}
-static int wm_exit_blender_exec(bContext *C, wmOperator *op)
+static int wm_exit_blender_exec(bContext *C, wmOperator *UNUSED(op))
{
- WM_operator_free(op);
-
- WM_exit(C);
-
+ wm_quit_with_optional_confirmation_prompt(C, CTX_wm_window(C));
return OPERATOR_FINISHED;
}
+static int wm_exit_blender_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (U.uiflag & USER_QUIT_PROMPT) {
+ return wm_exit_blender_exec(C, op);
+ }
+ else {
+ return WM_operator_confirm(C, op, event);
+ }
+}
+
static void WM_OT_quit_blender(wmOperatorType *ot)
{
ot->name = "Quit Blender";
ot->idname = "WM_OT_quit_blender";
ot->description = "Quit Blender";
- ot->invoke = WM_operator_confirm;
+ ot->invoke = wm_exit_blender_invoke;
ot->exec = wm_exit_blender_exec;
}
@@ -2171,705 +2249,6 @@ void WM_paint_cursor_end(wmWindowManager *wm, void *handle)
}
}
-/* ************ window gesture operator-callback definitions ************** */
-/*
- * These are default callbacks for use in operators requiring gesture input
- */
-
-/* **************** Border gesture *************** */
-
-/**
- * Border gesture has two types:
- * -# #WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border.
- * -# #WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends.
- *
- * It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type)
- */
-
-static int border_apply_rect(wmOperator *op)
-{
- wmGesture *gesture = op->customdata;
- rcti *rect = gesture->customdata;
-
- if (rect->xmin == rect->xmax || rect->ymin == rect->ymax)
- return 0;
-
-
- /* operator arguments and storage. */
- RNA_int_set(op->ptr, "xmin", min_ii(rect->xmin, rect->xmax));
- RNA_int_set(op->ptr, "ymin", min_ii(rect->ymin, rect->ymax));
- RNA_int_set(op->ptr, "xmax", max_ii(rect->xmin, rect->xmax));
- RNA_int_set(op->ptr, "ymax", max_ii(rect->ymin, rect->ymax));
-
- return 1;
-}
-
-static int border_apply(bContext *C, wmOperator *op, int gesture_mode)
-{
- PropertyRNA *prop;
-
- int retval;
-
- if (!border_apply_rect(op))
- return 0;
-
- /* XXX weak; border should be configured for this without reading event types */
- if ((prop = RNA_struct_find_property(op->ptr, "gesture_mode"))) {
- RNA_property_int_set(op->ptr, prop, gesture_mode);
- }
-
- retval = op->type->exec(C, op);
- OPERATOR_RETVAL_CHECK(retval);
-
- return 1;
-}
-
-static void wm_gesture_end(bContext *C, wmOperator *op)
-{
- wmGesture *gesture = op->customdata;
-
- WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */
- op->customdata = NULL;
-
- ED_area_tag_redraw(CTX_wm_area(C));
-
- if (RNA_struct_find_property(op->ptr, "cursor")) {
- WM_cursor_modal_restore(CTX_wm_window(C));
- }
-}
-
-int WM_border_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- if (ISTWEAK(event->type))
- op->customdata = WM_gesture_new(C, event, WM_GESTURE_RECT);
- else
- op->customdata = WM_gesture_new(C, event, WM_GESTURE_CROSS_RECT);
-
- /* add modal handler */
- WM_event_add_modal_handler(C, op);
-
- wm_gesture_tag_redraw(C);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-int WM_border_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- wmGesture *gesture = op->customdata;
- rcti *rect = gesture->customdata;
- int sx, sy;
-
- if (event->type == MOUSEMOVE) {
- wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
-
- if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->mode == 0) {
- rect->xmin = rect->xmax = event->x - sx;
- rect->ymin = rect->ymax = event->y - sy;
- }
- else {
- rect->xmax = event->x - sx;
- rect->ymax = event->y - sy;
- }
- border_apply_rect(op);
-
- wm_gesture_tag_redraw(C);
- }
- else if (event->type == EVT_MODAL_MAP) {
- switch (event->val) {
- case GESTURE_MODAL_BEGIN:
- if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->mode == 0) {
- gesture->mode = 1;
- wm_gesture_tag_redraw(C);
- }
- break;
- case GESTURE_MODAL_SELECT:
- case GESTURE_MODAL_DESELECT:
- case GESTURE_MODAL_IN:
- case GESTURE_MODAL_OUT:
- if (border_apply(C, op, event->val)) {
- wm_gesture_end(C, op);
- return OPERATOR_FINISHED;
- }
- wm_gesture_end(C, op);
- return OPERATOR_CANCELLED;
-
- case GESTURE_MODAL_CANCEL:
- wm_gesture_end(C, op);
- return OPERATOR_CANCELLED;
- }
-
- }
-#ifdef WITH_INPUT_NDOF
- else if (event->type == NDOF_MOTION) {
- return OPERATOR_PASS_THROUGH;
- }
-#endif
-// /* Allow view navigation??? */
-// else {
-// return OPERATOR_PASS_THROUGH;
-// }
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-void WM_border_select_cancel(bContext *C, wmOperator *op)
-{
- wm_gesture_end(C, op);
-}
-
-/* **************** circle gesture *************** */
-/* works now only for selection or modal paint stuff, calls exec while hold mouse, exit on release */
-
-#ifdef GESTURE_MEMORY
-int circle_select_size = 25; /* XXX - need some operator memory thing! */
-#endif
-
-int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- op->customdata = WM_gesture_new(C, event, WM_GESTURE_CIRCLE);
-
- /* add modal handler */
- WM_event_add_modal_handler(C, op);
-
- wm_gesture_tag_redraw(C);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-static void gesture_circle_apply(bContext *C, wmOperator *op)
-{
- wmGesture *gesture = op->customdata;
- rcti *rect = gesture->customdata;
-
- if (RNA_int_get(op->ptr, "gesture_mode") == GESTURE_MODAL_NOP)
- return;
-
- /* operator arguments and storage. */
- RNA_int_set(op->ptr, "x", rect->xmin);
- RNA_int_set(op->ptr, "y", rect->ymin);
- RNA_int_set(op->ptr, "radius", rect->xmax);
-
- if (op->type->exec) {
- int retval;
- retval = op->type->exec(C, op);
- OPERATOR_RETVAL_CHECK(retval);
- }
-#ifdef GESTURE_MEMORY
- circle_select_size = rect->xmax;
-#endif
-}
-
-int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- wmGesture *gesture = op->customdata;
- rcti *rect = gesture->customdata;
- int sx, sy;
-
- if (event->type == MOUSEMOVE) {
- wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
-
- rect->xmin = event->x - sx;
- rect->ymin = event->y - sy;
-
- wm_gesture_tag_redraw(C);
-
- if (gesture->mode)
- gesture_circle_apply(C, op);
- }
- else if (event->type == EVT_MODAL_MAP) {
- float fac;
-
- switch (event->val) {
- case GESTURE_MODAL_CIRCLE_SIZE:
- fac = 0.3f * (event->y - event->prevy);
- if (fac > 0)
- rect->xmax += ceil(fac);
- else
- rect->xmax += floor(fac);
- if (rect->xmax < 1) rect->xmax = 1;
- wm_gesture_tag_redraw(C);
- break;
- case GESTURE_MODAL_CIRCLE_ADD:
- rect->xmax += 2 + rect->xmax / 10;
- wm_gesture_tag_redraw(C);
- break;
- case GESTURE_MODAL_CIRCLE_SUB:
- rect->xmax -= 2 + rect->xmax / 10;
- if (rect->xmax < 1) rect->xmax = 1;
- wm_gesture_tag_redraw(C);
- break;
- case GESTURE_MODAL_SELECT:
- case GESTURE_MODAL_DESELECT:
- case GESTURE_MODAL_NOP:
- if (RNA_struct_find_property(op->ptr, "gesture_mode"))
- RNA_int_set(op->ptr, "gesture_mode", event->val);
-
- if (event->val != GESTURE_MODAL_NOP) {
- /* apply first click */
- gesture_circle_apply(C, op);
- gesture->mode = 1;
- wm_gesture_tag_redraw(C);
- }
- break;
-
- case GESTURE_MODAL_CANCEL:
- case GESTURE_MODAL_CONFIRM:
- wm_gesture_end(C, op);
- return OPERATOR_FINISHED; /* use finish or we don't get an undo */
- }
- }
-#ifdef WITH_INPUT_NDOF
- else if (event->type == NDOF_MOTION) {
- return OPERATOR_PASS_THROUGH;
- }
-#endif
- /* Allow view navigation??? */
- /* note, this gives issues: 1) other modal ops run on top (border select), 2) middlemouse is used now 3) tablet/trackpad? */
-// else {
-// return OPERATOR_PASS_THROUGH;
-// }
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-void WM_gesture_circle_cancel(bContext *C, wmOperator *op)
-{
- wm_gesture_end(C, op);
-}
-
-#if 0
-/* template to copy from */
-void WM_OT_circle_gesture(wmOperatorType *ot)
-{
- ot->name = "Circle Gesture";
- ot->idname = "WM_OT_circle_gesture";
- ot->description = "Enter rotate mode with a circular gesture";
-
- ot->invoke = WM_gesture_circle_invoke;
- ot->modal = WM_gesture_circle_modal;
-
- ot->poll = WM_operator_winactive;
-
- RNA_def_property(ot->srna, "x", PROP_INT, PROP_NONE);
- RNA_def_property(ot->srna, "y", PROP_INT, PROP_NONE);
- RNA_def_property(ot->srna, "radius", PROP_INT, PROP_NONE);
-
-}
-#endif
-
-/* **************** Tweak gesture *************** */
-
-static void tweak_gesture_modal(bContext *C, const wmEvent *event)
-{
- wmWindow *window = CTX_wm_window(C);
- wmGesture *gesture = window->tweak;
- rcti *rect = gesture->customdata;
- int sx, sy, val;
-
- switch (event->type) {
- case MOUSEMOVE:
- case INBETWEEN_MOUSEMOVE:
-
- wm_subwindow_origin_get(window, gesture->swinid, &sx, &sy);
-
- rect->xmax = event->x - sx;
- rect->ymax = event->y - sy;
-
- if ((val = wm_gesture_evaluate(gesture))) {
- wmEvent tevent;
-
- wm_event_init_from_window(window, &tevent);
- /* We want to get coord from start of drag, not from point where it becomes a tweak event, see T40549 */
- tevent.x = rect->xmin + sx;
- tevent.y = rect->ymin + sy;
- if (gesture->event_type == LEFTMOUSE)
- tevent.type = EVT_TWEAK_L;
- else if (gesture->event_type == RIGHTMOUSE)
- tevent.type = EVT_TWEAK_R;
- else
- tevent.type = EVT_TWEAK_M;
- tevent.val = val;
- /* mouse coords! */
-
- /* important we add immediately after this event, so future mouse releases
- * (which may be in the queue already), are handled in order, see T44740 */
- wm_event_add_ex(window, &tevent, event);
-
- WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */
- }
-
- break;
-
- case LEFTMOUSE:
- case RIGHTMOUSE:
- case MIDDLEMOUSE:
- if (gesture->event_type == event->type) {
- WM_gesture_end(C, gesture);
-
- /* when tweak fails we should give the other keymap entries a chance */
-
- /* XXX, assigning to readonly, BAD JUJU! */
- ((wmEvent *)event)->val = KM_RELEASE;
- }
- break;
- default:
- if (!ISTIMER(event->type) && event->type != EVENT_NONE) {
- WM_gesture_end(C, gesture);
- }
- break;
- }
-}
-
-/* standard tweak, called after window handlers passed on event */
-void wm_tweakevent_test(bContext *C, wmEvent *event, int action)
-{
- wmWindow *win = CTX_wm_window(C);
-
- if (win->tweak == NULL) {
- if (CTX_wm_region(C)) {
- if (event->val == KM_PRESS) {
- if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
- win->tweak = WM_gesture_new(C, event, WM_GESTURE_TWEAK);
- }
- }
- }
- }
- else {
- /* no tweaks if event was handled */
- if ((action & WM_HANDLER_BREAK)) {
- WM_gesture_end(C, win->tweak);
- }
- else
- tweak_gesture_modal(C, event);
- }
-}
-
-/* *********************** lasso gesture ****************** */
-
-int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- PropertyRNA *prop;
-
- op->customdata = WM_gesture_new(C, event, WM_GESTURE_LASSO);
-
- /* add modal handler */
- WM_event_add_modal_handler(C, op);
-
- wm_gesture_tag_redraw(C);
-
- if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
- WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
- }
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-int WM_gesture_lines_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- PropertyRNA *prop;
-
- op->customdata = WM_gesture_new(C, event, WM_GESTURE_LINES);
-
- /* add modal handler */
- WM_event_add_modal_handler(C, op);
-
- wm_gesture_tag_redraw(C);
-
- if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
- WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
- }
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-
-static void gesture_lasso_apply(bContext *C, wmOperator *op)
-{
- wmGesture *gesture = op->customdata;
- PointerRNA itemptr;
- float loc[2];
- int i;
- const short *lasso = gesture->customdata;
-
- /* operator storage as path. */
-
- RNA_collection_clear(op->ptr, "path");
- for (i = 0; i < gesture->points; i++, lasso += 2) {
- loc[0] = lasso[0];
- loc[1] = lasso[1];
- RNA_collection_add(op->ptr, "path", &itemptr);
- RNA_float_set_array(&itemptr, "loc", loc);
- }
-
- wm_gesture_end(C, op);
-
- if (op->type->exec) {
- int retval = op->type->exec(C, op);
- OPERATOR_RETVAL_CHECK(retval);
- }
-}
-
-int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- wmGesture *gesture = op->customdata;
- int sx, sy;
-
- switch (event->type) {
- case MOUSEMOVE:
- case INBETWEEN_MOUSEMOVE:
-
- wm_gesture_tag_redraw(C);
-
- wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
-
- if (gesture->points == gesture->size) {
- short *old_lasso = gesture->customdata;
- gesture->customdata = MEM_callocN(2 * sizeof(short) * (gesture->size + WM_LASSO_MIN_POINTS), "lasso points");
- memcpy(gesture->customdata, old_lasso, 2 * sizeof(short) * gesture->size);
- gesture->size = gesture->size + WM_LASSO_MIN_POINTS;
- MEM_freeN(old_lasso);
- // printf("realloc\n");
- }
-
- {
- int x, y;
- short *lasso = gesture->customdata;
-
- lasso += (2 * gesture->points - 2);
- x = (event->x - sx - lasso[0]);
- y = (event->y - sy - lasso[1]);
-
- /* make a simple distance check to get a smoother lasso
- * add only when at least 2 pixels between this and previous location */
- if ((x * x + y * y) > 4) {
- lasso += 2;
- lasso[0] = event->x - sx;
- lasso[1] = event->y - sy;
- gesture->points++;
- }
- }
- break;
-
- case LEFTMOUSE:
- case MIDDLEMOUSE:
- case RIGHTMOUSE:
- if (event->val == KM_RELEASE) { /* key release */
- gesture_lasso_apply(C, op);
- return OPERATOR_FINISHED;
- }
- break;
- case ESCKEY:
- wm_gesture_end(C, op);
- return OPERATOR_CANCELLED;
- }
- return OPERATOR_RUNNING_MODAL;
-}
-
-int WM_gesture_lines_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- return WM_gesture_lasso_modal(C, op, event);
-}
-
-void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
-{
- wm_gesture_end(C, op);
-}
-
-void WM_gesture_lines_cancel(bContext *C, wmOperator *op)
-{
- wm_gesture_end(C, op);
-}
-
-/**
- * helper function, we may want to add options for conversion to view space
- *
- * caller must free.
- */
-const int (*WM_gesture_lasso_path_to_array(bContext *UNUSED(C), wmOperator *op, int *mcords_tot))[2]
-{
- PropertyRNA *prop = RNA_struct_find_property(op->ptr, "path");
- int (*mcords)[2] = NULL;
- BLI_assert(prop != NULL);
-
- if (prop) {
- const int len = RNA_property_collection_length(op->ptr, prop);
-
- if (len) {
- int i = 0;
- mcords = MEM_mallocN(sizeof(int) * 2 * len, __func__);
-
- RNA_PROP_BEGIN (op->ptr, itemptr, prop)
- {
- float loc[2];
-
- RNA_float_get_array(&itemptr, "loc", loc);
- mcords[i][0] = (int)loc[0];
- mcords[i][1] = (int)loc[1];
- i++;
- }
- RNA_PROP_END;
- }
- *mcords_tot = len;
- }
- else {
- *mcords_tot = 0;
- }
-
- /* cast for 'const' */
- return (const int (*)[2])mcords;
-}
-
-#if 0
-/* template to copy from */
-
-static int gesture_lasso_exec(bContext *C, wmOperator *op)
-{
- RNA_BEGIN (op->ptr, itemptr, "path")
- {
- float loc[2];
-
- RNA_float_get_array(&itemptr, "loc", loc);
- printf("Location: %f %f\n", loc[0], loc[1]);
- }
- RNA_END;
-
- return OPERATOR_FINISHED;
-}
-
-void WM_OT_lasso_gesture(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- ot->name = "Lasso Gesture";
- ot->idname = "WM_OT_lasso_gesture";
- ot->description = "Select objects within the lasso as you move the pointer";
-
- ot->invoke = WM_gesture_lasso_invoke;
- ot->modal = WM_gesture_lasso_modal;
- ot->exec = gesture_lasso_exec;
-
- ot->poll = WM_operator_winactive;
-
- prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
-}
-#endif
-
-/* *********************** straight line gesture ****************** */
-
-static int straightline_apply(bContext *C, wmOperator *op)
-{
- wmGesture *gesture = op->customdata;
- rcti *rect = gesture->customdata;
-
- if (rect->xmin == rect->xmax && rect->ymin == rect->ymax)
- return 0;
-
- /* operator arguments and storage. */
- RNA_int_set(op->ptr, "xstart", rect->xmin);
- RNA_int_set(op->ptr, "ystart", rect->ymin);
- RNA_int_set(op->ptr, "xend", rect->xmax);
- RNA_int_set(op->ptr, "yend", rect->ymax);
-
- if (op->type->exec) {
- int retval = op->type->exec(C, op);
- OPERATOR_RETVAL_CHECK(retval);
- }
-
- return 1;
-}
-
-
-int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- PropertyRNA *prop;
-
- op->customdata = WM_gesture_new(C, event, WM_GESTURE_STRAIGHTLINE);
-
- /* add modal handler */
- WM_event_add_modal_handler(C, op);
-
- wm_gesture_tag_redraw(C);
-
- if ((prop = RNA_struct_find_property(op->ptr, "cursor"))) {
- WM_cursor_modal_set(CTX_wm_window(C), RNA_property_int_get(op->ptr, prop));
- }
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- wmGesture *gesture = op->customdata;
- rcti *rect = gesture->customdata;
- int sx, sy;
-
- if (event->type == MOUSEMOVE) {
- wm_subwindow_origin_get(CTX_wm_window(C), gesture->swinid, &sx, &sy);
-
- if (gesture->mode == 0) {
- rect->xmin = rect->xmax = event->x - sx;
- rect->ymin = rect->ymax = event->y - sy;
- }
- else {
- rect->xmax = event->x - sx;
- rect->ymax = event->y - sy;
- straightline_apply(C, op);
- }
-
- wm_gesture_tag_redraw(C);
- }
- else if (event->type == EVT_MODAL_MAP) {
- switch (event->val) {
- case GESTURE_MODAL_BEGIN:
- if (gesture->mode == 0) {
- gesture->mode = 1;
- wm_gesture_tag_redraw(C);
- }
- break;
- case GESTURE_MODAL_SELECT:
- if (straightline_apply(C, op)) {
- wm_gesture_end(C, op);
- return OPERATOR_FINISHED;
- }
- wm_gesture_end(C, op);
- return OPERATOR_CANCELLED;
-
- case GESTURE_MODAL_CANCEL:
- wm_gesture_end(C, op);
- return OPERATOR_CANCELLED;
- }
-
- }
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-void WM_gesture_straightline_cancel(bContext *C, wmOperator *op)
-{
- wm_gesture_end(C, op);
-}
-
-#if 0
-/* template to copy from */
-void WM_OT_straightline_gesture(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- ot->name = "Straight Line Gesture";
- ot->idname = "WM_OT_straightline_gesture";
- ot->description = "Draw a straight line as you move the pointer";
-
- ot->invoke = WM_gesture_straightline_invoke;
- ot->modal = WM_gesture_straightline_modal;
- ot->exec = gesture_straightline_exec;
-
- ot->poll = WM_operator_winactive;
-
- WM_operator_properties_gesture_straightline(ot, 0);
-}
-#endif
-
/* *********************** radial control ****************** */
#define WM_RADIAL_CONTROL_DISPLAY_SIZE (200 * UI_DPI_FAC)
@@ -3661,26 +3040,38 @@ static void WM_OT_radial_control(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
/* all paths relative to the context */
- RNA_def_string(ot->srna, "data_path_primary", NULL, 0, "Primary Data Path", "Primary path of property to be set by the radial control");
+ PropertyRNA *prop;
+ prop = RNA_def_string(ot->srna, "data_path_primary", NULL, 0, "Primary Data Path", "Primary path of property to be set by the radial control");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_string(ot->srna, "data_path_secondary", NULL, 0, "Secondary Data Path", "Secondary path of property to be set by the radial control");
+ prop = RNA_def_string(ot->srna, "data_path_secondary", NULL, 0, "Secondary Data Path", "Secondary path of property to be set by the radial control");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_string(ot->srna, "use_secondary", NULL, 0, "Use Secondary", "Path of property to select between the primary and secondary data paths");
+ prop = RNA_def_string(ot->srna, "use_secondary", NULL, 0, "Use Secondary", "Path of property to select between the primary and secondary data paths");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_string(ot->srna, "rotation_path", NULL, 0, "Rotation Path", "Path of property used to rotate the texture display");
+ prop = RNA_def_string(ot->srna, "rotation_path", NULL, 0, "Rotation Path", "Path of property used to rotate the texture display");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_string(ot->srna, "color_path", NULL, 0, "Color Path", "Path of property used to set the color of the control");
+ prop = RNA_def_string(ot->srna, "color_path", NULL, 0, "Color Path", "Path of property used to set the color of the control");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_string(ot->srna, "fill_color_path", NULL, 0, "Fill Color Path", "Path of property used to set the fill color of the control");
+ prop = RNA_def_string(ot->srna, "fill_color_path", NULL, 0, "Fill Color Path", "Path of property used to set the fill color of the control");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_string(ot->srna, "fill_color_override_path", NULL, 0, "Fill Color Override Path", "");
- RNA_def_string(ot->srna, "fill_color_override_test_path", NULL, 0, "Fill Color Override Test", "");
+ prop = RNA_def_string(ot->srna, "fill_color_override_path", NULL, 0, "Fill Color Override Path", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_string(ot->srna, "fill_color_override_test_path", NULL, 0, "Fill Color Override Test", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_string(ot->srna, "zoom_path", NULL, 0, "Zoom Path", "Path of property used to set the zoom level for the control");
+ prop = RNA_def_string(ot->srna, "zoom_path", NULL, 0, "Zoom Path", "Path of property used to set the zoom level for the control");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_string(ot->srna, "image_id", NULL, 0, "Image ID", "Path of ID that is used to generate an image for the control");
+ prop = RNA_def_string(ot->srna, "image_id", NULL, 0, "Image ID", "Path of ID that is used to generate an image for the control");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_boolean(ot->srna, "secondary_tex", false, "Secondary Texture", "Tweak brush secondary/mask texture");
+ prop = RNA_def_boolean(ot->srna, "secondary_tex", false, "Secondary Texture", "Tweak brush secondary/mask texture");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* ************************** timer for testing ***************** */
@@ -3710,7 +3101,7 @@ enum {
eRTUndo = 6,
};
-static EnumPropertyItem redraw_timer_type_items[] = {
+static const EnumPropertyItem redraw_timer_type_items[] = {
{eRTDrawRegion, "DRAW", 0, "Draw Region", "Draw Region"},
{eRTDrawRegionSwap, "DRAW_SWAP", 0, "Draw Region + Swap", "Draw Region and Swap"},
{eRTDrawWindow, "DRAW_WIN", 0, "Draw Window", "Draw Window"},
@@ -3904,7 +3295,7 @@ static void previews_id_ensure(bContext *C, Scene *scene, ID *id)
/* Only preview non-library datablocks, lib ones do not pertain to this .blend file!
* Same goes for ID with no user. */
- if (!ID_IS_LINKED_DATABLOCK(id) && (id->us != 0)) {
+ if (!ID_IS_LINKED(id) && (id->us != 0)) {
UI_id_icon_render(C, scene, id, false, false);
UI_id_icon_render(C, scene, id, true, false);
}
@@ -3978,7 +3369,7 @@ static void WM_OT_previews_ensure(wmOperatorType *ot)
/* *************************** Datablocks previews clear ************* */
/* Only types supporting previews currently. */
-static EnumPropertyItem preview_id_type_items[] = {
+static const EnumPropertyItem preview_id_type_items[] = {
{FILTER_ID_SCE, "SCENE", 0, "Scenes", ""},
{FILTER_ID_GR, "GROUP", 0, "Groups", ""},
{FILTER_ID_OB, "OBJECT", 0, "Objects", ""},
@@ -4179,7 +3570,7 @@ void wm_operatortype_init(void)
/* circleselect-like modal operators */
static void gesture_circle_modal_keymap(wmKeyConfig *keyconf)
{
- static EnumPropertyItem modal_items[] = {
+ static const EnumPropertyItem modal_items[] = {
{GESTURE_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
{GESTURE_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
{GESTURE_MODAL_CIRCLE_ADD, "ADD", 0, "Add", ""},
@@ -4210,14 +3601,15 @@ static void gesture_circle_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, 0, 0, GESTURE_MODAL_SELECT);
+ /* Note: use 'KM_ANY' for release, so the circle exits on any mouse release,
+ * this is needed when circle select is activated as a tool. */
+
/* left mouse shift for deselect too */
WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_SHIFT, 0, GESTURE_MODAL_DESELECT);
- WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_SHIFT, 0, GESTURE_MODAL_NOP);
+ WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, GESTURE_MODAL_NOP);
WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_PRESS, 0, 0, GESTURE_MODAL_DESELECT); // default 2.4x
- WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, 0, 0, GESTURE_MODAL_NOP); // default 2.4x
-
- WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, 0, 0, GESTURE_MODAL_NOP);
+ WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, GESTURE_MODAL_NOP); // default 2.4x
WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, GESTURE_MODAL_CIRCLE_SUB);
WM_modalkeymap_add_item(keymap, PADMINUS, KM_PRESS, 0, 0, GESTURE_MODAL_CIRCLE_SUB);
@@ -4240,7 +3632,7 @@ static void gesture_circle_modal_keymap(wmKeyConfig *keyconf)
/* straight line modal operators */
static void gesture_straightline_modal_keymap(wmKeyConfig *keyconf)
{
- static EnumPropertyItem modal_items[] = {
+ static const EnumPropertyItem modal_items[] = {
{GESTURE_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
{GESTURE_MODAL_SELECT, "SELECT", 0, "Select", ""},
{GESTURE_MODAL_BEGIN, "BEGIN", 0, "Begin", ""},
@@ -4259,7 +3651,7 @@ static void gesture_straightline_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_ANY, KM_ANY, 0, GESTURE_MODAL_CANCEL);
WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, 0, 0, GESTURE_MODAL_BEGIN);
- WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, 0, 0, GESTURE_MODAL_SELECT);
+ WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, GESTURE_MODAL_SELECT);
/* assign map to operators */
WM_modalkeymap_assign(keymap, "IMAGE_OT_sample_line");
@@ -4271,7 +3663,7 @@ static void gesture_straightline_modal_keymap(wmKeyConfig *keyconf)
/* borderselect-like modal operators */
static void gesture_border_modal_keymap(wmKeyConfig *keyconf)
{
- static EnumPropertyItem modal_items[] = {
+ static const EnumPropertyItem modal_items[] = {
{GESTURE_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
{GESTURE_MODAL_SELECT, "SELECT", 0, "Select", ""},
{GESTURE_MODAL_DESELECT, "DESELECT", 0, "DeSelect", ""},
@@ -4337,7 +3729,7 @@ static void gesture_border_modal_keymap(wmKeyConfig *keyconf)
/* zoom to border modal operators */
static void gesture_zoom_border_modal_keymap(wmKeyConfig *keyconf)
{
- static EnumPropertyItem modal_items[] = {
+ static const EnumPropertyItem modal_items[] = {
{GESTURE_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
{GESTURE_MODAL_IN, "IN", 0, "In", ""},
{GESTURE_MODAL_OUT, "OUT", 0, "Out", ""},
@@ -4485,15 +3877,31 @@ void wm_window_keymap(wmKeyConfig *keyconf)
gesture_straightline_modal_keymap(keyconf);
}
+/**
+ * Filter functions that can be used with rna_id_itemf() below.
+ * Should return false if 'id' should be excluded.
+ */
+static bool rna_id_enum_filter_single(ID *id, void *user_data)
+{
+ return (id != user_data);
+}
+
/* Generic itemf's for operators that take library args */
-static EnumPropertyItem *rna_id_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), bool *r_free, ID *id, bool local)
+static const EnumPropertyItem *rna_id_itemf(
+ bContext *UNUSED(C), PointerRNA *UNUSED(ptr),
+ bool *r_free, ID *id, bool local,
+ bool (*filter_ids)(ID *id, void *user_data), void *user_data)
{
EnumPropertyItem item_tmp = {0}, *item = NULL;
int totitem = 0;
int i = 0;
for (; id; id = id->next) {
- if (local == false || !ID_IS_LINKED_DATABLOCK(id)) {
+ if ((filter_ids != NULL) && filter_ids(user_data, id) == false) {
+ i++;
+ continue;
+ }
+ if (local == false || !ID_IS_LINKED(id)) {
item_tmp.identifier = item_tmp.name = id->name + 2;
item_tmp.value = i++;
RNA_enum_item_add(&item, &totitem, &item_tmp);
@@ -4507,58 +3915,64 @@ static EnumPropertyItem *rna_id_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(pt
}
/* can add more as needed */
-EnumPropertyItem *RNA_action_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_action_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->action.first : NULL, false);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->action.first : NULL, false, NULL, NULL);
}
#if 0 /* UNUSED */
-EnumPropertyItem *RNA_action_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_action_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->action.first : NULL, true);
}
#endif
-EnumPropertyItem *RNA_group_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_group_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->group.first : NULL, false);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->group.first : NULL, false, NULL, NULL);
}
-EnumPropertyItem *RNA_group_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_group_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->group.first : NULL, true);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->group.first : NULL, true, NULL, NULL);
}
-EnumPropertyItem *RNA_image_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_image_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->image.first : NULL, false);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->image.first : NULL, false, NULL, NULL);
}
-EnumPropertyItem *RNA_image_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_image_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->image.first : NULL, true);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->image.first : NULL, true, NULL, NULL);
}
-EnumPropertyItem *RNA_scene_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_scene_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->scene.first : NULL, false);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->scene.first : NULL, false, NULL, NULL);
}
-EnumPropertyItem *RNA_scene_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_scene_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->scene.first : NULL, true);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->scene.first : NULL, true, NULL, NULL);
}
-
-EnumPropertyItem *RNA_movieclip_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_scene_without_active_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ Scene *scene_active = C ? CTX_data_scene(C) : NULL;
+ return rna_id_itemf(
+ C, ptr, r_free, C ? (ID *)CTX_data_main(C)->scene.first : NULL, true,
+ rna_id_enum_filter_single, scene_active);
+}
+const EnumPropertyItem *RNA_movieclip_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->movieclip.first : NULL, false);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->movieclip.first : NULL, false, NULL, NULL);
}
-EnumPropertyItem *RNA_movieclip_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_movieclip_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->movieclip.first : NULL, true);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->movieclip.first : NULL, true, NULL, NULL);
}
-EnumPropertyItem *RNA_mask_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_mask_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->mask.first : NULL, false);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->mask.first : NULL, false, NULL, NULL);
}
-EnumPropertyItem *RNA_mask_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+const EnumPropertyItem *RNA_mask_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
- return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->mask.first : NULL, true);
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->mask.first : NULL, true, NULL, NULL);
}
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index 6bf7bcc2934..77378cf8e0c 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -1256,7 +1256,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
//GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
/* initialize the font */
- BLF_init(11, 72);
+ BLF_init();
ps.fontid = BLF_load_mem("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
BLF_size(ps.fontid, 11, 72);
@@ -1428,8 +1428,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.next_frame = ps.direction;
-
- while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0)) || ps.wait2) {
+ while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, ps.wait2))) {
if (hasevent) {
GHOST_DispatchEvents(g_WS.ghost_system);
}
diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c
index 46cee907991..55fe2ec846c 100644
--- a/source/blender/windowmanager/intern/wm_stereo.c
+++ b/source/blender/windowmanager/intern/wm_stereo.c
@@ -345,6 +345,32 @@ bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
return true;
}
+/**
+ * If needed, this adjusts \a r_mouse_xy so that drawn cursor and handled mouse position are matching visually.
+ */
+void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy)
+{
+ if (!WM_stereo3d_enabled(win, false))
+ return;
+
+ if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) {
+ const int half_x = win->sizex / 2;
+ /* right half of the screen */
+ if (r_mouse_xy[0] > half_x) {
+ r_mouse_xy[0] -= half_x;
+ }
+ r_mouse_xy[0] *= 2;
+ }
+ else if (win->stereo3d_format->display_mode == S3D_DISPLAY_TOPBOTTOM) {
+ const int half_y = win->sizey / 2;
+ /* upper half of the screen */
+ if (r_mouse_xy[1] > half_y) {
+ r_mouse_xy[1] -= half_y;
+ }
+ r_mouse_xy[1] *= 2;
+ }
+}
+
/************************** Stereo 3D operator **********************************/
typedef struct Stereo3dData {
Stereo3dFormat stereo3d_format;
diff --git a/source/blender/windowmanager/intern/wm_tooltip.c b/source/blender/windowmanager/intern/wm_tooltip.c
new file mode 100644
index 00000000000..86ca95ef377
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_tooltip.c
@@ -0,0 +1,106 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/intern/wm_tooltip.c
+ * \ingroup wm
+ *
+ * Manages a per-window tool-tip.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+
+#include "BKE_context.h"
+
+#include "ED_screen.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+void WM_tooltip_timer_init(
+ bContext *C, wmWindow *win, ARegion *ar,
+ wmTooltipInitFn init)
+{
+ bScreen *screen = win->screen;
+ wmWindowManager *wm = CTX_wm_manager(C);
+ if (screen->tool_tip == NULL) {
+ screen->tool_tip = MEM_callocN(sizeof(*screen->tool_tip), __func__);
+ }
+ screen->tool_tip->region_from = ar;
+ screen->tool_tip->timer = WM_event_add_timer(wm, win, TIMER, UI_TOOLTIP_DELAY);
+ screen->tool_tip->init = init;
+}
+
+void WM_tooltip_timer_clear(bContext *C, wmWindow *win)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ bScreen *screen = win->screen;
+ if (screen->tool_tip != NULL) {
+ if (screen->tool_tip->timer != NULL) {
+ WM_event_remove_timer(wm, win, screen->tool_tip->timer);
+ screen->tool_tip->timer = NULL;
+ }
+ }
+}
+
+void WM_tooltip_clear(bContext *C, wmWindow *win)
+{
+ WM_tooltip_timer_clear(C, win);
+ bScreen *screen = win->screen;
+ if (screen->tool_tip != NULL) {
+ if (screen->tool_tip->region) {
+ UI_tooltip_free(C, screen, screen->tool_tip->region);
+ screen->tool_tip->region = NULL;
+ }
+ MEM_freeN(screen->tool_tip);
+ screen->tool_tip = NULL;
+ }
+}
+
+void WM_tooltip_init(bContext *C, wmWindow *win)
+{
+ WM_tooltip_timer_clear(C, win);
+ bScreen *screen = win->screen;
+ if (screen->tool_tip->region) {
+ UI_tooltip_free(C, screen, screen->tool_tip->region);
+ screen->tool_tip->region = NULL;
+ }
+ screen->tool_tip->region = screen->tool_tip->init(
+ C, screen->tool_tip->region_from, &screen->tool_tip->exit_on_event);
+ if (screen->tool_tip->region == NULL) {
+ WM_tooltip_clear(C, win);
+ }
+}
+
+void WM_tooltip_refresh(bContext *C, wmWindow *win)
+{
+ WM_tooltip_timer_clear(C, win);
+ bScreen *screen = win->screen;
+ if (screen->tool_tip != NULL) {
+ if (screen->tool_tip->region) {
+ UI_tooltip_free(C, screen, screen->tool_tip->region);
+ screen->tool_tip->region = NULL;
+ }
+ WM_tooltip_init(C, win);
+ }
+}
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 2d43c47679d..06bd60e8692 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -70,6 +70,7 @@
#include "ED_fileselect.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "PIL_time.h"
@@ -77,6 +78,7 @@
#include "GPU_extensions.h"
#include "GPU_init_exit.h"
#include "GPU_glew.h"
+#include "BLF_api.h"
/* for assert */
#ifndef NDEBUG
@@ -294,11 +296,158 @@ wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src)
}
}
+
+/* -------------------------------------------------------------------- */
+/** \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))
+{
+ wmWindow *win = CTX_wm_window(C);
+ UI_popup_block_close(C, win, arg_block);
+}
+
+/** Discard the file changes and quit */
+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))
+{
+
+ 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_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 (G.main->name[0] == '\0') {
+ message = BLI_strdup(IFACE_("This file has not been saved yet. Save before closing?"));
+ }
+ else {
+ const char *basename = BLI_path_basename(G.main->name);
+ 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_block_bounds_set_centered(block, 10);
+
+ return block;
+}
+
+
+/**
+ * Call the confirm dialog on quitting. It's displayed in the context window so
+ * caller should set it as desired.
+ */
+static void wm_confirm_quit(bContext *C)
+{
+ wmWindow *win = CTX_wm_window(C);
+
+ if (GHOST_SupportsNativeDialogs() == 0) {
+ UI_popup_block_invoke(C, block_create_confirm_quit, NULL);
+ }
+ else if (GHOST_confirmQuit(win->ghostwin)) {
+ wm_exit_schedule_delayed(C);
+ }
+}
+
+/**
+ * Call the quit confirmation prompt or exit directly if needed. The use can
+ * still cancel via the confirmation popup. Also, this may not quit Blender
+ * immediately, but rather schedule the closing.
+ *
+ * \param win The window to show the confirmation popup/window in.
+ */
+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
+ * here (this function gets called outside of normal event handling loop). */
+ CTX_wm_window_set(C, win);
+
+ if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved && !G.background) {
+ wm_confirm_quit(C);
+ }
+ else {
+ wm_exit_schedule_delayed(C);
+ }
+
+ CTX_wm_window_set(C, win_ctx);
+}
+
+/** \} */
+
/* this is event from ghost, or exit-blender op */
void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
{
wmWindow *tmpwin;
- bool do_exit = false;
/* first check if we have to quit (there are non-temp remaining windows) */
for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) {
@@ -308,21 +457,11 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
break;
}
- if (tmpwin == NULL)
- do_exit = 1;
-
- if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved) {
- if (do_exit) {
- if (!GHOST_confirmQuit(win->ghostwin))
- return;
- }
- }
-
- /* let WM_exit do all freeing, for correct quit.blend save */
- if (do_exit) {
- WM_exit(C);
+ if (tmpwin == NULL) {
+ wm_quit_with_optional_confirmation_prompt(C, win);
}
else {
+ /* We're just closing a window */
bScreen *screen = win->screen;
BLI_remlink(&wm->windows, win);
@@ -374,14 +513,49 @@ void wm_window_title(wmWindowManager *wm, wmWindow *win)
}
}
-static float wm_window_get_virtual_pixelsize(void)
+void WM_window_set_dpi(wmWindow *win)
{
- return ((U.virtual_pixel == VIRTUAL_PIXEL_NATIVE) ? 1.0f : 2.0f);
-}
+ float auto_dpi = GHOST_GetDPIHint(win->ghostwin);
-float wm_window_pixelsize(wmWindow *win)
-{
- return (GHOST_GetNativePixelSize(win->ghostwin) * wm_window_get_virtual_pixelsize());
+ /* Clamp auto DPI to 96, since our font/interface drawing does not work well
+ * with lower sizes. The main case we are interested in supporting is higher
+ * DPI. If a smaller UI is desired it is still possible to adjust UI scale. */
+ auto_dpi = max_ff(auto_dpi, 96.0f);
+
+ /* Lazily init UI scale size, preserving backwards compatibility by
+ * computing UI scale from ratio of previous DPI and auto DPI */
+ if (U.ui_scale == 0) {
+ int virtual_pixel = (U.virtual_pixel == VIRTUAL_PIXEL_NATIVE) ? 1 : 2;
+
+ if (U.dpi == 0) {
+ U.ui_scale = virtual_pixel;
+ }
+ else {
+ U.ui_scale = (virtual_pixel * U.dpi * 96.0f) / (auto_dpi * 72.0f);
+ }
+
+ CLAMP(U.ui_scale, 0.25f, 4.0f);
+ }
+
+ /* Blender's UI drawing assumes DPI 72 as a good default following macOS
+ * while Windows and Linux use DPI 96. GHOST assumes a default 96 so we
+ * remap the DPI to Blender's convention. */
+ auto_dpi *= GHOST_GetNativePixelSize(win->ghostwin);
+ int dpi = auto_dpi * U.ui_scale * (72.0 / 96.0f);
+
+ /* Automatically set larger pixel size for high DPI. */
+ int pixelsize = max_ii(1, (int)(dpi / 64));
+ /* User adjustment for pixel size. */
+ pixelsize = max_ii(1, pixelsize + U.ui_line_width);
+
+ /* Set user preferences globals for drawing, and for forward compatibility. */
+ U.pixelsize = pixelsize;
+ U.dpi = dpi / pixelsize;
+ U.virtual_pixel = (pixelsize == 1) ? VIRTUAL_PIXEL_NATIVE : VIRTUAL_PIXEL_DOUBLE;
+ U.widget_unit = (U.pixelsize * U.dpi * 20 + 36) / 72;
+
+ /* update font drawing */
+ BLF_default_dpi(U.pixelsize * U.dpi);
}
/* belongs to below */
@@ -412,12 +586,7 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
ghostwin = GHOST_CreateWindow(g_system, title,
win->posx, posy, win->sizex, win->sizey,
-#ifdef __APPLE__
- /* we agreed to not set any fullscreen or iconized state on startup */
- GHOST_kWindowStateNormal,
-#else
(GHOST_TWindowState)win->windowstate,
-#endif
GHOST_kDrawingContextTypeOpenGL,
glSettings);
@@ -441,8 +610,12 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
/* store actual window size in blender window */
bounds = GHOST_GetClientBounds(win->ghostwin);
- win->sizex = GHOST_GetWidthRectangle(bounds);
- win->sizey = GHOST_GetHeightRectangle(bounds);
+
+ /* win32: gives undefined window size when minimized */
+ if (GHOST_GetWindowState(win->ghostwin) != GHOST_kWindowStateMinimized) {
+ win->sizex = GHOST_GetWidthRectangle(bounds);
+ win->sizey = GHOST_GetHeightRectangle(bounds);
+ }
GHOST_DisposeRectangle(bounds);
#ifndef __APPLE__
@@ -456,10 +629,8 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
glClear(GL_COLOR_BUFFER_BIT);
}
- /* displays with larger native pixels, like Macbook. Used to scale dpi with */
/* needed here, because it's used before it reads userdef */
- U.pixelsize = wm_window_pixelsize(win);
- BKE_blender_userdef_refresh();
+ WM_window_set_dpi(win);
wm_window_swap_buffers(win);
@@ -472,7 +643,7 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
}
/**
- * Initialize #wmWindows without ghostwin, open these and clear.
+ * Initialize #wmWindow without ghostwin, open these and clear.
*
* window size is read from window, if 0 it uses prefsize
* called in #WM_check, also inits stuff after file read.
@@ -618,15 +789,27 @@ wmWindow *WM_window_open(bContext *C, const rcti *rect)
* \param type: WM_WINDOW_RENDER, WM_WINDOW_USERPREFS...
* \return the window or NULL.
*/
-wmWindow *WM_window_open_temp(bContext *C, const rcti *rect_init, int type)
+wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, int type)
{
wmWindow *win_prev = CTX_wm_window(C);
wmWindow *win;
ScrArea *sa;
Scene *scene = CTX_data_scene(C);
const char *title;
- rcti rect = *rect_init;
- const short px_virtual = (short)wm_window_get_virtual_pixelsize();
+
+ /* convert to native OS window coordinates */
+ const float native_pixel_size = GHOST_GetNativePixelSize(win_prev->ghostwin);
+ x /= native_pixel_size;
+ y /= native_pixel_size;
+ sizex /= native_pixel_size;
+ sizey /= native_pixel_size;
+
+ /* calculate postition */
+ rcti rect;
+ rect.xmin = x + win_prev->posx - sizex / 2;
+ rect.ymin = y + win_prev->posy - sizey / 2;
+ rect.xmax = rect.xmin + sizex;
+ rect.ymax = rect.ymin + sizey;
/* changes rect to fit within desktop */
wm_window_check_position(&rect);
@@ -644,9 +827,8 @@ wmWindow *WM_window_open_temp(bContext *C, const rcti *rect_init, int type)
win->posy = rect.ymin;
}
- /* multiply with virtual pixelsize, ghost handles native one (e.g. for retina) */
- win->sizex = BLI_rcti_size_x(&rect) * px_virtual;
- win->sizey = BLI_rcti_size_y(&rect) * px_virtual;
+ win->sizex = BLI_rcti_size_x(&rect);
+ win->sizey = BLI_rcti_size_y(&rect);
if (win->ghostwin) {
wm_window_set_size(win, win->sizex, win->sizey);
@@ -835,8 +1017,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
GHOST_ActivateWindowDrawingContext(win->ghostwin);
/* this can change per window */
- U.pixelsize = wm_window_pixelsize(win);
- BKE_blender_userdef_refresh();
+ WM_window_set_dpi(win);
}
}
@@ -1035,6 +1216,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
if (type == GHOST_kEventWindowSize) {
WM_jobs_stop(wm, win->screen, NULL);
}
+
+ WM_window_set_dpi(win);
/* win32: gives undefined window size when minimized */
if (state != GHOST_kWindowStateMinimized) {
@@ -1118,7 +1301,18 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
}
break;
}
-
+
+ case GHOST_kEventWindowDPIHintChanged:
+ {
+ WM_window_set_dpi(win);
+ /* font's are stored at each DPI level, without this we can easy load 100's of fonts */
+ BLF_cache_clear();
+
+ WM_main_add_notifier(NC_WINDOW, NULL); /* full redraw */
+ WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); /* refresh region sizes */
+ break;
+ }
+
case GHOST_kEventOpenMainFile:
{
PointerRNA props_ptr;
@@ -1199,11 +1393,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
{
// only update if the actual pixel size changes
float prev_pixelsize = U.pixelsize;
- U.pixelsize = wm_window_pixelsize(win);
+ WM_window_set_dpi(win);
if (U.pixelsize != prev_pixelsize) {
- BKE_blender_userdef_refresh();
-
// close all popups since they are positioned with the pixel
// size baked in and it's difficult to correct them
wmWindow *oldWindow = CTX_wm_window(C);
@@ -1407,6 +1599,7 @@ wmTimer *WM_event_add_timer_notifier(wmWindowManager *wm, wmWindow *win, unsigne
wt->timestep = timestep;
wt->win = win;
wt->customdata = SET_UINT_IN_POINTER(type);
+ wt->flags |= WM_TIMER_NO_FREE_CUSTOM_DATA;
BLI_addtail(&wm->timers, wt);
@@ -1428,8 +1621,9 @@ void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *
wm->reports.reporttimer = NULL;
BLI_remlink(&wm->timers, wt);
- if (wt->customdata)
+ if (wt->customdata != NULL && (wt->flags & WM_TIMER_NO_FREE_CUSTOM_DATA) == 0) {
MEM_freeN(wt->customdata);
+ }
MEM_freeN(wt);
/* there might be events in queue with this timer as customdata */
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index 2f06ddab1e8..5ced36a104f 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -43,6 +43,9 @@ typedef struct wmPaintCursor {
void (*draw)(bContext *C, int, int, void *customdata);
} wmPaintCursor;
+
+void wm_exit_schedule_delayed(const bContext *C);
+
extern void wm_close_and_free(bContext *C, wmWindowManager *);
extern void wm_close_and_free_all(bContext *C, ListBase *);
@@ -57,14 +60,14 @@ void wm_operatortype_free(void);
void wm_operatortype_init(void);
void wm_window_keymap(wmKeyConfig *keyconf);
-void wm_tweakevent_test(bContext *C, wmEvent *event, int action);
-
/* wm_gesture.c */
-#define WM_LASSO_MIN_POINTS 1024
void wm_gesture_draw(struct wmWindow *win);
int wm_gesture_evaluate(wmGesture *gesture);
void wm_gesture_tag_redraw(bContext *C);
+/* wm_gesture_ops.c */
+void wm_tweakevent_test(bContext *C, const wmEvent *event, int action);
+
/* wm_jobs.c */
void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt);
void wm_jobs_timer_ended(wmWindowManager *wm, wmTimer *wt);
@@ -78,6 +81,7 @@ void wm_autosave_location(char *filepath);
/* wm_stereo.c */
void wm_method_draw_stereo3d(const bContext *C, wmWindow *win);
+void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy);
int wm_stereo3d_set_exec(bContext *C, wmOperator *op);
int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *event);
void wm_stereo3d_set_draw(bContext *C, wmOperator *op);
@@ -88,12 +92,4 @@ void wm_stereo3d_set_cancel(bContext *C, wmOperator *op);
void wm_open_init_load_ui(wmOperator *op, bool use_prefs);
void wm_open_init_use_scripts(wmOperator *op, bool use_prefs);
-/* hack to store circle select size - campbell, must replace with nice operator memory */
-#define GESTURE_MEMORY
-
-#ifdef GESTURE_MEMORY
-extern int circle_select_size;
-#endif
-
#endif /* __WM_H__ */
-
diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h
index efc01b1f8a8..9977e73f2fd 100644
--- a/source/blender/windowmanager/wm_event_system.h
+++ b/source/blender/windowmanager/wm_event_system.h
@@ -71,7 +71,7 @@ typedef struct wmEventHandler {
} wmEventHandler;
-/* custom types for handlers, for signalling, freeing */
+/* custom types for handlers, for signaling, freeing */
enum {
WM_HANDLER_DEFAULT,
WM_HANDLER_FILESELECT
@@ -87,14 +87,14 @@ void wm_event_do_handlers (bContext *C);
void wm_event_add_ghostevent (wmWindowManager *wm, wmWindow *win, int type, int time, void *customdata);
-void wm_event_do_notifiers (bContext *C);
+void wm_event_do_refresh_wm_and_depsgraph(bContext *C);
+void wm_event_do_notifiers(bContext *C);
/* wm_keymap.c */
/* wm_dropbox.c */
void wm_dropbox_free(void);
-void wm_drags_check_ops(bContext *C, wmEvent *event);
+void wm_drags_check_ops(bContext *C, const wmEvent *event);
void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect);
#endif /* __WM_EVENT_SYSTEM_H__ */
-
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index 3085f138846..090c8c90280 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -431,6 +431,7 @@ enum {
GESTURE_MODAL_CANCEL = 1,
GESTURE_MODAL_CONFIRM = 2,
+ /* Uses 'deselect' operator property. */
GESTURE_MODAL_SELECT = 3,
GESTURE_MODAL_DESELECT = 4,
@@ -441,6 +442,7 @@ enum {
GESTURE_MODAL_BEGIN = 8, /* border select/straight line, activate, use release to detect which button */
+ /* Uses 'zoom_out' operator property. */
GESTURE_MODAL_IN = 9,
GESTURE_MODAL_OUT = 10,
diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h
index 396907a3f6d..b102b6c7cc7 100644
--- a/source/blender/windowmanager/wm_files.h
+++ b/source/blender/windowmanager/wm_files.h
@@ -35,7 +35,10 @@ struct wmOperatorType;
/* wm_files.c */
void wm_history_file_read(void);
-int wm_homefile_read(struct bContext *C, struct ReportList *reports, bool from_memory, const char *filepath);
+int wm_homefile_read(
+ struct bContext *C, struct ReportList *reports,
+ bool use_factory_settings, bool use_empty_data, bool use_userdef,
+ const char *filepath_startup_override, const char *app_template_override);
void wm_file_read_report(bContext *C);
void WM_OT_save_homefile(struct wmOperatorType *ot);
diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h
index c106f9d7851..5209af87960 100644
--- a/source/blender/windowmanager/wm_window.h
+++ b/source/blender/windowmanager/wm_window.h
@@ -63,8 +63,6 @@ void wm_window_swap_buffers (wmWindow *win);
void wm_window_set_swap_interval(wmWindow *win, int interval);
bool wm_window_get_swap_interval(wmWindow *win, int *intervalOut);
-float wm_window_pixelsize(wmWindow *win);
-
void wm_get_cursor_position (wmWindow *win, int *x, int *y);
void wm_cursor_position_from_ghost (wmWindow *win, int *x, int *y);
void wm_cursor_position_to_ghost (wmWindow *win, int *x, int *y);
@@ -80,6 +78,7 @@ void wm_window_IME_end (wmWindow *win);
int wm_window_close_exec(bContext *C, struct wmOperator *op);
int wm_window_duplicate_exec(bContext *C, struct wmOperator *op);
int wm_window_fullscreen_toggle_exec(bContext *C, struct wmOperator *op);
+void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win) ATTR_NONNULL();
/* Initial (unmaximized) size to start with for
* systems that can't find it for themselves (X11).