diff options
Diffstat (limited to 'source/blender/windowmanager')
33 files changed, 5933 insertions, 644 deletions
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index a8b3c994d24..464906dad1e 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -25,12 +25,15 @@ set(INC . + manipulators + manipulators/intern ../blenfont ../blenkernel ../blenlib ../blenloader ../blentranslation ../compositor + ../depsgraph ../editors/include ../gpu ../imbuf @@ -69,6 +72,12 @@ set(SRC intern/wm_subwindow.c intern/wm_window.c intern/wm_stereo.c + manipulators/intern/wm_manipulator.c + manipulators/intern/wm_manipulator_group.c + manipulators/intern/wm_manipulator_group_type.c + manipulators/intern/wm_manipulator_map.c + manipulators/intern/wm_manipulator_target_props.c + manipulators/intern/wm_manipulator_type.c WM_api.h WM_keymap.h @@ -81,10 +90,15 @@ set(SRC wm_files.h wm_subwindow.h wm_window.h + manipulators/WM_manipulator_api.h + manipulators/WM_manipulator_types.h + manipulators/wm_manipulator_fn.h + manipulators/wm_manipulator_wmapi.h + manipulators/intern/wm_manipulator_intern.h ) if(WITH_AUDASPACE) - add_definitions(${AUDASPACE_DEFINITIONS}) + add_definitions(-DWITH_AUDASPACE) list(APPEND INC_SYS ${AUDASPACE_C_INCLUDE_DIRS} diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index e40026880c4..46aea519340 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -64,12 +64,17 @@ struct wmDrag; struct ImBuf; struct ImageFormatData; struct ARegion; +struct ScrArea; +struct Main; #ifdef WITH_INPUT_NDOF struct wmNDOFMotionData; #endif typedef struct wmJob wmJob; +typedef struct wmManipulator wmManipulator; +typedef struct wmManipulatorMap wmManipulatorMap; +typedef struct wmManipulatorMapType wmManipulatorMapType; /* general API */ void WM_init_state_size_set (int stax, int stay, int sizx, int sizy); @@ -94,6 +99,22 @@ int WM_window_pixels_x (struct wmWindow *win); int WM_window_pixels_y (struct wmWindow *win); bool WM_window_is_fullscreen (struct wmWindow *win); +void WM_windows_scene_data_sync(const ListBase *win_lb, struct Scene *scene) ATTR_NONNULL(); +struct Scene *WM_windows_scene_get_from_screen(const struct wmWindowManager *wm, const struct bScreen *screen) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +struct WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const struct bScreen *screen) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; + +struct Scene *WM_window_get_active_scene(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +void WM_window_change_active_scene(struct Main *bmain, struct bContext *C, struct wmWindow *win, + struct Scene *scene_new) ATTR_NONNULL(); +struct WorkSpace *WM_window_get_active_workspace(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +void WM_window_set_active_workspace(struct wmWindow *win, struct WorkSpace *workspace) ATTR_NONNULL(1); +struct WorkSpaceLayout *WM_window_get_active_layout(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +void WM_window_set_active_layout( + struct wmWindow *win, struct WorkSpace *workspace, struct WorkSpaceLayout *layout) ATTR_NONNULL(1); +struct bScreen *WM_window_get_active_screen(const struct wmWindow *win) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +void WM_window_set_active_screen(struct wmWindow *win, struct WorkSpace *workspace, struct bScreen *screen) ATTR_NONNULL(1); +bool WM_window_is_temp_screen(const struct wmWindow *win) ATTR_WARN_UNUSED_RESULT; + /* defines for 'type' WM_window_open_temp */ enum { WM_WINDOW_RENDER = 1, @@ -143,6 +164,7 @@ float WM_cursor_pressure (const struct wmWindow *win); /* event map */ int WM_userdef_event_map(int kmitype); +int WM_userdef_event_type_from_keymap_type(int kmitype); /* handlers */ @@ -172,6 +194,9 @@ void WM_event_free_ui_handler_all( wmUIHandlerFunc ui_handle, wmUIHandlerRemoveFunc ui_remove); struct wmEventHandler *WM_event_add_modal_handler(struct bContext *C, struct wmOperator *op); +void WM_event_modal_handler_area_replace(wmWindow *win, const struct ScrArea *old_area, struct ScrArea *new_area); +void WM_event_modal_handler_region_replace(wmWindow *win, const struct ARegion *old_region, struct ARegion *new_region); + void WM_event_remove_handlers(struct bContext *C, ListBase *handlers); /* handler flag */ @@ -230,6 +255,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); +int WM_enum_search_invoke_previews(struct bContext *C, struct wmOperator *op, short prv_cols, short prv_rows); 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); @@ -423,9 +449,7 @@ ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); void wmSubWindowSet (struct wmWindow *win, int swinid); void wmSubWindowScissorSet (struct wmWindow *win, int swinid, const struct rcti *srct, bool srct_pad); - /* OpenGL utilities with safety check + working in modelview matrix mode */ -void wmFrustum (float x1, float x2, float y1, float y2, float n, float f); -void wmOrtho (float x1, float x2, float y1, float y2, float n, float f); + /* OpenGL utilities with safety check */ void wmOrtho2 (float x1, float x2, float y1, float y2); /* use for conventions (avoid hard-coded offsets all over) */ void wmOrtho2_region_pixelspace(const struct ARegion *ar); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 6c47d61c535..f48a2cef1e6 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -119,6 +119,10 @@ struct ImBuf; /* exported types for WM */ #include "wm_cursors.h" #include "wm_event_types.h" +#include "manipulators/WM_manipulator_types.h" + +/* Include external manipulator API's */ +#include "manipulators/WM_manipulator_api.h" /* ************** wmOperatorType ************************ */ @@ -223,7 +227,7 @@ typedef struct wmNotifier { #define NOTE_CATEGORY 0xFF000000 #define NC_WM (1<<24) #define NC_WINDOW (2<<24) -#define NC_SCREEN (3<<24) +#define NC_SCREEN (3<<24) #define NC_SCENE (4<<24) #define NC_OBJECT (5<<24) #define NC_MATERIAL (6<<24) @@ -257,15 +261,16 @@ typedef struct wmNotifier { #define ND_JOB (5<<16) #define ND_UNDO (6<<16) - /* NC_SCREEN screen */ -#define ND_SCREENBROWSE (1<<16) -#define ND_SCREENDELETE (2<<16) + /* NC_SCREEN */ +#define ND_LAYOUTBROWSE (1<<16) +#define ND_LAYOUTDELETE (2<<16) #define ND_SCREENCAST (3<<16) #define ND_ANIMPLAY (4<<16) #define ND_GPENCIL (5<<16) #define ND_EDITOR_CHANGED (6<<16) /*sent to new editors after switching to them*/ -#define ND_SCREENSET (7<<16) +#define ND_LAYOUTSET (7<<16) #define ND_SKETCH (8<<16) +#define ND_WORKSPACE_SET (9<<16) /* NC_SCENE Scene */ #define ND_SCENEBROWSE (1<<16) diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index a09cc4aeb31..e953f0c1712 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -433,10 +433,12 @@ void wm_add_default(bContext *C) 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 */ - + struct WorkSpace *workspace = G.main->workspaces.last; + CTX_wm_manager_set(C, wm); win = wm_window_new(C); - win->screen = screen; + WM_window_set_active_workspace(win, workspace); + WM_window_set_active_screen(win, workspace, screen); screen->winid = win->winid; BLI_strncpy(win->screenname, screen->id.name + 2, sizeof(win->screenname)); @@ -457,7 +459,7 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) wm_autosave_timer_ended(wm); while ((win = BLI_pophead(&wm->windows))) { - win->screen = NULL; /* prevent draw clear to use screen */ + WM_window_set_active_workspace(win, NULL); /* prevent draw clear to use screen */ wm_draw_window_clear(win); wm_window_free(C, wm, win); } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index dd01efdb4c4..05d9689565c 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -46,6 +46,8 @@ #include "BKE_context.h" +#include "GPU_shader.h" + #include "IMB_imbuf_types.h" #include "UI_interface.h" @@ -268,10 +270,10 @@ void wm_drags_check_ops(bContext *C, const wmEvent *event) static void wm_drop_operator_draw(const char *name, int x, int y) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - const unsigned char fg[4] = {255, 255, 255, 255}; - const unsigned char bg[4] = {0, 0, 0, 50}; + const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; - UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, fg, bg); + UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg); } static const char *wm_drag_name(wmDrag *drag) @@ -332,8 +334,10 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) if (rect) drag_rect_minmax(rect, x, y, x + drag->sx, y + drag->sy); else { - glColor4f(1.0, 1.0, 1.0, 0.65); /* this blends texture */ - glaDrawPixelsTexScaled(x, y, drag->imb->x, drag->imb->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, drag->imb->rect, drag->scale, drag->scale); + float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */ + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); + immDrawPixelsTexScaled(&state, x, y, drag->imb->x, drag->imb->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, + drag->imb->rect, drag->scale, drag->scale, 1.0f, 1.0f, col); } } else { @@ -361,8 +365,8 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) drag_rect_minmax(rect, x, y, x + w, y + iconsize); } else { - glColor4ub(255, 255, 255, 255); - UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag)); + const unsigned char col[] = {255, 255, 255, 255}; + UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag), col); } /* operator name with roundbox */ diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index f802e873555..c6b25ff8079 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -34,6 +34,8 @@ #include <string.h> #include "DNA_listBase.h" +#include "DNA_object_types.h" +#include "DNA_camera_types.h" #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" #include "DNA_userdef_types.h" @@ -57,8 +59,8 @@ #include "GPU_draw.h" #include "GPU_extensions.h" -#include "GPU_glew.h" -#include "GPU_basic_shader.h" +#include "GPU_immediate.h" +#include "GPU_viewport.h" #include "RE_engine.h" @@ -88,7 +90,7 @@ static void wm_paintcursor_draw(bContext *C, ARegion *ar) if (wm->paintcursors.first) { wmWindow *win = CTX_wm_window(C); - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); wmPaintCursor *pc; if (ar->swinid && screen->subwinactive == ar->swinid) { @@ -128,18 +130,18 @@ 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 Scene *scene, ScrArea *sa, ARegion *ar) { /* tag region for redraw from render engine preview running inside of it */ if (sa->spacetype == SPACE_VIEW3D) { RegionView3D *rv3d = ar->regiondata; RenderEngine *engine = (rv3d) ? rv3d->render_engine : NULL; + GPUViewport *viewport = (rv3d) ? rv3d->viewport : NULL; if (engine && (engine->flag & RE_ENGINE_DO_DRAW)) { - Scene *scene = screen->scene; View3D *v3d = sa->spacedata.first; rcti border_rect; @@ -151,6 +153,9 @@ static void wm_region_test_render_do_draw(bScreen *screen, ScrArea *sa, ARegion engine->flag &= ~RE_ENGINE_DO_DRAW; } + else if (viewport && GPU_viewport_do_update(viewport)) { + ED_region_tag_redraw(ar); + } } } @@ -159,7 +164,7 @@ static void wm_region_test_render_do_draw(bScreen *screen, ScrArea *sa, ARegion static void wm_method_draw_full(bContext *C, wmWindow *win) { - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; @@ -182,7 +187,7 @@ static void wm_method_draw_full(bContext *C, wmWindow *win) } ED_screen_draw(win); - win->screen->do_draw = false; + screen->do_draw = false; /* draw overlapping regions */ for (ar = screen->regionbase.first; ar; ar = ar->next) { @@ -238,7 +243,7 @@ static void wm_flush_regions_up(bScreen *screen, rcti *dirty) static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) { wmWindowManager *wm = CTX_wm_manager(C); - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; static rcti rect = {0, 0, 0, 0}; @@ -319,7 +324,7 @@ 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; + screen->do_draw = false; if (exchange) screen->swap = WIN_FRONT_OK; @@ -327,7 +332,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) else if (exchange) { if (screen->swap == WIN_FRONT_OK) { ED_screen_draw(win); - win->screen->do_draw = false; + screen->do_draw = false; screen->swap = WIN_BOTH_OK; } else if (screen->swap == WIN_BACK_OK) @@ -372,118 +377,116 @@ static void wm_draw_triple_fail(bContext *C, wmWindow *win) { wm_draw_window_clear(win); - win->drawfail = 1; + win->drawfail = true; wm_method_draw_overlap_all(C, win, 0); } -static int wm_triple_gen_textures(wmWindow *win, wmDrawTriple *triple) +static bool wm_triple_gen_textures(wmWindow *win, wmDrawTriple *triple) { - const int winsize_x = WM_window_pixels_x(win); - const int winsize_y = WM_window_pixels_y(win); - - GLint maxsize; - /* compute texture sizes */ - if (GLEW_ARB_texture_rectangle || GLEW_NV_texture_rectangle || GLEW_EXT_texture_rectangle) { - triple->target = GL_TEXTURE_RECTANGLE_ARB; - } - else { - triple->target = GL_TEXTURE_2D; - } - - triple->x = winsize_x; - triple->y = winsize_y; + triple->x = WM_window_pixels_x(win); + triple->y = WM_window_pixels_y(win); + +#if USE_TEXTURE_RECTANGLE + /* GL_TEXTURE_RECTANGLE is part of GL 3.1 so we can use it soon without runtime checks */ + triple->target = GL_TEXTURE_RECTANGLE; +#else + triple->target = GL_TEXTURE_2D; +#endif /* generate texture names */ glGenTextures(1, &triple->bind); - if (!triple->bind) { - /* not the typical failure case but we handle it anyway */ - printf("WM: failed to allocate texture for triple buffer drawing (glGenTextures).\n"); - return 0; - } - /* proxy texture is only guaranteed to test for the cases that * there is only one texture in use, which may not be the case */ - maxsize = GPU_max_texture_size(); + const GLint maxsize = GPU_max_texture_size(); if (triple->x > maxsize || triple->y > maxsize) { - glBindTexture(triple->target, 0); printf("WM: failed to allocate texture for triple buffer drawing " - "(texture too large for graphics card).\n"); - return 0; + "(texture too large for graphics card).\n"); + return false; } /* setup actual texture */ glBindTexture(triple->target, triple->bind); - glTexImage2D(triple->target, 0, GL_RGB8, triple->x, triple->y, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + + /* no mipmaps */ +#if USE_TEXTURE_RECTANGLE + /* already has no mipmaps */ +#else + glTexParameteri(triple->target, GL_TEXTURE_MAX_LEVEL, 0); + /* GL_TEXTURE_BASE_LEVEL = 0 by default */ +#endif + glTexParameteri(triple->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(triple->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glBindTexture(triple->target, 0); - /* not sure if this works everywhere .. */ - if (glGetError() == GL_OUT_OF_MEMORY) { - printf("WM: failed to allocate texture for triple buffer drawing (out of memory).\n"); - return 0; - } + glTexImage2D(triple->target, 0, GL_RGB8, triple->x, triple->y, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - return 1; + glBindTexture(triple->target, 0); + + return true; } -void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha, bool is_interlace) +void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha) { const int sizex = WM_window_pixels_x(win); const int sizey = WM_window_pixels_y(win); - float halfx, halfy, ratiox, ratioy; - /* wmOrtho for the screen has this same offset */ - ratiox = sizex; - ratioy = sizey; - halfx = GLA_PIXEL_OFS; - halfy = GLA_PIXEL_OFS; + float ratiox = sizex; + float ratioy = sizey; + float halfx = GLA_PIXEL_OFS; + float halfy = GLA_PIXEL_OFS; +#if USE_TEXTURE_RECTANGLE /* texture rectangle has unnormalized coordinates */ - if (triple->target == GL_TEXTURE_2D) { - ratiox /= triple->x; - ratioy /= triple->y; - halfx /= triple->x; - halfy /= triple->y; - } +#else + ratiox /= triple->x; + ratioy /= triple->y; + halfx /= triple->x; + halfy /= triple->y; +#endif - /* interlace stereo buffer bind the shader before calling wm_triple_draw_textures */ - if (is_interlace) { - glEnable(triple->target); - } - else { - GPU_basic_shader_bind((triple->target == GL_TEXTURE_2D) ? GPU_SHADER_TEXTURE_2D : GPU_SHADER_TEXTURE_RECT); - } + Gwn_VertFormat *format = immVertexFormat(); + unsigned int texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + const int activeTex = 7; /* arbitrary */ + glActiveTexture(GL_TEXTURE0 + activeTex); glBindTexture(triple->target, triple->bind); - glColor4f(1.0f, 1.0f, 1.0f, alpha); - glBegin(GL_QUADS); - glTexCoord2f(halfx, halfy); - glVertex2f(0, 0); +#if USE_TEXTURE_RECTANGLE + immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_RECT_MODULATE_ALPHA); +#else + immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA); + /* TODO: make pure 2D version + * and a 2D_IMAGE (replace, not modulate) version for when alpha = 1.0 + */ +#endif + immUniform1f("alpha", alpha); + immUniform1i("image", activeTex); - glTexCoord2f(ratiox + halfx, halfy); - glVertex2f(sizex, 0); + immBegin(GWN_PRIM_TRI_FAN, 4); - glTexCoord2f(ratiox + halfx, ratioy + halfy); - glVertex2f(sizex, sizey); + immAttrib2f(texcoord, halfx, halfy); + immVertex2f(pos, 0.0f, 0.0f); - glTexCoord2f(halfx, ratioy + halfy); - glVertex2f(0, sizey); - glEnd(); + immAttrib2f(texcoord, ratiox + halfx, halfy); + immVertex2f(pos, sizex, 0.0f); - glBindTexture(triple->target, 0); + immAttrib2f(texcoord, ratiox + halfx, ratioy + halfy); + immVertex2f(pos, sizex, sizey); - if (is_interlace) { - glDisable(triple->target); - } - else { - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); - } + immAttrib2f(texcoord, halfx, ratioy + halfy); + immVertex2f(pos, 0.0f, sizey); + + immEnd(); + immUnbindProgram(); + + glBindTexture(triple->target, 0); + if (activeTex != 0) + glActiveTexture(GL_TEXTURE0); } static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple) @@ -492,8 +495,8 @@ static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple) const int sizey = WM_window_pixels_y(win); glBindTexture(triple->target, triple->bind); + /* what is GL_READ_BUFFER right now? */ glCopyTexSubImage2D(triple->target, 0, 0, 0, 0, 0, sizex, sizey); - glBindTexture(triple->target, 0); } @@ -503,10 +506,12 @@ static void wm_draw_region_blend(wmWindow *win, ARegion *ar, wmDrawTriple *tripl /* region blend always is 1, except when blend timer is running */ if (fac < 1.0f) { - wmSubWindowScissorSet(win, win->screen->mainwin, &ar->winrct, true); + bScreen *screen = WM_window_get_active_screen(win); + + wmSubWindowScissorSet(win, screen->mainwin, &ar->winrct, true); glEnable(GL_BLEND); - wm_triple_draw_textures(win, triple, 1.0f - fac, false); + wm_triple_draw_textures(win, triple, 1.0f - fac); glDisable(GL_BLEND); } } @@ -514,20 +519,21 @@ 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; + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; - int copytex = false; + bool copytex = false; if (drawdata && drawdata->triple) { - glClearColor(0, 0, 0, 0); +#if 0 /* why do we need to clear before overwriting? */ + glClearColor(1, 1, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +#endif wmSubWindowSet(win, screen->mainwin); - wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); + wm_triple_draw_textures(win, drawdata->triple, 1.0f); } else { /* we run it when we start OR when we turn stereo on */ @@ -554,7 +560,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 +568,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); @@ -623,7 +628,7 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) /* after area regions so we can do area 'overlay' drawing */ ED_screen_draw(win); - win->screen->do_draw = false; + WM_window_get_active_screen(win)->do_draw = false; /* draw floating regions (menus) */ for (ar = screen->regionbase.first; ar; ar = ar->next) { @@ -650,7 +655,7 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoV wmWindowManager *wm = CTX_wm_manager(C); wmDrawData *drawdata; wmDrawTriple *triple_data, *triple_all; - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; int copytex = false; @@ -662,12 +667,14 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoV if (drawdata && drawdata->triple) { if (id == 0) { +#if 0 /* why do we need to clear before overwriting? */ glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +#endif wmSubWindowSet(win, screen->mainwin); - wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); + wm_triple_draw_textures(win, drawdata->triple, 1.0f); } } else { @@ -703,9 +710,12 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoV case SPACE_VIEW3D: { View3D *v3d = sa->spacedata.first; - BGpic *bgpic = v3d->bgpicbase.first; - v3d->multiview_eye = sview; - if (bgpic) bgpic->iuser.multiview_eye = sview; + if (v3d->camera && v3d->camera->type == OB_CAMERA) { + Camera *cam = v3d->camera->data; + CameraBGImage *bgpic = cam->bg_images.first; + v3d->multiview_eye = sview; + if (bgpic) bgpic->iuser.multiview_eye = sview; + } break; } case SPACE_NODE: @@ -794,7 +804,7 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoV /* after area regions so we can do area 'overlay' drawing */ ED_screen_draw(win); if (sview == STEREO_RIGHT_ID) - win->screen->do_draw = false; + screen->do_draw = false; /* draw floating regions (menus) */ for (ar = screen->regionbase.first; ar; ar = ar->next) { @@ -826,11 +836,13 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, eStereoV /* quick test to prevent changing window drawable */ static bool wm_draw_update_test_window(wmWindow *win) { + const Scene *scene = WM_window_get_active_scene(win); + const bScreen *screen = WM_window_get_active_screen(win); 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 +851,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(scene, sa, ar); if (ar->swinid && ar->do_draw) do_draw = true; @@ -849,20 +861,20 @@ 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) @@ -888,15 +900,18 @@ void wm_tag_redraw_overlay(wmWindow *win, ARegion *ar) { /* for draw triple gestures, paint cursors don't need region redraw */ if (ar && win) { + bScreen *screen = WM_window_get_active_screen(win); + if (wm_automatic_draw_method(win) != USER_DRAW_TRIPLE) ED_region_tag_redraw(ar); - win->screen->do_draw_paintcursor = true; + screen->do_draw_paintcursor = true; } } void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *ar) { - win->screen->do_draw_paintcursor = true; + bScreen *screen = WM_window_get_active_screen(win); + screen->do_draw_paintcursor = true; wm_tag_redraw_overlay(win, ar); } @@ -904,7 +919,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(); @@ -930,16 +944,18 @@ void wm_draw_update(bContext *C) } if (wm_draw_update_test_window(win)) { + bScreen *screen = WM_window_get_active_screen(win); + 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); @@ -960,9 +976,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); @@ -983,7 +999,7 @@ void wm_draw_data_free(wmWindow *win) void wm_draw_window_clear(wmWindow *win) { - bScreen *screen = win->screen; + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa; ARegion *ar; @@ -1001,12 +1017,13 @@ void wm_draw_window_clear(wmWindow *win) void wm_draw_region_clear(wmWindow *win, ARegion *ar) { + bScreen *screen = WM_window_get_active_screen(win); int drawmethod = wm_automatic_draw_method(win); if (ELEM(drawmethod, USER_DRAW_OVERLAP, USER_DRAW_OVERLAP_FLIP)) - wm_flush_regions_down(win->screen, &ar->winrct); + wm_flush_regions_down(screen, &ar->winrct); - win->screen->do_draw = true; + screen->do_draw = true; } void WM_redraw_windows(bContext *C) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index a71202bfa71..7167e61fccd 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -39,6 +39,7 @@ #include "DNA_screen_types.h" #include "DNA_scene_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "DNA_userdef_types.h" #include "MEM_guardedalloc.h" @@ -53,10 +54,12 @@ #include "BKE_context.h" #include "BKE_idprop.h" #include "BKE_global.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_workspace.h" #include "BKE_sound.h" @@ -68,8 +71,6 @@ #include "RNA_access.h" -#include "GPU_debug.h" - #include "UI_interface.h" #include "PIL_time.h" @@ -277,15 +278,20 @@ void wm_event_do_refresh_wm_and_depsgraph(bContext *C) /* 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); + const Scene *scene = WM_window_get_active_scene(win); + const bScreen *screen = WM_window_get_active_screen(win); + + win_combine_v3d_datamask |= ED_view3d_screen_datamask(scene, screen); } /* cached: editor refresh callbacks now, they get context */ for (wmWindow *win = wm->windows.first; win; win = win->next) { + const bScreen *screen = WM_window_get_active_screen(win); + Scene *scene = WM_window_get_active_scene(win); ScrArea *sa; CTX_wm_window_set(C, win); - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + for (sa = screen->areabase.first; sa; sa = sa->next) { if (sa->do_refresh) { CTX_wm_area_set(C, sa); ED_area_do_refresh(C, sa); @@ -298,12 +304,12 @@ void wm_event_do_refresh_wm_and_depsgraph(bContext *C) Main *bmain = CTX_data_main(C); /* copied to set's in scene_update_tagged_recursive() */ - win->screen->scene->customdata_mask = win_combine_v3d_datamask; + 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; + scene->customdata_mask |= scene->customdata_mask_modal; - BKE_scene_update_tagged(bmain->eval_ctx, bmain, win->screen->scene); + BKE_scene_update_tagged(bmain->eval_ctx, bmain, scene); } } @@ -322,6 +328,7 @@ void wm_event_do_notifiers(bContext *C) /* cache & catch WM level notifiers, such as frame change, scene/screen set */ for (win = wm->windows.first; win; win = win->next) { + Scene *scene = WM_window_get_active_scene(win); bool do_anim = false; CTX_wm_window_set(C, win); @@ -339,17 +346,31 @@ void wm_event_do_notifiers(bContext *C) } if (note->window == win) { if (note->category == NC_SCREEN) { - if (note->data == ND_SCREENBROWSE) { + if (note->data == ND_WORKSPACE_SET) { + WorkSpace *ref_ws = note->reference; + + UI_popup_handlers_remove_all(C, &win->modalhandlers); + + ED_workspace_change(ref_ws, C, wm, win); + if (G.debug & G_DEBUG_EVENTS) + printf("%s: Workspace set %p\n", __func__, note->reference); + } + else if (note->data == ND_LAYOUTBROWSE) { + bScreen *ref_screen = BKE_workspace_layout_screen_get(note->reference); + /* free popup handlers only [#35434] */ UI_popup_handlers_remove_all(C, &win->modalhandlers); - ED_screen_set(C, note->reference); // XXX hrms, think this over! + ED_screen_change(C, ref_screen); /* XXX hrms, think this over! */ if (G.debug & G_DEBUG_EVENTS) printf("%s: screen set %p\n", __func__, note->reference); } - else if (note->data == ND_SCREENDELETE) { - ED_screen_delete(C, note->reference); // XXX hrms, think this over! + else if (note->data == ND_LAYOUTDELETE) { + WorkSpace *workspace = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout = note->reference; + + ED_workspace_layout_delete(workspace, layout, C); // XXX hrms, think this over! if (G.debug & G_DEBUG_EVENTS) printf("%s: screen delete %p\n", __func__, note->reference); } @@ -357,7 +378,7 @@ void wm_event_do_notifiers(bContext *C) } if (note->window == win || - (note->window == NULL && (note->reference == NULL || note->reference == win->screen->scene))) + (note->window == NULL && (note->reference == NULL || note->reference == scene))) { if (note->category == NC_SCENE) { if (note->data == ND_FRAME) @@ -365,19 +386,20 @@ void wm_event_do_notifiers(bContext *C) } } if (ELEM(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_WM)) { - ED_info_stats_clear(win->screen->scene); + SceneLayer *sl = CTX_data_scene_layer(C); + ED_info_stats_clear(sl); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL); } } if (do_anim) { /* XXX, quick frame changes can cause a crash if framechange and rendering - * collide (happens on slow scenes), BKE_scene_update_for_newframe can be called + * collide (happens on slow scenes), BKE_scene_graph_update_for_newframe can be called * twice which can depgraph update the same object at once */ if (G.is_rendering == false) { /* depsgraph gets called, might send more notifiers */ - ED_update_for_newframe(CTX_data_main(C), win->screen->scene, 1); + ED_update_for_newframe(CTX_data_main(C), scene, CTX_data_depsgraph(C)); } } } @@ -385,12 +407,20 @@ void wm_event_do_notifiers(bContext *C) /* the notifiers are sent without context, to keep it clean */ while ((note = BLI_pophead(&wm->queue))) { for (win = wm->windows.first; win; win = win->next) { - + Scene *scene = WM_window_get_active_scene(win); + bScreen *screen = WM_window_get_active_screen(win); + WorkSpace *workspace = WM_window_get_active_workspace(win); + /* filter out notifiers */ - if (note->category == NC_SCREEN && note->reference && note->reference != win->screen) { + if (note->category == NC_SCREEN && + note->reference && + note->reference != screen && + note->reference != workspace && + note->reference != WM_window_get_active_layout(win)) + { /* pass */ } - else if (note->category == NC_SCENE && note->reference && note->reference != win->screen->scene) { + else if (note->category == NC_SCENE && note->reference && note->reference != scene) { /* pass */ } else { @@ -403,14 +433,14 @@ void wm_event_do_notifiers(bContext *C) /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name + 2, note->category); */ ED_screen_do_listen(C, note); - for (ar = win->screen->regionbase.first; ar; ar = ar->next) { - ED_region_do_listen(win->screen, NULL, ar, note); + for (ar = screen->regionbase.first; ar; ar = ar->next) { + ED_region_do_listen(screen, NULL, ar, note, scene); } - for (sa = win->screen->areabase.first; sa; sa = sa->next) { - ED_area_do_listen(win->screen, sa, note); + for (sa = screen->areabase.first; sa; sa = sa->next) { + ED_area_do_listen(screen, sa, note, scene, workspace); for (ar = sa->regionbase.first; ar; ar = ar->next) { - ED_region_do_listen(win->screen, sa, ar, note); + ED_region_do_listen(screen, sa, ar, note, scene); } } } @@ -418,7 +448,7 @@ void wm_event_do_notifiers(bContext *C) MEM_freeN(note); } - + wm_event_do_refresh_wm_and_depsgraph(C); } @@ -1571,6 +1601,36 @@ int WM_userdef_event_map(int kmitype) return kmitype; } +/** + * Use so we can check if 'wmEvent.type' is released in modal operators. + * + * An alternative would be to add a 'wmEvent.type_nokeymap'... or similar. + */ +int WM_userdef_event_type_from_keymap_type(int kmitype) +{ + switch (kmitype) { + case SELECTMOUSE: + return (U.flag & USER_LMOUSESELECT) ? LEFTMOUSE : RIGHTMOUSE; + case ACTIONMOUSE: + return (U.flag & USER_LMOUSESELECT) ? RIGHTMOUSE : LEFTMOUSE; + case EVT_TWEAK_S: + return (U.flag & USER_LMOUSESELECT) ? LEFTMOUSE : RIGHTMOUSE; + case EVT_TWEAK_A: + return (U.flag & USER_LMOUSESELECT) ? RIGHTMOUSE : LEFTMOUSE; + case EVT_TWEAK_L: + return LEFTMOUSE; + case EVT_TWEAK_M: + return MIDDLEMOUSE; + case EVT_TWEAK_R: + return RIGHTMOUSE; + case WHEELOUTMOUSE: + return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE; + case WHEELINMOUSE: + return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE; + } + + return kmitype; +} static int wm_eventmatch(const wmEvent *winevent, wmKeyMapItem *kmi) { @@ -1719,7 +1779,7 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand wm_handler_op_context(C, handler, event); wm_region_mouse_co(C, event); wm_event_modalkeymap(C, op, event, &dbl_click_disabled); - + if (ot->flag & OPTYPE_UNDO) wm->op_undo_depth++; @@ -1768,6 +1828,9 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand CTX_wm_region_set(C, NULL); } + /* update manipulators during modal handlers */ + wm_manipulatormaps_handled_modal_update(C, event, handler); + /* remove modal handler, operator itself should have been canceled and freed */ if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) { WM_cursor_grab_disable(CTX_wm_window(C), NULL); @@ -2140,6 +2203,137 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers } } } + else if (handler->manipulator_map) { + ScrArea *area = CTX_wm_area(C); + ARegion *region = CTX_wm_region(C); + wmManipulatorMap *mmap = handler->manipulator_map; + wmManipulator *mpr = wm_manipulatormap_highlight_get(mmap); + + if (region->manipulator_map != handler->manipulator_map) { + WM_manipulatormap_tag_refresh(handler->manipulator_map); + } + + wm_manipulatormap_handler_context(C, handler); + wm_region_mouse_co(C, event); + + /* handle manipulator highlighting */ + if (event->type == MOUSEMOVE && !wm_manipulatormap_modal_get(mmap)) { + int part; + mpr = wm_manipulatormap_highlight_find(mmap, C, event, &part); + wm_manipulatormap_highlight_set(mmap, C, mpr, part); + } + /* handle user configurable manipulator-map keymap */ + else { + /* Either we operate on a single highlighted item + * or groups attached to the selected manipulators. + * To simplify things both cases loop over an array of items. */ + wmManipulatorGroup *mgroup_first; + bool is_mgroup_single; + + if (ISMOUSE(event->type)) { + /* Keep mpr set as-is, just fake single selection. */ + if (mpr) { + mgroup_first = mpr->parent_mgroup; + } + else { + mgroup_first = NULL; + } + is_mgroup_single = true; + } + else { + if (WM_manipulatormap_is_any_selected(mmap)) { + const ListBase *groups = WM_manipulatormap_group_list(mmap); + mgroup_first = groups->first; + } + else { + mgroup_first = NULL; + } + is_mgroup_single = false; + } + + /* Don't use from now on. */ + mpr = NULL; + + for (wmManipulatorGroup *mgroup = mgroup_first; mgroup; mgroup = mgroup->next) { + /* get user customized keymap from default one */ + + if ((is_mgroup_single == false) && + /* We might want to change the logic here and use some kind of manipulator edit-mode. + * For now just use keymap when a selection exists. */ + wm_manipulatorgroup_is_any_selected(mgroup) == false) + { + continue; + } + + const wmKeyMap *keymap = WM_keymap_active(wm, mgroup->type->keymap); + wmKeyMapItem *kmi; + + PRINT("%s: checking '%s' ...", __func__, keymap->idname); + + if (!keymap->poll || keymap->poll(C)) { + PRINT("pass\n"); + for (kmi = keymap->items.first; kmi; kmi = kmi->next) { + if (wm_eventmatch(event, kmi)) { + wmOperator *op = handler->op; + + PRINT("%s: item matched '%s'\n", __func__, kmi->idname); + + /* weak, but allows interactive callback to not use rawkey */ + event->keymap_idname = kmi->idname; + + CTX_wm_manipulator_group_set(C, mgroup); + + /* handler->op is called later, we want keymap op to be triggered here */ + handler->op = NULL; + action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr); + handler->op = op; + + CTX_wm_manipulator_group_set(C, NULL); + + if (action & WM_HANDLER_BREAK) { + if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) { + printf("%s: handled - and pass on! '%s'\n", + __func__, 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); + } + } + else { + PRINT("%s: un-handled '%s'\n", + __func__, kmi->idname); + } + } + } + } + } + else { + PRINT("fail\n"); + } + + if (action & WM_HANDLER_BREAK) { + break; + } + + if (is_mgroup_single) { + break; + } + } + } + + /* restore the area */ + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + + if (handler->op) { + action |= wm_handler_operator_call(C, handlers, handler, event, NULL); + } + } else { /* modal, swallows all */ action |= wm_handler_operator_call(C, handlers, handler, event, NULL); @@ -2320,17 +2514,19 @@ static void wm_paintcursor_test(bContext *C, const wmEvent *event) static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event) { + bScreen *screen = WM_window_get_active_screen(win); + if (BLI_listbase_is_empty(&wm->drags)) { return; } if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) { - win->screen->do_draw_drag = true; + screen->do_draw_drag = true; } else if (event->type == ESCKEY) { WM_drag_free_list(&wm->drags); - win->screen->do_draw_drag = true; + screen->do_draw_drag = true; } else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { event->type = EVT_DROP; @@ -2346,16 +2542,16 @@ static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *even event->customdatafree = 1; /* clear drop icon */ - win->screen->do_draw_drag = true; + screen->do_draw_drag = true; /* restore cursor (disabled, see wm_dragdrop.c) */ // WM_cursor_modal_restore(win); } /* overlap fails otherwise */ - if (win->screen->do_draw_drag) + if (screen->do_draw_drag) if (win->drawmethod == USER_DRAW_OVERLAP) - win->screen->do_draw = true; + screen->do_draw = true; } @@ -2385,22 +2581,28 @@ void wm_event_do_handlers(bContext *C) /* update key configuration before handling events */ WM_keyconfig_update(wm); + WM_manipulatorconfig_update(CTX_data_main(C)); for (win = wm->windows.first; win; win = win->next) { + bScreen *screen = WM_window_get_active_screen(win); wmEvent *event; - - if (win->screen == NULL) + + /* some safty checks - these should always be set! */ + BLI_assert(WM_window_get_active_scene(win)); + BLI_assert(WM_window_get_active_screen(win)); + BLI_assert(WM_window_get_active_workspace(win)); + + if (screen == NULL) wm_event_free_all(win); else { - Scene *scene = win->screen->scene; + Scene *scene = WM_window_get_active_scene(win); if (scene) { - int is_playing_sound = BKE_sound_scene_playing(win->screen->scene); + int is_playing_sound = BKE_sound_scene_playing(scene); if (is_playing_sound != -1) { bool is_playing_screen; CTX_wm_window_set(C, win); - CTX_wm_screen_set(C, win->screen); CTX_data_scene_set(C, scene); is_playing_screen = (ED_screen_animation_playing(wm) != NULL); @@ -2417,7 +2619,7 @@ void wm_event_do_handlers(bContext *C) int ncfra = time * (float)FPS + 0.5f; if (ncfra != scene->r.cfra) { scene->r.cfra = ncfra; - ED_update_for_newframe(CTX_data_main(C), scene, 1); + ED_update_for_newframe(CTX_data_main(C), scene, CTX_data_depsgraph(C)); WM_event_add_notifier(C, NC_WINDOW, NULL); } } @@ -2433,6 +2635,9 @@ void wm_event_do_handlers(bContext *C) while ( (event = win->queue.first) ) { int action = WM_HANDLER_CONTINUE; + /* active screen might change during handlers, update pointer */ + screen = WM_window_get_active_screen(win); + 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); @@ -2490,12 +2695,12 @@ void wm_event_do_handlers(bContext *C) } #endif - for (sa = win->screen->areabase.first; sa; sa = sa->next) { + for (sa = screen->areabase.first; sa; sa = sa->next) { /* after restoring a screen from SCREENMAXIMIZED we have to wait * with the screen handling till the region coordinates are updated */ - if (win->screen->skip_handling == true) { + if (screen->skip_handling == true) { /* restore for the next iteration of wm_event_do_handlers */ - win->screen->skip_handling = false; + screen->skip_handling = false; break; } @@ -2522,9 +2727,42 @@ void wm_event_do_handlers(bContext *C) wm_drags_check_ops(C, event); } } - + +#ifdef USE_WORKSPACE_TOOL + /* How to solve properly? + * + * Handlers are stored in each region, + * however the tool-system swaps keymaps often and isn't stored + * per region. + * + * Need to investigate how this could be done better. + * We might need to add a more dynamic handler type that uses a callback + * to fetch its current keymap. + */ + wmEventHandler sneaky_handler = {NULL}; + if (ar->regiontype == RGN_TYPE_WINDOW) { + WorkSpace *workspace = WM_window_get_active_workspace(win); + if (workspace->tool.keymap[0] && + workspace->tool.spacetype == sa->spacetype) + { + wmKeyMap *km = WM_keymap_find_all( + C, workspace->tool.keymap, sa->spacetype, RGN_TYPE_WINDOW); + if (km != NULL) { + sneaky_handler.keymap = km; + BLI_addhead(&ar->handlers, &sneaky_handler); + } + } + } +#endif /* USE_WORKSPACE_TOOL */ + action |= wm_handlers_do(C, event, &ar->handlers); +#ifdef USE_WORKSPACE_TOOL + if (sneaky_handler.keymap) { + BLI_remlink(&ar->handlers, &sneaky_handler); + } +#endif /* USE_WORKSPACE_TOOL */ + /* fileread case (python), [#29489] */ if (CTX_wm_window(C) == NULL) return; @@ -2589,8 +2827,7 @@ void wm_event_do_handlers(bContext *C) /* update key configuration after handling events */ WM_keyconfig_update(wm); - - GPU_ASSERT_NO_GL_ERRORS("wm_event_do_handlers"); + WM_manipulatorconfig_update(CTX_data_main(C)); } /* ********** filesector handling ************ */ @@ -2703,6 +2940,34 @@ wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op) return handler; } +/** + * Modal handlers store a pointer to an area which might be freed while the handler runs. + * Use this function to NULL all handler pointers to \a old_area. + */ +void WM_event_modal_handler_area_replace(wmWindow *win, const ScrArea *old_area, ScrArea *new_area) +{ + for (wmEventHandler *handler = win->modalhandlers.first; handler; handler = handler->next) { + /* fileselect handler is quite special... it needs to keep old area stored in handler, so don't change it */ + if ((handler->op_area == old_area) && (handler->type != WM_HANDLER_FILESELECT)) { + handler->op_area = new_area; + } + } +} + +/** + * Modal handlers store a pointer to a region which might be freed while the handler runs. + * Use this function to NULL all handler pointers to \a old_region. + */ +void WM_event_modal_handler_region_replace(wmWindow *win, const ARegion *old_region, ARegion *new_region) +{ + for (wmEventHandler *handler = win->modalhandlers.first; handler; handler = handler->next) { + if (handler->op_region == old_region) { + handler->op_region = new_region; + handler->op_region_type = new_region ? new_region->regiontype : RGN_TYPE_WINDOW; + } + } +} + wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap) { wmEventHandler *handler; diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 637ace9cd82..33f7a2ecb4d 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -69,6 +69,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "BKE_appdir.h" #include "BKE_utildefines.h" @@ -77,7 +78,6 @@ #include "BKE_blendfile.h" #include "BKE_blender_undo.h" #include "BKE_context.h" -#include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" @@ -86,6 +86,7 @@ #include "BKE_sound.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_workspace.h" #include "BLO_readfile.h" #include "BLO_writefile.h" @@ -119,6 +120,8 @@ #include "BPY_extern.h" #endif +#include "DEG_depsgraph.h" + #include "WM_api.h" #include "WM_types.h" #include "wm.h" @@ -159,7 +162,7 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist) CTX_wm_window_set(C, win); /* needed by operator close callbacks */ WM_event_remove_handlers(C, &win->handlers); WM_event_remove_handlers(C, &win->modalhandlers); - ED_screen_exit(C, win, win->screen); + ED_screen_exit(C, win, WM_window_get_active_screen(win)); } } @@ -248,16 +251,23 @@ static void wm_window_match_do(bContext *C, ListBase *oldwmlist) /* match oldwm to new dbase, only old files */ for (wm = oldwmlist->first; wm; wm = wm->id.next) { - for (win = wm->windows.first; win; win = win->next) { + WorkSpace *workspace = WM_window_get_active_workspace(win); + /* all windows get active screen from file */ - if (screen->winid == 0) - win->screen = screen; - else - win->screen = ED_screen_duplicate(win, screen); - - BLI_strncpy(win->screenname, win->screen->id.name + 2, sizeof(win->screenname)); - win->screen->winid = win->winid; + if (screen->winid == 0) { + WM_window_set_active_screen(win, workspace, screen); + } + else { + WorkSpaceLayout *layout_old = WM_window_get_active_layout(win); + WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(workspace, layout_old, win); + + WM_window_set_active_layout(win, workspace, layout_new); + } + + bScreen *win_screen = WM_window_get_active_screen(win); + BLI_strncpy(win->screenname, win_screen->id.name + 2, sizeof(win->screenname)); + win_screen->winid = win->winid; } } } @@ -316,10 +326,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 read_userdef_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(); @@ -425,8 +433,9 @@ void wm_file_read_report(bContext *C) Scene *sce; for (sce = G.main->scene.first; sce; sce = sce->id.next) { - if (sce->r.engine[0] && - BLI_findstring(&R_engines, sce->r.engine, offsetof(RenderEngineType, idname)) == NULL) + ViewRender *view_render = &sce->view_render; + if (view_render->engine_id[0] && + BLI_findstring(&R_engines, view_render->engine_id, offsetof(RenderEngineType, idname)) == NULL) { if (reports == NULL) { reports = CTX_wm_reports(C); @@ -434,7 +443,7 @@ void wm_file_read_report(bContext *C) BKE_reportf(reports, RPT_ERROR, "Engine '%s' not available for scene '%s' (an add-on may need to be installed or enabled)", - sce->r.engine, sce->id.name + 2); + view_render->engine_id, sce->id.name + 2); } } @@ -462,7 +471,7 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo CTX_wm_window_set(C, wm->windows.first); ED_editors_init(C); - DAG_on_visible_update(CTX_data_main(C), true); + DEG_on_visible_update(CTX_data_main(C), true); #ifdef WITH_PYTHON if (is_startup_file) { @@ -580,7 +589,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) { @@ -839,10 +848,10 @@ int wm_homefile_read( /* prevent buggy files that had G_FILE_RELATIVE_REMAP written out by mistake. Screws up autosaves otherwise * can remove this eventually, only in a 2.53 and older, now its not written */ G.fileflags &= ~G_FILE_RELATIVE_REMAP; - - if (use_userdef) { + + if (use_userdef) { /* check userdef before open window, keymaps etc */ - wm_init_userdef(C, read_userdef_from_memory); + wm_init_userdef(CTX_data_main(C), read_userdef_from_memory); } /* match the read WM with current WM */ @@ -992,7 +1001,7 @@ static void wm_history_file_update(void) /* screen can be NULL */ -static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, BlendThumbnail **thumb_pt) +static ImBuf *blend_file_thumb(const bContext *C, Scene *scene, SceneLayer *scene_layer, bScreen *screen, BlendThumbnail **thumb_pt) { /* will be scaled down, but gives some nice oversampling */ ImBuf *ibuf; @@ -1004,6 +1013,10 @@ static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, BlendThumbnail **t ARegion *ar = NULL; View3D *v3d = NULL; + EvaluationContext eval_ctx; + + CTX_data_eval_ctx(C, &eval_ctx); + /* In case we are given a valid thumbnail data, just generate image from it. */ if (*thumb_pt) { thumb = *thumb_pt; @@ -1029,14 +1042,14 @@ static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, BlendThumbnail **t /* gets scaled to BLEN_THUMB_SIZE */ if (scene->camera) { ibuf = ED_view3d_draw_offscreen_imbuf_simple( - scene, scene->camera, + &eval_ctx, scene, scene_layer, scene->camera, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, 0, false, NULL, NULL, NULL, err_out); } else { ibuf = ED_view3d_draw_offscreen_imbuf( - scene, v3d, ar, + &eval_ctx, scene, scene_layer, v3d, ar, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, IB_rect, false, R_ALPHAPREMUL, 0, false, NULL, NULL, NULL, err_out); @@ -1132,7 +1145,7 @@ static int wm_file_write(bContext *C, const char *filepath, int fileflags, Repor /* Main now can store a .blend thumbnail, usefull for background mode or thumbnail customization. */ main_thumb = thumb = CTX_data_main(C)->blen_thumb; if ((U.flag & USER_SAVE_PREVIEWS) && BLI_thread_is_main()) { - ibuf_thumb = blend_file_thumb(CTX_data_scene(C), CTX_wm_screen(C), &thumb); + ibuf_thumb = blend_file_thumb(C, CTX_data_scene(C), CTX_data_scene_layer(C), CTX_wm_screen(C), &thumb); } /* operator now handles overwrite checks */ @@ -1387,7 +1400,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE); /* check current window and close it if temp */ - if (win && win->screen->temp) + if (win && WM_window_is_temp_screen(win)) wm_window_close(C, wm, win); /* update keymaps in user preferences */ @@ -1522,6 +1535,42 @@ void WM_OT_save_userpref(wmOperatorType *ot) ot->exec = wm_userpref_write_exec; } +static int wm_workspace_configuration_file_write_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + char filepath[FILE_MAX]; + + 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 workspace configuration file path"); + return OPERATOR_CANCELLED; + } + + BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_WORKSPACES_FILE, NULL); + printf("trying to save workspace configuration file at %s ", filepath); + + if (BKE_blendfile_workspace_config_write(bmain, filepath, op->reports) != 0) { + printf("ok\n"); + return OPERATOR_FINISHED; + } + else { + printf("fail\n"); + } + + return OPERATOR_CANCELLED; +} + +void WM_OT_save_workspace_file(wmOperatorType *ot) +{ + ot->name = "Save Workspace Configuration"; + ot->idname = "WM_OT_save_workspace_file"; + ot->description = "Save workspaces of the current file as part of the user configuration"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_workspace_configuration_file_write_exec; +} + static int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) { ED_file_read_bookmarks(); diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index ad71ce1aad9..cee08e1f8c1 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -59,7 +59,7 @@ #include "BLO_readfile.h" #include "BKE_context.h" -#include "BKE_depsgraph.h" +#include "BKE_layer.h" #include "BKE_library.h" #include "BKE_library_remap.h" #include "BKE_global.h" @@ -69,6 +69,8 @@ #include "BKE_idcode.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "IMB_colormanagement.h" @@ -131,8 +133,8 @@ static short wm_link_append_flag(wmOperator *op) if (RNA_boolean_get(op->ptr, "autoselect")) flag |= FILE_AUTOSELECT; - if (RNA_boolean_get(op->ptr, "active_layer")) - flag |= FILE_ACTIVELAY; + if (RNA_boolean_get(op->ptr, "active_collection")) + flag |= FILE_ACTIVE_COLLECTION; if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop)) flag |= FILE_RELPATH; if (RNA_boolean_get(op->ptr, "link")) @@ -212,7 +214,7 @@ static WMLinkAppendDataItem *wm_link_append_data_item_add( } static void wm_link_do( - WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d, + WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, SceneLayer *sl, const bool use_placeholders, const bool force_indirect) { Main *mainl; @@ -261,7 +263,7 @@ static void wm_link_do( } new_id = BLO_library_link_named_part_ex( - mainl, &bh, item->idcode, item->name, flag, scene, v3d, use_placeholders, force_indirect); + mainl, &bh, item->idcode, item->name, flag, scene, sl, use_placeholders, force_indirect); if (new_id) { /* If the link is successful, clear item's libs 'todo' flags. @@ -271,21 +273,58 @@ static void wm_link_do( } } - BLO_library_link_end(mainl, &bh, flag, scene, v3d); + BLO_library_link_end(mainl, &bh, flag, scene, sl); BLO_blendhandle_close(bh); } } +/** + * Check if an item defined by \a name and \a group can be appended/linked. + * + * \param reports: Optionally report an error when an item can't be appended/linked. + */ +static bool wm_link_append_item_poll( + ReportList *reports, const char *path, const char *group, const char *name, const bool do_append) +{ + short idcode; + + if (!group || !name) { + printf("skipping %s\n", path); + return false; + } + + idcode = BKE_idcode_from_name(group); + + /* XXX For now, we do a nasty exception for workspace, forbid linking them. + * Not nice, ultimately should be solved! */ + if (!BKE_idcode_is_linkable(idcode) && (do_append || idcode != ID_WS)) { + if (reports) { + if (do_append) { + BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, "Can't append data-block '%s' of type '%s'", name, group); + } + else { + BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, "Can't link data-block '%s' of type '%s'", name, group); + } + } + return false; + } + + return true; +} + static int wm_link_append_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + SceneLayer *sl = CTX_data_scene_layer(C); PropertyRNA *prop; WMLinkAppendData *lapp_data; char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX_LIBEXTRA], relname[FILE_MAX]; char *group, *name; int totfiles = 0; short flag; + bool has_item = false; + bool do_append; RNA_string_get(op->ptr, "filename", relname); RNA_string_get(op->ptr, "directory", root); @@ -323,6 +362,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) } flag = wm_link_append_flag(op); + do_append = (flag & FILE_LINK) == 0; /* sanity checks for flag */ if (scene && scene->id.lib) { @@ -334,8 +374,8 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) /* from here down, no error returns */ - if (scene && RNA_boolean_get(op->ptr, "autoselect")) { - BKE_scene_base_deselect_all(scene); + if (sl && RNA_boolean_get(op->ptr, "autoselect")) { + BKE_scene_layer_base_deselect_all(sl); } /* tag everything, all untagged data can be made local @@ -358,7 +398,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) BLI_join_dirfile(path, sizeof(path), root, relname); if (BLO_library_path_explode(path, libname, &group, &name)) { - if (!group || !name) { + if (!wm_link_append_item_poll(NULL, path, group, name, do_append)) { continue; } @@ -366,6 +406,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx)); lib_idx++; wm_link_append_data_library_add(lapp_data, libname); + has_item = true; } } } @@ -379,8 +420,8 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) if (BLO_library_path_explode(path, libname, &group, &name)) { WMLinkAppendDataItem *item; - if (!group || !name) { - printf("skipping %s\n", path); + + if (!wm_link_append_item_poll(op->reports, path, group, name, do_append)) { continue; } @@ -388,6 +429,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); BLI_BITMAP_ENABLE(item->libraries, lib_idx); + has_item = true; } } RNA_END; @@ -400,12 +442,18 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) wm_link_append_data_library_add(lapp_data, libname); item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); BLI_BITMAP_ENABLE(item->libraries, 0); + has_item = true; + } + + if (!has_item) { + 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, sl, false, false); /* BKE_main_unlock(bmain); */ @@ -414,7 +462,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) IMB_colormanagement_check_file_config(bmain); /* append, rather than linking */ - if ((flag & FILE_LINK) == 0) { + if (do_append) { const bool set_fake = RNA_boolean_get(op->ptr, "set_fake"); const bool use_recursive = RNA_boolean_get(op->ptr, "use_recursive"); @@ -445,13 +493,17 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) * link into other scenes from this blend file */ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + /* TODO(sergey): Use proper flag for tagging here. */ + + /* TODO (dalai): Temporary solution! + * Ideally we only need to tag the new objects themselves, not the scene. This way we'll avoid flush of + * collection properties to all objects and limit update to the particular object only. + * But afraid first we need to change collection evaluation in DEG according to depsgraph manifesto. + */ + DEG_id_tag_update(&scene->id, 0); + /* recreate dependency graph to include new objects */ - 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(); + DEG_relations_tag_update(bmain); /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ BLI_strncpy(G.lib, root, FILE_MAX); @@ -473,8 +525,8 @@ static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link) prop = RNA_def_boolean(ot->srna, "autoselect", true, "Select", "Select new objects"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "active_layer", true, - "Active Layer", "Put new objects on the active layer"); + prop = RNA_def_boolean(ot->srna, "active_collection", true, + "Active Collection", "Put new objects on the active collection"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "instance_groups", is_link, "Instance Groups", "Create Dupli-Group instances for each group"); @@ -554,7 +606,7 @@ static int wm_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *UN } static void lib_relocate_do( - Main *bmain, Scene *scene, + Main *bmain, Library *library, WMLinkAppendData *lapp_data, ReportList *reports, const bool do_reload) { ListBase *lbarray[MAX_LIBARRAY]; @@ -562,6 +614,7 @@ static void lib_relocate_do( LinkNode *itemlink; int item_idx; + bool has_item = false; /* Remove all IDs to be reloaded from Main. */ lba_idx = set_listbasepointers(bmain, lbarray); @@ -583,6 +636,7 @@ static void lib_relocate_do( BLI_remlink(lbarray[lba_idx], id); item = wm_link_append_data_item_add(lapp_data, id->name + 2, idcode, id); BLI_BITMAP_SET_ALL(item->libraries, true, lapp_data->num_libraries); + has_item = true; #ifdef PRINT_DEBUG printf("\tdatablock to seek for: %s\n", id->name); @@ -591,6 +645,11 @@ static void lib_relocate_do( } } + if (!has_item) { + /* nothing to relocate */ + return; + } + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); /* We do not want any instanciation here! */ @@ -739,10 +798,7 @@ static void lib_relocate_do( BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); /* recreate dependency graph to include new objects */ - DAG_scene_relations_rebuild(bmain, scene); - - /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ - GPU_materials_free(); + DEG_relations_tag_update(bmain); } void WM_lib_reload(Library *lib, bContext *C, ReportList *reports) @@ -762,7 +818,7 @@ void WM_lib_reload(Library *lib, bContext *C, ReportList *reports) wm_link_append_data_library_add(lapp_data, lib->filepath); - lib_relocate_do(CTX_data_main(C), CTX_data_scene(C), lib, lapp_data, reports, true); + lib_relocate_do(CTX_data_main(C), lib, lapp_data, reports, true); wm_link_append_data_free(lapp_data); @@ -779,7 +835,6 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) if (lib) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); PropertyRNA *prop; WMLinkAppendData *lapp_data; @@ -869,7 +924,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) } } - lib_relocate_do(bmain, scene, lib, lapp_data, op->reports, do_reload); + lib_relocate_do(bmain, 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 b63962ffce2..fa5fd38b7b2 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -45,15 +45,15 @@ #include "BKE_context.h" - #include "WM_api.h" #include "WM_types.h" #include "wm.h" #include "wm_subwindow.h" #include "wm_draw.h" -#include "GPU_basic_shader.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" #include "BIF_glutil.h" @@ -166,69 +166,99 @@ int wm_gesture_evaluate(wmGesture *gesture) /* ******************* gesture draw ******************* */ -static void wm_gesture_draw_rect(wmGesture *gt) +static void wm_gesture_draw_line(wmGesture *gt) { rcti *rect = (rcti *)gt->customdata; - - glEnable(GL_BLEND); - glColor4f(1.0, 1.0, 1.0, 0.05); - glBegin(GL_QUADS); - glVertex2s(rect->xmax, rect->ymin); - glVertex2s(rect->xmax, rect->ymax); - glVertex2s(rect->xmin, rect->ymax); - glVertex2s(rect->xmin, rect->ymin); - glEnd(); - glDisable(GL_BLEND); - - GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR); - glColor3ub(96, 96, 96); - GPU_basic_shader_line_stipple(1, 0xCCCC); - sdrawbox(rect->xmin, rect->ymin, rect->xmax, rect->ymax); - glColor3ub(255, 255, 255); - GPU_basic_shader_line_stipple(1, 0x3333); - sdrawbox(rect->xmin, rect->ymin, rect->xmax, rect->ymax); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + + uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("num_colors", 2); /* "advanced" mode */ + immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2); + immUniform1f("dash_width", 8.0f); + + float xmin = (float)rect->xmin; + float ymin = (float)rect->ymin; + + immBegin(GWN_PRIM_LINES, 2); + immVertex2f(shdr_pos, xmin, ymin); + immVertex2f(shdr_pos, (float)rect->xmax, (float)rect->ymax); + immEnd(); + + immUnbindProgram(); } -static void wm_gesture_draw_line(wmGesture *gt) +static void wm_gesture_draw_rect(wmGesture *gt) { rcti *rect = (rcti *)gt->customdata; - - GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE); - glColor3ub(96, 96, 96); - GPU_basic_shader_line_stipple(1, 0xAAAA); - sdrawline(rect->xmin, rect->ymin, rect->xmax, rect->ymax); - glColor3ub(255, 255, 255); - GPU_basic_shader_line_stipple(1, 0x5555); - sdrawline(rect->xmin, rect->ymin, rect->xmax, rect->ymax); - - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); - + + uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT); + + glEnable(GL_BLEND); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f); + + immRecti(shdr_pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); + + immUnbindProgram(); + + glDisable(GL_BLEND); + + shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("num_colors", 2); /* "advanced" mode */ + immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2); + immUniform1f("dash_width", 8.0f); + + imm_draw_box_wire_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, (float)rect->ymax); + + immUnbindProgram(); + + // wm_gesture_draw_line(gt); // draws a diagonal line in the lined box to test wm_gesture_draw_line } static void wm_gesture_draw_circle(wmGesture *gt) { rcti *rect = (rcti *)gt->customdata; - glTranslatef((float)rect->xmin, (float)rect->ymin, 0.0f); - glEnable(GL_BLEND); - glColor4f(1.0, 1.0, 1.0, 0.05); - glutil_draw_filled_arc(0.0, M_PI * 2.0, rect->xmax, 40); + + const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f); + imm_draw_circle_fill_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40); + + immUnbindProgram(); + glDisable(GL_BLEND); - - // for USE_GLSL works bad because of no relation between lines - GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR); - glColor3ub(96, 96, 96); - GPU_basic_shader_line_stipple(1, 0xAAAA); - glutil_draw_lined_arc(0.0, M_PI * 2.0, rect->xmax, 40); - glColor3ub(255, 255, 255); - GPU_basic_shader_line_stipple(1, 0x5555); - glutil_draw_lined_arc(0.0, M_PI * 2.0, rect->xmax, 40); - - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); - glTranslatef(-rect->xmin, -rect->ymin, 0.0f); - + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("num_colors", 2); /* "advanced" mode */ + immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2); + immUniform1f("dash_width", 4.0f); + + imm_draw_circle_wire_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40); + + immUnbindProgram(); } struct LassoFillData { @@ -251,6 +281,7 @@ static void draw_filled_lasso(wmWindow *win, wmGesture *gt) int i; rcti rect; rcti rect_win; + float red[4] = {1.0f, 0.0f, 0.0f, 0.0f}; for (i = 0; i < tot; i++, lasso += 2) { moves[i][0] = lasso[0]; @@ -276,28 +307,28 @@ static void draw_filled_lasso(wmWindow *win, wmGesture *gt) (const int (*)[2])moves, tot, draw_filled_lasso_px_cb, &lasso_fill_data); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + /* Additive Blending */ + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); - glColor4f(1, 1, 1, 1); - glPixelTransferf(GL_RED_BIAS, 1); - glPixelTransferf(GL_GREEN_BIAS, 1); - glPixelTransferf(GL_BLUE_BIAS, 1); + GLint unpack_alignment; + glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment); - GPU_basic_shader_bind(GPU_SHADER_TEXTURE_2D | GPU_SHADER_USE_COLOR); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glEnable(GL_BLEND); - glaDrawPixelsTex(rect.xmin, rect.ymin, w, h, GL_ALPHA, GL_UNSIGNED_BYTE, GL_NEAREST, pixel_buf); - glDisable(GL_BLEND); + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); + GPU_shader_bind(state.shader); + GPU_shader_uniform_vector(state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + immDrawPixelsTex(&state, rect.xmin, rect.ymin, w, h, GL_RED, GL_UNSIGNED_BYTE, GL_NEAREST, pixel_buf, 1.0f, 1.0f, NULL); - glPixelTransferf(GL_RED_BIAS, 0); - glPixelTransferf(GL_GREEN_BIAS, 0); - glPixelTransferf(GL_BLUE_BIAS, 0); + GPU_shader_unbind(); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment); MEM_freeN(pixel_buf); + + glDisable(GL_BLEND); } MEM_freeN(moves); @@ -313,29 +344,34 @@ static void wm_gesture_draw_lasso(wmWindow *win, wmGesture *gt, bool filled) draw_filled_lasso(win, gt); } - // for USE_GLSL can't check this yet - GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR); - glColor3ub(96, 96, 96); - GPU_basic_shader_line_stipple(1, 0xAAAA); - glBegin(GL_LINE_STRIP); - for (i = 0; i < gt->points; i++, lasso += 2) - glVertex2sv(lasso); - if (gt->type == WM_GESTURE_LASSO) - glVertex2sv((short *)gt->customdata); - glEnd(); - - glColor3ub(255, 255, 255); - GPU_basic_shader_line_stipple(1, 0x5555); - glBegin(GL_LINE_STRIP); - lasso = (short *)gt->customdata; - for (i = 0; i < gt->points; i++, lasso += 2) - glVertex2sv(lasso); - if (gt->type == WM_GESTURE_LASSO) - glVertex2sv((short *)gt->customdata); - glEnd(); - - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); - + const int numverts = gt->points; + + /* Nothing to draw, do early output. */ + if (numverts < 2) { + return; + } + + const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("num_colors", 2); /* "advanced" mode */ + immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2); + immUniform1f("dash_width", 2.0f); + + immBegin((gt->type == WM_GESTURE_LASSO) ? GWN_PRIM_LINE_LOOP : GWN_PRIM_LINE_STRIP, numverts); + + for (i = 0; i < gt->points; i++, lasso += 2) { + immVertex2f(shdr_pos, (float)lasso[0], (float)lasso[1]); + } + + immEnd(); + + immUnbindProgram(); } static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt) @@ -344,25 +380,49 @@ static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt) const int winsize_x = WM_window_pixels_x(win); const int winsize_y = WM_window_pixels_y(win); - GPU_basic_shader_bind(GPU_SHADER_LINE | GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR); - glColor3ub(96, 96, 96); - GPU_basic_shader_line_stipple(1, 0xCCCC); - sdrawline(rect->xmin - winsize_x, rect->ymin, rect->xmin + winsize_x, rect->ymin); - sdrawline(rect->xmin, rect->ymin - winsize_y, rect->xmin, rect->ymin + winsize_y); - - glColor3ub(255, 255, 255); - GPU_basic_shader_line_stipple(1, 0x3333); - sdrawline(rect->xmin - winsize_x, rect->ymin, rect->xmin + winsize_x, rect->ymin); - sdrawline(rect->xmin, rect->ymin - winsize_y, rect->xmin, rect->ymin + winsize_y); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + float x1, x2, y1, y2; + + const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("num_colors", 2); /* "advanced" mode */ + immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2); + immUniform1f("dash_width", 8.0f); + + immBegin(GWN_PRIM_LINES, 4); + + x1 = (float)(rect->xmin - winsize_x); + y1 = (float)rect->ymin; + x2 = (float)(rect->xmin + winsize_x); + y2 = y1; + + immVertex2f(shdr_pos, x1, y1); + immVertex2f(shdr_pos, x2, y2); + + x1 = (float)rect->xmin; + y1 = (float)(rect->ymin - winsize_y); + x2 = x1; + y2 = (float)(rect->ymin + winsize_y); + + immVertex2f(shdr_pos, x1, y1); + immVertex2f(shdr_pos, x2, y2); + + immEnd(); + + immUnbindProgram(); } /* called in wm_draw.c */ void wm_gesture_draw(wmWindow *win) { wmGesture *gt = (wmGesture *)win->gesture.first; - - GPU_basic_shader_line_width(1); + + glLineWidth(1.0f); for (; gt; gt = gt->next) { /* all in subwindow space */ wmSubWindowSet(win, gt->swinid); diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index e73ec2b081a..8346535cd6a 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -119,10 +119,11 @@ #include "GPU_draw.h" #include "GPU_init_exit.h" -#include "BKE_depsgraph.h" #include "BKE_sound.h" #include "COM_compositor.h" +#include "DEG_depsgraph.h" + #ifdef WITH_OPENSUBDIV # include "BKE_subsurf.h" #endif @@ -164,17 +165,21 @@ void WM_init(bContext *C, int argc, const char **argv) wm_operatortype_init(); WM_menutype_init(); WM_uilisttype_init(); + wm_manipulatortype_init(); + wm_manipulatorgrouptype_init(); BKE_undo_callback_wm_kill_jobs_set(wm_undo_kill_callback); 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 */ + BKE_region_callback_free_manipulatormap_set(wm_manipulatormap_remove); /* screen.c */ + BKE_region_callback_refresh_tag_manipulatormap_set(WM_manipulatormap_tag_refresh); BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference); /* library.c */ BKE_blender_callback_test_break_set(wm_window_testbreak); /* blender.c */ BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); /* screen.c */ - DAG_editors_update_cb(ED_render_id_flush_update, - ED_render_scene_update, - ED_render_scene_update_pre); /* depsgraph.c */ + DEG_editors_set_update_cb(ED_render_id_flush_update, + ED_render_scene_update, + ED_render_scene_update_pre); ED_spacetypes_init(); /* editors/space_api/spacetype.c */ @@ -473,7 +478,7 @@ void WM_exit_ext(bContext *C, const bool do_python) CTX_wm_window_set(C, win); /* needed by operator close callbacks */ WM_event_remove_handlers(C, &win->handlers); WM_event_remove_handlers(C, &win->modalhandlers); - ED_screen_exit(C, win, win->screen); + ED_screen_exit(C, win, WM_window_get_active_screen(win)); } } @@ -524,6 +529,11 @@ void WM_exit_ext(bContext *C, const bool do_python) ED_gpencil_strokes_copybuf_free(); BKE_node_clipboard_clear(); + /* free manipulator-maps after freeing blender, so no deleted data get accessed during cleaning up of areas */ + wm_manipulatormaptypes_free(); + wm_manipulatorgrouptype_free(); + wm_manipulatortype_free(); + BLF_exit(); #ifdef WITH_INTERNATIONAL diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index fbd6d89e7bc..ee2d9553c60 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -52,6 +52,7 @@ #include "DNA_scene_types.h" #include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "BLT_translation.h" @@ -70,7 +71,6 @@ #include "BKE_blender_version.h" #include "BKE_brush.h" #include "BKE_context.h" -#include "BKE_depsgraph.h" #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_image.h" @@ -86,9 +86,12 @@ #include "BKE_idcode.h" -#include "BIF_glutil.h" /* for paint cursor */ #include "BLF_api.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" + #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -97,8 +100,6 @@ #include "ED_util.h" #include "ED_view3d.h" -#include "GPU_basic_shader.h" - #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -157,18 +158,20 @@ void WM_operatortype_iter(GHashIterator *ghi) BLI_ghashIterator_init(ghi, global_ops_hash); } -/* all ops in 1 list (for time being... needs evaluation later) */ -void WM_operatortype_append(void (*opfunc)(wmOperatorType *)) +/** \name Operator Type Append + * \{ */ + +static wmOperatorType *wm_operatortype_append__begin(void) { - wmOperatorType *ot; - - ot = MEM_callocN(sizeof(wmOperatorType), "operatortype"); + wmOperatorType *ot = MEM_callocN(sizeof(wmOperatorType), "operatortype"); ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties); /* Set the default i18n context now, so that opfunc can redefine it if needed! */ RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; - opfunc(ot); - + return ot; +} +static void wm_operatortype_append__end(wmOperatorType *ot) +{ if (ot->name == NULL) { fprintf(stderr, "ERROR: Operator %s has no name property!\n", ot->idname); ot->name = N_("Dummy Name"); @@ -181,22 +184,23 @@ void WM_operatortype_append(void (*opfunc)(wmOperatorType *)) BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot); } -void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata) +/* all ops in 1 list (for time being... needs evaluation later) */ +void WM_operatortype_append(void (*opfunc)(wmOperatorType *)) { - wmOperatorType *ot; + wmOperatorType *ot = wm_operatortype_append__begin(); + opfunc(ot); + wm_operatortype_append__end(ot); +} - ot = MEM_callocN(sizeof(wmOperatorType), "operatortype"); - ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties); - /* Set the default i18n context now, so that opfunc can redefine it if needed! */ - RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); - ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; +void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata) +{ + wmOperatorType *ot = wm_operatortype_append__begin(); opfunc(ot, userdata); - RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP); - RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname); - - BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot); + wm_operatortype_append__end(ot); } +/** \} */ + /* ********************* macro operator ******************** */ typedef struct { @@ -1164,46 +1168,70 @@ int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_REGION_WIN); } +struct EnumSearchMenu { + wmOperator *op; /* the operator that will be executed when selecting an item */ + + bool use_previews; + short prv_cols, prv_rows; +}; /* generic enum search invoke popup */ -static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg_op) +static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg) { - static char search[256] = ""; - wmEvent event; + struct EnumSearchMenu *search_menu = arg; wmWindow *win = CTX_wm_window(C); + wmOperator *op = search_menu->op; + /* template_ID uses 4 * widget_unit for width, we use a bit more, some items may have a suffix to show */ + const int width = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_cols : UI_searchbox_size_x(); + const int height = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_rows : UI_searchbox_size_y(); + static char search[256] = ""; uiBlock *block; uiBut *but; - wmOperator *op = (wmOperator *)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'; + BLI_assert(search_menu->use_previews || (search_menu->prv_cols == 0 && search_menu->prv_rows == 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 but = uiDefSearchButO_ptr(block, op->type, op->ptr->data, search, 0, ICON_VIEWZOOM, sizeof(search), - 10, 10, UI_searchbox_size_x(), UI_UNIT_Y, 0, 0, ""); + 10, 10, width, UI_UNIT_Y, search_menu->prv_rows, search_menu->prv_cols, ""); /* fake button, it holds space for search items */ - uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 10 - UI_searchbox_size_y(), UI_searchbox_size_x(), UI_searchbox_size_y(), NULL, 0, 0, 0, 0, NULL); + uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 10 - UI_searchbox_size_y(), width, height, NULL, 0, 0, 0, 0, NULL); UI_block_bounds_set_popup(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */ - - wm_event_init_from_window(win, &event); - event.type = EVT_BUT_OPEN; - event.val = KM_PRESS; - event.customdata = but; - event.customdatafree = false; - wm_event_add(win, &event); + UI_but_focus_on_enter_event(win, but); return block; } +/** + * Similar to #WM_enum_search_invoke, but draws previews. Also, this can't + * be used as invoke callback directly since it needs additional info. + */ +int WM_enum_search_invoke_previews( + bContext *C, wmOperator *op, short prv_cols, short prv_rows) +{ + static struct EnumSearchMenu search_menu; + + search_menu.op = op; + search_menu.use_previews = true; + search_menu.prv_cols = prv_cols; + search_menu.prv_rows = prv_rows; + + UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu); + + return OPERATOR_INTERFACE; +} int WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - UI_popup_block_invoke(C, wm_enum_search_menu, op); + static struct EnumSearchMenu search_menu; + search_menu.op = op; + UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu); return OPERATOR_INTERFACE; } @@ -1716,6 +1744,67 @@ static void WM_OT_operator_defaults(wmOperatorType *ot) ot->flag = OPTYPE_INTERNAL; } +#ifdef USE_WORKSPACE_TOOL +/* ***************** Set Active Tool ************************* */ + +/* Developers note: in it's current form this doesn't need to be an operator, + * keep this as-is for now since it may end up setting an active key-map. + */ + +static int wm_operator_tool_set_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + WorkSpace *workspace = CTX_wm_workspace(C); + ScrArea *sa = CTX_wm_area(C); + char id_keymap[sizeof(workspace->tool.keymap)]; + char id_manipulator_group[sizeof(workspace->tool.manipulator_group)]; + RNA_string_get(op->ptr, "keymap", id_keymap); + RNA_string_get(op->ptr, "manipulator_group", id_manipulator_group); + int index = RNA_int_get(op->ptr, "index"); + + workspace->tool.index = index; + + if (workspace->tool.manipulator_group[0]) { + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(workspace->tool.manipulator_group, false); + if (wgt != NULL) { + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params); + WM_manipulatormaptype_group_unlink(C, bmain, mmap_type, wgt); + } + } + + /* NOTE: we may want to move this logic into a function. */ + { + BLI_strncpy(workspace->tool.keymap, id_keymap, sizeof(workspace->tool.keymap)); + BLI_strncpy(workspace->tool.manipulator_group, id_manipulator_group, sizeof(workspace->tool.manipulator_group)); + workspace->tool.spacetype = sa->spacetype; + } + + if (workspace->tool.manipulator_group[0]) { + WM_manipulator_group_type_ensure(workspace->tool.manipulator_group); + } + + /* For some reason redraw fails with menus (even though 'ar' isn't the menu's region). */ + ED_area_tag_redraw(sa); + + return OPERATOR_FINISHED; +} + +static void WM_OT_tool_set(wmOperatorType *ot) +{ + ot->name = "Set Active Tool"; + ot->idname = "WM_OT_tool_set"; + ot->description = "Set the active tool"; + + ot->exec = wm_operator_tool_set_exec; + + ot->flag = OPTYPE_INTERNAL; + + RNA_def_string(ot->srna, "keymap", NULL, KMAP_MAX_NAME, "Key Map", ""); + RNA_def_string(ot->srna, "manipulator_group", NULL, MAX_NAME, "Manipulator Group", ""); + RNA_def_int(ot->srna, "index", 0, INT_MIN, INT_MAX, "Index", "", INT_MIN, INT_MAX); +} +#endif /* USE_WORKSPACE_TOOL */ + /* ***************** Splash Screen ************************* */ static void wm_block_splash_close(bContext *C, void *arg_block, void *UNUSED(arg)) @@ -1859,8 +1948,8 @@ 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 * U.pixelsize - (2 * UI_DPI_FAC); - int y = 242 * U.pixelsize + (4 * UI_DPI_FAC); + int x = 236 * U.pixelsize - (2 * UI_DPI_FAC); + int y = 231 * U.pixelsize + (4 * UI_DPI_FAC); int w = 240 * U.pixelsize; /* hack to have text draw 'text_sel' */ @@ -1873,17 +1962,32 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar #ifdef WITH_BUILDINFO if (build_commit_timestamp != 0) { - uiDefBut(block, UI_BTYPE_LABEL, 0, date_buf, U.pixelsize * 494 - date_width, U.pixelsize * 270, date_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + but = uiDefBut( + block, UI_BTYPE_LABEL, 0, date_buf, + U.pixelsize * 502 - date_width, U.pixelsize * 267, + date_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + /* XXX, set internal flag - UI_SELECT */ + UI_but_flag_enable(but, 1); label_delta = 12; } - uiDefBut(block, UI_BTYPE_LABEL, 0, hash_buf, U.pixelsize * 494 - hash_width, U.pixelsize * (270 - label_delta), hash_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + but = uiDefBut( + block, UI_BTYPE_LABEL, 0, hash_buf, + U.pixelsize * 502 - hash_width, U.pixelsize * (267 - label_delta), + hash_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + /* XXX, set internal flag - UI_SELECT */ + UI_but_flag_enable(but, 1); if (!STREQ(build_branch, "master")) { char branch_buf[128] = "\0"; int branch_width; BLI_snprintf(branch_buf, sizeof(branch_buf), "Branch: %s", build_branch); branch_width = (int)BLF_width(style->widgetlabel.uifont_id, branch_buf, sizeof(branch_buf)) + U.widget_unit; - uiDefBut(block, UI_BTYPE_LABEL, 0, branch_buf, U.pixelsize * 494 - branch_width, U.pixelsize * (258 - label_delta), branch_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + but = uiDefBut( + block, UI_BTYPE_LABEL, 0, branch_buf, + U.pixelsize * 502 - branch_width, U.pixelsize * (255 - label_delta), + branch_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL); + /* XXX, set internal flag - UI_SELECT */ + UI_but_flag_enable(but, 1); } #endif /* WITH_BUILDINFO */ @@ -2122,8 +2226,11 @@ static void WM_OT_call_menu_pie(wmOperatorType *ot) static int wm_operator_winactive_normal(bContext *C) { wmWindow *win = CTX_wm_window(C); + bScreen *screen; - if (win == NULL || win->screen == NULL || win->screen->state != SCREENNORMAL) + if (win == NULL) + return 0; + if (!((screen = WM_window_get_active_screen(win)) && (screen->state == SCREENNORMAL))) return 0; return 1; @@ -2140,14 +2247,22 @@ static void WM_OT_window_close(wmOperatorType *ot) ot->poll = WM_operator_winactive; } -static void WM_OT_window_duplicate(wmOperatorType *ot) +static void WM_OT_window_new(wmOperatorType *ot) { - ot->name = "Duplicate Window"; - ot->idname = "WM_OT_window_duplicate"; - ot->description = "Duplicate the current Blender window"; - - ot->exec = wm_window_duplicate_exec; + PropertyRNA *prop; + + ot->name = "New Window"; + ot->idname = "WM_OT_window_new"; + ot->description = "Create a new Blender window"; + + ot->exec = wm_window_new_exec; + ot->invoke = wm_window_new_invoke; ot->poll = wm_operator_winactive_normal; + + prop = RNA_def_enum(ot->srna, "screen", DummyRNA_NULL_items, 0, "Screen", ""); + RNA_def_enum_funcs(prop, wm_window_new_screen_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + ot->prop = prop; } static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot) @@ -2351,8 +2466,8 @@ static void radial_control_set_tex(RadialControl *rc) if ((ibuf = BKE_brush_gen_radial_control_imbuf(rc->image_id_ptr.data, rc->use_secondary_tex))) { glGenTextures(1, &rc->gltex); glBindTexture(GL_TEXTURE_2D, rc->gltex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, ibuf->x, ibuf->y, 0, - GL_ALPHA, GL_FLOAT, ibuf->rect_float); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, ibuf->x, ibuf->y, 0, + GL_RED, GL_FLOAT, ibuf->rect_float); MEM_freeN(ibuf->rect_float); MEM_freeN(ibuf); } @@ -2385,43 +2500,63 @@ static void radial_control_paint_tex(RadialControl *rc, float radius, float alph RNA_property_float_get_array(fill_ptr, fill_prop, col); } - glColor4f(col[0], col[1], col[2], alpha); + + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); if (rc->gltex) { + + unsigned int texCoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + glBindTexture(GL_TEXTURE_2D, rc->gltex); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + GLint swizzleMask[] = {GL_ZERO, GL_ZERO, GL_ZERO, GL_RED}; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + + immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR); + + immUniformColor3fvAlpha(col, alpha); + immUniform1i("image", GL_TEXTURE0); /* set up rotation if available */ if (rc->rot_prop) { rot = RNA_property_float_get(&rc->rot_ptr, rc->rot_prop); - glPushMatrix(); - glRotatef(RAD2DEGF(rot), 0, 0, 1); + gpuPushMatrix(); + gpuRotate2D(RAD2DEGF(rot)); } /* draw textured quad */ - GPU_basic_shader_bind(GPU_SHADER_TEXTURE_2D | GPU_SHADER_USE_COLOR); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex2f(-radius, -radius); - glTexCoord2f(1, 0); - glVertex2f(radius, -radius); - glTexCoord2f(1, 1); - glVertex2f(radius, radius); - glTexCoord2f(0, 1); - glVertex2f(-radius, radius); - glEnd(); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + immBegin(GWN_PRIM_TRI_FAN, 4); + + immAttrib2f(texCoord, 0, 0); + immVertex2f(pos, -radius, -radius); + + immAttrib2f(texCoord, 1, 0); + immVertex2f(pos, radius, -radius); + + immAttrib2f(texCoord, 1, 1); + immVertex2f(pos, radius, radius); + + immAttrib2f(texCoord, 0, 1); + immVertex2f(pos, -radius, radius); + + immEnd(); /* undo rotation */ if (rc->rot_prop) - glPopMatrix(); + gpuPopMatrix(); } else { /* flat color if no texture available */ - glutil_draw_filled_arc(0, M_PI * 2, radius, 40); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fvAlpha(col, alpha); + imm_draw_circle_fill_2d(pos, 0.0f, 0.0f, radius, 40); } + + immUnbindProgram(); } static void radial_control_paint_cursor(bContext *C, int x, int y, void *customdata) @@ -2480,7 +2615,7 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd /* Keep cursor in the original place */ x = rc->initial_mouse[0] - ar->winrct.xmin; y = rc->initial_mouse[1] - ar->winrct.ymin; - glTranslatef((float)x, (float)y, 0.0f); + gpuTranslate2f((float)x, (float)y); glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); @@ -2488,7 +2623,7 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd /* apply zoom if available */ if (rc->zoom_prop) { RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom); - glScalef(zoom[0], zoom[1], 1); + gpuScale2fv(zoom); } /* draw rotated texture */ @@ -2497,24 +2632,39 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd /* set line color */ if (rc->col_prop) RNA_property_float_get_array(&rc->col_ptr, rc->col_prop, col); - glColor4f(col[0], col[1], col[2], 0.5); + + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fvAlpha(col, 0.5f); if (rc->subtype == PROP_ANGLE) { - glPushMatrix(); + gpuPushMatrix(); + /* draw original angle line */ - glRotatef(RAD2DEGF(rc->initial_value), 0, 0, 1); - fdrawline((float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f); + gpuRotate2D(RAD2DEGF(rc->initial_value)); + immBegin(GWN_PRIM_LINES, 2); + immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f); + immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f); + immEnd(); + /* draw new angle line */ - glRotatef(RAD2DEGF(rc->current_value - rc->initial_value), 0, 0, 1); - fdrawline((float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f); - glPopMatrix(); + gpuRotate2D(RAD2DEGF(rc->current_value - rc->initial_value)); + immBegin(GWN_PRIM_LINES, 2); + immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f); + immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f); + immEnd(); + + gpuPopMatrix(); } /* draw circles on top */ - glutil_draw_lined_arc(0.0, (float)(M_PI * 2.0), r1, 40); - glutil_draw_lined_arc(0.0, (float)(M_PI * 2.0), r2, 40); + imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r1, 40); + imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r2, 40); if (rmin > 0.0f) - glutil_draw_lined_arc(0.0, (float)(M_PI * 2.0), rmin, 40); + imm_draw_circle_wire_2d(pos, 0.0, 0.0f, rmin, 40); + immUnbindProgram(); BLF_size(fontid, 1.5 * fstyle_points * U.pixelsize, U.dpi); BLF_enable(fontid, BLF_SHADOW); @@ -2530,6 +2680,7 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd glDisable(GL_BLEND); glDisable(GL_LINE_SMOOTH); + } typedef enum { @@ -3089,7 +3240,7 @@ static const EnumPropertyItem redraw_timer_type_items[] = { static void redraw_timer_step( - bContext *C, Main *bmain, Scene *scene, + bContext *C, Main *bmain, Scene *scene, struct Depsgraph *depsgraph, wmWindow *win, ScrArea *sa, ARegion *ar, const int type, const int cfra) { @@ -3108,11 +3259,12 @@ static void redraw_timer_step( CTX_wm_window_set(C, win); /* XXX context manipulation warning! */ } else if (type == eRTDrawWindow) { + bScreen *screen = WM_window_get_active_screen(win); ScrArea *sa_iter; CTX_wm_menu_set(C, NULL); - for (sa_iter = win->screen->areabase.first; sa_iter; sa_iter = sa_iter->next) { + for (sa_iter = screen->areabase.first; sa_iter; sa_iter = sa_iter->next) { ARegion *ar_iter; CTX_wm_area_set(C, sa_iter); @@ -3135,7 +3287,7 @@ static void redraw_timer_step( } else if (type == eRTAnimationStep) { scene->r.cfra += (cfra == scene->r.cfra) ? 1 : -1; - BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, scene->lay); + BKE_scene_graph_update_for_newframe(bmain->eval_ctx, depsgraph, bmain, scene); } else if (type == eRTAnimationPlay) { /* play anim, return on same frame as started with */ @@ -3147,7 +3299,7 @@ static void redraw_timer_step( if (scene->r.cfra > scene->r.efra) scene->r.cfra = scene->r.sfra; - BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, scene->lay); + BKE_scene_graph_update_for_newframe(bmain->eval_ctx, depsgraph, bmain, scene); redraw_timer_window_swap(C); } } @@ -3171,13 +3323,14 @@ static int redraw_timer_exec(bContext *C, wmOperator *op) const int cfra = scene->r.cfra; int a, iter_steps = 0; const char *infostr = ""; + struct Depsgraph *depsgraph = CTX_data_depsgraph(C); WM_cursor_wait(1); time_start = PIL_check_seconds_timer(); for (a = 0; a < iter; a++) { - redraw_timer_step(C, bmain, scene, win, sa, ar, type, cfra); + redraw_timer_step(C, bmain, scene, depsgraph, win, sa, ar, type, cfra); iter_steps += 1; if (time_limit != 0.0) { @@ -3235,28 +3388,6 @@ static void WM_OT_memory_statistics(wmOperatorType *ot) ot->exec = memory_statistics_exec; } -/* ************************** memory statistics for testing ***************** */ - -static int dependency_relations_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - - DAG_print_dependencies(bmain, scene, ob); - - return OPERATOR_FINISHED; -} - -static void WM_OT_dependency_relations(wmOperatorType *ot) -{ - ot->name = "Dependency Relations"; - ot->idname = "WM_OT_dependency_relations"; - ot->description = "Print dependency graph relations to the console"; - - ot->exec = dependency_relations_exec; -} - /* *************************** Mat/tex/etc. previews generation ************* */ typedef struct PreviewsIDEnsureData { @@ -3503,12 +3634,13 @@ void wm_operatortype_init(void) global_ops_hash = BLI_ghash_str_new_ex("wm_operatortype_init gh", 2048); WM_operatortype_append(WM_OT_window_close); - WM_operatortype_append(WM_OT_window_duplicate); + WM_operatortype_append(WM_OT_window_new); WM_operatortype_append(WM_OT_read_history); WM_operatortype_append(WM_OT_read_homefile); WM_operatortype_append(WM_OT_read_factory_settings); WM_operatortype_append(WM_OT_save_homefile); WM_operatortype_append(WM_OT_save_userpref); + WM_operatortype_append(WM_OT_save_workspace_file); WM_operatortype_append(WM_OT_userpref_autoexec_path_add); WM_operatortype_append(WM_OT_userpref_autoexec_path_remove); WM_operatortype_append(WM_OT_window_fullscreen_toggle); @@ -3525,9 +3657,11 @@ void wm_operatortype_init(void) WM_operatortype_append(WM_OT_save_mainfile); WM_operatortype_append(WM_OT_redraw_timer); WM_operatortype_append(WM_OT_memory_statistics); - WM_operatortype_append(WM_OT_dependency_relations); WM_operatortype_append(WM_OT_debug_menu); WM_operatortype_append(WM_OT_operator_defaults); +#ifdef USE_WORKSPACE_TOOL + WM_operatortype_append(WM_OT_tool_set); +#endif WM_operatortype_append(WM_OT_splash); WM_operatortype_append(WM_OT_search_menu); WM_operatortype_append(WM_OT_call_menu); @@ -3540,6 +3674,10 @@ void wm_operatortype_init(void) WM_operatortype_append(WM_OT_previews_ensure); WM_operatortype_append(WM_OT_previews_clear); WM_operatortype_append(WM_OT_doc_view_manual_ui_context); + + /* manipulators */ + WM_operatortype_append(MANIPULATORGROUP_OT_manipulator_select); + WM_operatortype_append(MANIPULATORGROUP_OT_manipulator_tweak); } /* circleselect-like modal operators */ @@ -3742,7 +3880,7 @@ void wm_window_keymap(wmKeyConfig *keyconf) wmKeyMapItem *kmi; /* note, this doesn't replace existing keymap items */ - WM_keymap_verify_item(keymap, "WM_OT_window_duplicate", WKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); + WM_keymap_verify_item(keymap, "WM_OT_window_new", WKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); #ifdef __APPLE__ WM_keymap_add_item(keymap, "WM_OT_read_homefile", NKEY, KM_PRESS, KM_OSKEY, 0); WM_keymap_add_menu(keymap, "INFO_MT_file_open_recent", OKEY, KM_PRESS, KM_SHIFT | KM_OSKEY, 0); @@ -3846,6 +3984,7 @@ void wm_window_keymap(wmKeyConfig *keyconf) RNA_float_set(kmi->ptr, "value", 1.0f / 1.5f); #endif /* WITH_INPUT_NDOF */ + wm_manipulators_keymap(keyconf); gesture_circle_modal_keymap(keyconf); gesture_border_modal_keymap(keyconf); gesture_zoom_border_modal_keymap(keyconf); diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 77378cf8e0c..fcb471530e0 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -58,24 +58,29 @@ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" -#include "BKE_depsgraph.h" #include "BKE_image.h" #include "BIF_gl.h" #include "BIF_glutil.h" +#include "GPU_matrix.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" + #include "DNA_scene_types.h" #include "ED_datafiles.h" /* for fonts */ #include "GHOST_C-api.h" #include "BLF_api.h" +#include "DEG_depsgraph.h" + #include "WM_api.h" /* only for WM_main_playanim */ #ifdef WITH_AUDASPACE -# include AUD_DEVICE_H -# include AUD_HANDLE_H -# include AUD_SOUND_H -# include AUD_SPECIAL_H +# include <AUD_Device.h> +# include <AUD_Handle.h> +# include <AUD_Sound.h> +# include <AUD_Special.h> static AUD_Sound *source = NULL; static AUD_Handle *playback_handle = NULL; @@ -190,10 +195,7 @@ static void playanim_window_get_size(int *r_width, int *r_height) static void playanim_gl_matrix(void) { /* unified matrix, note it affects offset for drawing */ - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); - glMatrixMode(GL_MODELVIEW); + gpuOrtho2D(0.0f, 1.0f, 0.0f, 1.0f); } /* implementation */ @@ -308,42 +310,45 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf CLAMP(offs_x, 0.0f, 1.0f); CLAMP(offs_y, 0.0f, 1.0f); - glRasterPos2f(offs_x, offs_y); glClearColor(0.1, 0.1, 0.1, 0.0); glClear(GL_COLOR_BUFFER_BIT); - + /* checkerboard for case alpha */ if (ibuf->planes == 32) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - fdrawcheckerboard(offs_x, offs_y, offs_x + span_x, offs_y + span_y); + imm_draw_box_checker_2d(offs_x, offs_y, offs_x + span_x, offs_y + span_y); } - glRasterPos2f(offs_x + (ps->draw_flip[0] ? span_x : 0.0f), - offs_y + (ps->draw_flip[1] ? span_y : 0.0f)); - - glPixelZoom(ps->zoom * (ps->draw_flip[0] ? -1.0f : 1.0f), - ps->zoom * (ps->draw_flip[1] ? -1.0f : 1.0f)); + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - glDrawPixels(ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + immDrawPixelsTex( + &state, + offs_x + (ps->draw_flip[0] ? span_x : 0.0f), + offs_y + (ps->draw_flip[1] ? span_y : 0.0f), + ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, + ibuf->rect, + ((ps->draw_flip[0] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_x), + ((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y), + NULL); glDisable(GL_BLEND); - + pupdate_time(); if (picture && (g_WS.qual & (WS_QUAL_SHIFT | WS_QUAL_LMOUSE)) && (fontid != -1)) { int sizex, sizey; float fsizex_inv, fsizey_inv; char str[32 + FILE_MAX]; - cpack(-1); BLI_snprintf(str, sizeof(str), "%s | %.2f frames/s", picture->name, fstep / swaptime); playanim_window_get_size(&sizex, &sizey); fsizex_inv = 1.0f / sizex; fsizey_inv = 1.0f / sizey; + BLF_color4f(fontid, 1.0, 1.0, 1.0, 1.0); BLF_enable(fontid, BLF_ASPECT); BLF_aspect(fontid, fsizex_inv, fsizey_inv, 1.0f); BLF_position(fontid, 10.0f * fsizex_inv, 10.0f * fsizey_inv, 0.0f); @@ -354,24 +359,25 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf float fac = ps->picture->frame / (double)(((PlayAnimPict *)picsbase.last)->frame - ((PlayAnimPict *)picsbase.first)->frame); fac = 2.0f * fac - 1.0f; - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glColor4f(0.0f, 1.0f, 0.0f, 1.0f); - - glBegin(GL_LINES); - glVertex2f(fac, -1.0f); - glVertex2f(fac, 1.0f); - glEnd(); - - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); + gpuPushProjectionMatrix(); + gpuLoadIdentityProjectionMatrix(); + gpuPushMatrix(); + gpuLoadIdentity(); + + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3ub(0, 255, 0); + + immBegin(GWN_PRIM_LINES, 2); + immVertex2f(pos, fac, -1.0f); + immVertex2f(pos, fac, 1.0f); + immEnd(); + + immUnbindProgram(); + + gpuPopMatrix(); + gpuPopProjectionMatrix(); } GHOST_SwapWindowBuffers(g_WS.ghost_window); @@ -1255,6 +1261,9 @@ static char *wm_main_playanim_intern(int argc, const char **argv) //GHOST_ActivateWindowDrawingContext(g_WS.ghost_window); + /* initialize OpenGL immediate mode */ + immInit(); + /* initialize the font */ BLF_init(); ps.fontid = BLF_load_mem("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size); @@ -1524,7 +1533,11 @@ static char *wm_main_playanim_intern(int argc, const char **argv) #endif /* we still miss freeing a lot!, * but many areas could skip initialization too for anim play */ - + + GPU_shader_free_builtin_shaders(); + + immDestroy(); + BLF_exit(); GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window); @@ -1537,7 +1550,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv) IMB_exit(); BKE_images_exit(); - DAG_exit(); + DEG_free_node_types(); totblock = MEM_get_memory_blocks_in_use(); if (totblock != 0) { diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c index 66ebf18c9e1..c0800705b28 100644 --- a/source/blender/windowmanager/intern/wm_stereo.c +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -52,8 +52,7 @@ #include "ED_screen.h" -#include "GPU_glew.h" -#include "GPU_basic_shader.h" +#include "GPU_immediate.h" #include "WM_api.h" #include "WM_types.h" @@ -77,54 +76,82 @@ static void wm_method_draw_stereo3d_pageflip(wmWindow *win) else //STEREO_RIGHT_ID glDrawBuffer(GL_BACK_RIGHT); - wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); + wm_triple_draw_textures(win, drawdata->triple, 1.0f); } glDrawBuffer(GL_BACK); } -static enum eStereo3dInterlaceType interlace_prev_type = -1; -static char interlace_prev_swap = -1; +static GPUInterlaceShader interlace_gpu_id_from_type(eStereo3dInterlaceType interlace_type) +{ + switch (interlace_type) { + case S3D_INTERLACE_ROW: + return GPU_SHADER_INTERLACE_ROW; + case S3D_INTERLACE_COLUMN: + return GPU_SHADER_INTERLACE_COLUMN; + case S3D_INTERLACE_CHECKERBOARD: + default: + return GPU_SHADER_INTERLACE_CHECKER; + } +} static void wm_method_draw_stereo3d_interlace(wmWindow *win) { - wmDrawData *drawdata; - int view; - bool flag; bool swap = (win->stereo3d_format->flag & S3D_INTERLACE_SWAP) != 0; enum eStereo3dInterlaceType interlace_type = win->stereo3d_format->interlace_type; - for (view = 0; view < 2; view ++) { - flag = swap ? !view : view; - drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); - GPU_basic_shader_bind(GPU_SHADER_STIPPLE); - switch (interlace_type) { - case S3D_INTERLACE_ROW: - if (flag) - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_ROW_SWAP); - else - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_ROW); - break; - case S3D_INTERLACE_COLUMN: - if (flag) - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_COLUMN_SWAP); - else - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_COLUMN); - break; - case S3D_INTERLACE_CHECKERBOARD: - default: - if (flag) - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_CHECKER_SWAP); - else - GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_CHECKER); - break; - } + wmDrawData *drawdata[2]; + for (int eye = 0; eye < 2; eye++) { + int view = swap ? !eye : eye; + drawdata[eye] = BLI_findlink(&win->drawdata, (view * 2) + 1); + } + + const int sizex = WM_window_pixels_x(win); + const int sizey = WM_window_pixels_y(win); + + /* wmOrtho for the screen has this same offset */ + float ratiox = sizex; + float ratioy = sizey; + float halfx = GLA_PIXEL_OFS; + float halfy = GLA_PIXEL_OFS; + + Gwn_VertFormat *format = immVertexFormat(); + unsigned int texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_INTERLACE); + + /* leave GL_TEXTURE0 as the latest bind texture */ + for (int eye = 1; eye >= 0; eye--) { + glActiveTexture(GL_TEXTURE0 + eye); + glBindTexture(drawdata[eye]->triple->target, drawdata[eye]->triple->bind); + } - wm_triple_draw_textures(win, drawdata->triple, 1.0f, true); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + immUniform1i("image_a", 0); + immUniform1i("image_b", 1); + + immUniform1i("interlace_id", interlace_gpu_id_from_type(interlace_type)); + + immBegin(GWN_PRIM_TRI_FAN, 4); + + immAttrib2f(texcoord, halfx, halfy); + immVertex2f(pos, 0.0f, 0.0f); + + immAttrib2f(texcoord, ratiox + halfx, halfy); + immVertex2f(pos, sizex, 0.0f); + + immAttrib2f(texcoord, ratiox + halfx, ratioy + halfy); + immVertex2f(pos, sizex, sizey); + + immAttrib2f(texcoord, halfx, ratioy + halfy); + immVertex2f(pos, 0.0f, sizey); + + immEnd(); + immUnbindProgram(); + + for (int eye = 0; eye < 2; eye++) { + glBindTexture(drawdata[eye]->triple->target, 0); } - interlace_prev_type = interlace_type; - interlace_prev_swap = swap; } static void wm_method_draw_stereo3d_anaglyph(wmWindow *win) @@ -157,7 +184,7 @@ static void wm_method_draw_stereo3d_anaglyph(wmWindow *win) break; } - wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); + wm_triple_draw_textures(win, drawdata->triple, 1.0f); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } @@ -172,6 +199,10 @@ static void wm_method_draw_stereo3d_sidebyside(wmWindow *win) int soffx; bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0; + Gwn_VertFormat *format = immVertexFormat(); + unsigned int texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + for (view = 0; view < 2; view ++) { drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); triple = drawdata->triple; @@ -203,26 +234,33 @@ static void wm_method_draw_stereo3d_sidebyside(wmWindow *win) halfy /= triple->y; } - glEnable(triple->target); + /* TODO: if target is always same for both eyes, bind program & set uniform before loop */ + immBindBuiltinProgram((triple->target == GL_TEXTURE_2D) ? GPU_SHADER_3D_IMAGE_MODULATE_ALPHA : GPU_SHADER_3D_IMAGE_RECT_MODULATE_ALPHA); + glBindTexture(triple->target, triple->bind); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - glTexCoord2f(halfx, halfy); - glVertex2f(soffx, 0); + immUniform1f("alpha", 1.0f); + immUniform1i("image", 0); /* default GL_TEXTURE0 unit */ - glTexCoord2f(ratiox + halfx, halfy); - glVertex2f(soffx + (sizex * 0.5f), 0); + immBegin(GWN_PRIM_TRI_FAN, 4); - glTexCoord2f(ratiox + halfx, ratioy + halfy); - glVertex2f(soffx + (sizex * 0.5f), sizey); + immAttrib2f(texcoord, halfx, halfy); + immVertex2f(pos, soffx, 0.0f); - glTexCoord2f(halfx, ratioy + halfy); - glVertex2f(soffx, sizey); - glEnd(); + immAttrib2f(texcoord, ratiox + halfx, halfy); + immVertex2f(pos, soffx + (sizex * 0.5f), 0.0f); + immAttrib2f(texcoord, ratiox + halfx, ratioy + halfy); + immVertex2f(pos, soffx + (sizex * 0.5f), sizey); + + immAttrib2f(texcoord, halfx, ratioy + halfy); + immVertex2f(pos, soffx, sizey); + + immEnd(); + + /* TODO: if target is always same for both eyes, unbind program & texture target after loop */ glBindTexture(triple->target, 0); - glDisable(triple->target); + immUnbindProgram(); } } @@ -234,6 +272,10 @@ static void wm_method_draw_stereo3d_topbottom(wmWindow *win) int view; int soffy; + Gwn_VertFormat *format = immVertexFormat(); + unsigned int texcoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + for (view = 0; view < 2; view ++) { drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); triple = drawdata->triple; @@ -262,26 +304,33 @@ static void wm_method_draw_stereo3d_topbottom(wmWindow *win) halfy /= triple->y; } - glEnable(triple->target); + /* TODO: if target is always same for both eyes, bind program & set uniforms before loop */ + immBindBuiltinProgram((triple->target == GL_TEXTURE_2D) ? GPU_SHADER_3D_IMAGE_MODULATE_ALPHA : GPU_SHADER_3D_IMAGE_RECT_MODULATE_ALPHA); + glBindTexture(triple->target, triple->bind); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - glTexCoord2f(halfx, halfy); - glVertex2f(0, soffy); + immUniform1f("alpha", 1.0f); + immUniform1i("image", 0); /* default GL_TEXTURE0 unit */ - glTexCoord2f(ratiox + halfx, halfy); - glVertex2f(sizex, soffy); + immBegin(GWN_PRIM_TRI_FAN, 4); - glTexCoord2f(ratiox + halfx, ratioy + halfy); - glVertex2f(sizex, soffy + (sizey * 0.5f)); + immAttrib2f(texcoord, halfx, halfy); + immVertex2f(pos, 0.0f, soffy); - glTexCoord2f(halfx, ratioy + halfy); - glVertex2f(0, soffy + (sizey * 0.5f)); - glEnd(); + immAttrib2f(texcoord, ratiox + halfx, halfy); + immVertex2f(pos, sizex, soffy); + immAttrib2f(texcoord, ratiox + halfx, ratioy + halfy); + immVertex2f(pos, sizex, soffy + (sizey * 0.5f)); + + immAttrib2f(texcoord, halfx, ratioy + halfy); + immVertex2f(pos, 0.0f, soffy + (sizey * 0.5f)); + + immEnd(); + + /* TODO: if target is always same for both eyes, unbind program & texture target after loop */ + immUnbindProgram(); glBindTexture(triple->target, 0); - glDisable(triple->target); } } @@ -310,9 +359,9 @@ void wm_method_draw_stereo3d(const bContext *UNUSED(C), wmWindow *win) static bool wm_stereo3d_quadbuffer_supported(void) { - int gl_stereo = 0; - glGetBooleanv(GL_STEREO, (GLboolean *)&gl_stereo); - return gl_stereo != 0; + GLboolean stereo = GL_FALSE; + glGetBooleanv(GL_STEREO, &stereo); + return stereo == GL_TRUE; } static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display) @@ -324,7 +373,8 @@ static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) { - bScreen *screen = win->screen; + const bScreen *screen = WM_window_get_active_screen(win); + const Scene *scene = WM_window_get_active_scene(win); /* some 3d methods change the window arrangement, thus they shouldn't * toggle on/off just because there is no 3d elements being drawn */ @@ -332,7 +382,7 @@ bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen; } - if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen) == false)) { + if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen, scene) == false)) { return false; } @@ -459,7 +509,7 @@ int wm_stereo3d_set_exec(bContext *C, wmOperator *op) prev_display_mode != win_src->stereo3d_format->display_mode) { /* in case the hardward supports pageflip but not the display */ - if ((win_dst = wm_window_copy_test(C, win_src))) { + if ((win_dst = wm_window_copy_test(C, win_src, false))) { /* pass */ } else { @@ -469,14 +519,16 @@ int wm_stereo3d_set_exec(bContext *C, wmOperator *op) } } else if (win_src->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) { - /* ED_screen_duplicate() can't handle other cases yet T44688 */ - if (win_src->screen->state != SCREENNORMAL) { + const bScreen *screen = WM_window_get_active_screen(win_src); + + /* ED_workspace_layout_duplicate() can't handle other cases yet T44688 */ + if (screen->state != SCREENNORMAL) { BKE_report(op->reports, RPT_ERROR, "Failed to switch to Time Sequential mode when in fullscreen"); ok = false; } /* pageflip requires a new window to be created with the proper OS flags */ - else if ((win_dst = wm_window_copy_test(C, win_src))) { + else if ((win_dst = wm_window_copy_test(C, win_src, false))) { if (wm_stereo3d_quadbuffer_supported()) { BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created"); } diff --git a/source/blender/windowmanager/intern/wm_subwindow.c b/source/blender/windowmanager/intern/wm_subwindow.c index 458ac4a121a..1a8f71891c0 100644 --- a/source/blender/windowmanager/intern/wm_subwindow.c +++ b/source/blender/windowmanager/intern/wm_subwindow.c @@ -47,8 +47,7 @@ #include "BIF_gl.h" -#include "GPU_extensions.h" -#include "GPU_basic_shader.h" +#include "GPU_matrix.h" #include "WM_api.h" #include "wm_subwindow.h" @@ -142,15 +141,17 @@ void wm_subwindow_origin_get(wmWindow *win, int swinid, int *x, int *y) static void wm_swin_matrix_get(wmWindow *win, wmSubWindow *swin, float mat[4][4]) { + const bScreen *screen = WM_window_get_active_screen(win); + /* used by UI, should find a better way to get the matrix there */ - if (swin->swinid == win->screen->mainwin) { + if (swin->swinid == screen->mainwin) { int width, height; wm_swin_size_get(swin, &width, &height); orthographic_m4(mat, -GLA_PIXEL_OFS, (float)width - GLA_PIXEL_OFS, -GLA_PIXEL_OFS, (float)height - GLA_PIXEL_OFS, -100, 100); } else { - glGetFloatv(GL_PROJECTION_MATRIX, (float *)mat); + gpuGetProjectionMatrix(mat); } } void wm_subwindow_matrix_get(wmWindow *win, int swinid, float mat[4][4]) @@ -216,7 +217,7 @@ int wm_subwindow_open(wmWindow *win, const rcti *winrct, bool activate) /* extra service */ wm_swin_size_get(swin, &width, &height); wmOrtho2_pixelspace(width, height); - glLoadIdentity(); + gpuLoadIdentity(); } return swin->swinid; @@ -322,7 +323,7 @@ void wmSubWindowScissorSet(wmWindow *win, int swinid, const rcti *srct, bool src glScissor(_curswin->winrct.xmin, _curswin->winrct.ymin, width, height); wmOrtho2_pixelspace(width, height); - glLoadIdentity(); + gpuLoadIdentity(); glFlush(); } @@ -333,31 +334,13 @@ void wmSubWindowSet(wmWindow *win, int swinid) wmSubWindowScissorSet(win, swinid, NULL, true); } -void wmFrustum(float x1, float x2, float y1, float y2, float n, float f) -{ - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glFrustum(x1, x2, y1, y2, n, f); - glMatrixMode(GL_MODELVIEW); -} - -void wmOrtho(float x1, float x2, float y1, float y2, float n, float f) -{ - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - glOrtho(x1, x2, y1, y2, n, f); - - glMatrixMode(GL_MODELVIEW); -} - void wmOrtho2(float x1, float x2, float y1, float y2) { /* prevent opengl from generating errors */ if (x1 == x2) x2 += 1.0f; if (y1 == y2) y2 += 1.0f; - wmOrtho(x1, x2, y1, y2, -100, 100); + gpuOrtho(x1, x2, y1, y2, -100, 100); } static void wmOrtho2_offset(const float x, const float y, const float ofs) diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 79078b321da..fef65d3dfc2 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -38,6 +38,7 @@ #include "DNA_listBase.h" #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "MEM_guardedalloc.h" @@ -51,12 +52,16 @@ #include "BKE_blender.h" #include "BKE_context.h" +#include "BKE_icons.h" #include "BKE_library.h" #include "BKE_global.h" #include "BKE_main.h" - +#include "BKE_screen.h" +#include "BKE_workspace.h" #include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" #include "WM_api.h" #include "WM_types.h" @@ -66,19 +71,23 @@ #include "wm_subwindow.h" #include "wm_event_system.h" +#include "ED_scene.h" #include "ED_screen.h" #include "ED_fileselect.h" #include "UI_interface.h" +#include "UI_interface_icons.h" #include "PIL_time.h" #include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_init_exit.h" -#include "GPU_glew.h" +#include "GPU_immediate.h" #include "BLF_api.h" +#include "UI_resources.h" + /* for assert */ #ifndef NDEBUG # include "BLI_threads.h" @@ -213,6 +222,7 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win) wm_ghostwindow_destroy(win); + BKE_workspace_instance_hook_free(G.main, win->workspace_hook); MEM_freeN(win->stereo3d_format); MEM_freeN(win); @@ -233,35 +243,60 @@ static int find_free_winid(wmWindowManager *wm) /* don't change context itself */ wmWindow *wm_window_new(bContext *C) { + Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = MEM_callocN(sizeof(wmWindow), "window"); - + BLI_addtail(&wm->windows, win); win->winid = find_free_winid(wm); win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo 3D Format (window)"); + win->workspace_hook = BKE_workspace_instance_hook_create(bmain); return win; } +/** + * A higher level version of copy that tests the new window can be added. + */ +static wmWindow *wm_window_new_test(bContext *C) +{ + wmWindow *win = wm_window_new(C); + + WM_check(C); + + if (win->ghostwin) { + WM_event_add_notifier(C, NC_WINDOW | NA_ADDED, NULL); + return win; + } + else { + wmWindowManager *wm = CTX_wm_manager(C); + wm_window_close(C, wm, win); + return NULL; + } +} /* part of wm_window.c api */ -wmWindow *wm_window_copy(bContext *C, wmWindow *win_src) +wmWindow *wm_window_copy(bContext *C, wmWindow *win_src, const bool duplicate_layout) { wmWindow *win_dst = wm_window_new(C); - + WorkSpace *workspace = WM_window_get_active_workspace(win_src); + WorkSpaceLayout *layout_old = WM_window_get_active_layout(win_src); + Scene *scene = WM_window_get_active_scene(win_src); + WorkSpaceLayout *layout_new; + bScreen *new_screen; + win_dst->posx = win_src->posx + 10; win_dst->posy = win_src->posy; win_dst->sizex = win_src->sizex; win_dst->sizey = win_src->sizey; - - /* duplicate assigns to window */ - win_dst->screen = ED_screen_duplicate(win_dst, win_src->screen); - BLI_strncpy(win_dst->screenname, win_dst->screen->id.name + 2, sizeof(win_dst->screenname)); - win_dst->screen->winid = win_dst->winid; - win_dst->screen->do_refresh = true; - win_dst->screen->do_draw = true; + win_dst->scene = scene; + WM_window_set_active_workspace(win_dst, workspace); + layout_new = duplicate_layout ? ED_workspace_layout_duplicate(workspace, layout_old, win_dst) : layout_old; + WM_window_set_active_layout(win_dst, workspace, layout_new); + new_screen = WM_window_get_active_screen(win_dst); + BLI_strncpy(win_dst->screenname, new_screen->id.name + 2, sizeof(win_dst->screenname)); win_dst->drawmethod = U.wmdrawmethod; @@ -276,12 +311,12 @@ wmWindow *wm_window_copy(bContext *C, wmWindow *win_src) * A higher level version of copy that tests the new window can be added. * (called from the operator directly) */ -wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src) +wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src, const bool duplicate_layout) { wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win_dst; - win_dst = wm_window_copy(C, win_src); + win_dst = wm_window_copy(C, win_src, duplicate_layout); WM_check(C); @@ -305,7 +340,7 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) { if (tmpwin == win) continue; - if (tmpwin->screen->temp == 0) + if (WM_window_is_temp_screen(tmpwin) == false) break; } @@ -324,8 +359,10 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) WM_exit(C); } else { - bScreen *screen = win->screen; - + bScreen *screen = WM_window_get_active_screen(win); + WorkSpace *workspace = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout = BKE_workspace_active_layout_get(win->workspace_hook); + BLI_remlink(&wm->windows, win); wm_draw_window_clear(win); @@ -336,8 +373,8 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) /* for regular use this will _never_ be NULL, * however we may be freeing an improperly initialized window. */ - if (win->screen) { - ED_screen_exit(C, win, win->screen); + if (screen) { + ED_screen_exit(C, win, screen); } wm_window_free(C, wm, win); @@ -345,14 +382,16 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) /* if temp screen, delete it after window free (it stops jobs that can access it) */ if (screen && screen->temp) { Main *bmain = CTX_data_main(C); - BKE_libblock_free(bmain, screen); + + BLI_assert(BKE_workspace_layout_screen_get(layout) == screen); + BKE_workspace_layout_remove(bmain, workspace, layout); } - } + } } void wm_window_title(wmWindowManager *wm, wmWindow *win) { - if (win->screen && win->screen->temp) { + if (WM_window_is_temp_screen(win)) { /* nothing to do for 'temp' windows, * because WM_window_open_temp always sets window title */ } @@ -658,8 +697,10 @@ wmWindow *WM_window_open(bContext *C, const rcti *rect) */ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, int type) { + Main *bmain = CTX_data_main(C); wmWindow *win_prev = CTX_wm_window(C); wmWindow *win; + bScreen *screen; ScrArea *sa; Scene *scene = CTX_data_scene(C); const char *title; @@ -683,7 +724,7 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i /* test if we have a temp screen already */ for (win = CTX_wm_manager(C)->windows.first; win; win = win->next) - if (win->screen->temp) + if (WM_window_is_temp_screen(win)) break; /* add new window? */ @@ -694,6 +735,8 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i win->posy = rect.ymin; } + screen = WM_window_get_active_screen(win); + win->sizex = BLI_rcti_size_x(&rect); win->sizey = BLI_rcti_size_y(&rect); @@ -701,19 +744,27 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i wm_window_set_size(win, win->sizex, win->sizey); wm_window_raise(win); } - - if (win->screen == NULL) { - /* add new screen */ - win->screen = ED_screen_add(win, scene, "temp"); + + if (WM_window_get_active_workspace(win) == NULL) { + WorkSpace *workspace = WM_window_get_active_workspace(win_prev); + WM_window_set_active_workspace(win, workspace); } - else { - /* switch scene for rendering */ - if (win->screen->scene != scene) - ED_screen_set_scene(C, win->screen, scene); + + if (screen == NULL) { + /* add new screen layout */ + WorkSpace *workspace = WM_window_get_active_workspace(win); + WorkSpaceLayout *layout = ED_workspace_layout_add(workspace, win, "temp"); + + screen = BKE_workspace_layout_screen_get(layout); + WM_window_set_active_layout(win, workspace, layout); } - win->screen->temp = 1; - + if (WM_window_get_active_scene(win) != scene) { + WM_window_change_active_scene(bmain, C, win, scene); + } + + screen->temp = 1; + /* make window active, and validate/resize */ CTX_wm_window_set(C, win); WM_check(C); @@ -725,7 +776,7 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i */ /* ensure it shows the right spacetype editor */ - sa = win->screen->areabase.first; + sa = screen->areabase.first; CTX_wm_area_set(C, sa); if (type == WM_WINDOW_RENDER) { @@ -735,7 +786,7 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i ED_area_newspace(C, sa, SPACE_USERPREF, false); } - ED_screen_set(C, win->screen); + ED_screen_change(C, screen); ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */ if (sa->spacetype == SPACE_IMAGE) @@ -771,17 +822,107 @@ int wm_window_close_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -/* operator callback */ -int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) +static WorkSpaceLayout *wm_window_new_find_layout(wmOperator *op, WorkSpace *workspace) +{ + ListBase *listbase = BKE_workspace_layouts_get(workspace); + const int layout_id = RNA_enum_get(op->ptr, "screen"); + int i = 0; + + for (WorkSpaceLayout *layout = listbase->first; layout; layout = layout->next) { + if (i++ == layout_id) { + return layout; + } + } + + BLI_assert(0); + return NULL; +} + +/* new window operator callback */ +int wm_window_new_exec(bContext *C, wmOperator *op) { wmWindow *win_src = CTX_wm_window(C); - bool ok; + WorkSpace *workspace = WM_window_get_active_workspace(win_src); + WorkSpaceLayout *layout_new = wm_window_new_find_layout(op, workspace); + bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new); + wmWindow *win_dst; - ok = (wm_window_copy_test(C, win_src) != NULL); + if ((win_dst = wm_window_new_test(C))) { + if (screen_new->winid) { + /* layout/screen is already used, duplicate it */ + layout_new = ED_workspace_layout_duplicate(workspace, layout_new, win_dst); + screen_new = BKE_workspace_layout_screen_get(layout_new); + } + /* New window with a different screen but same workspace */ + WM_window_set_active_workspace(win_dst, workspace); + WM_window_set_active_screen(win_dst, workspace, screen_new); + win_dst->scene = win_src->scene; + screen_new->winid = win_dst->winid; + CTX_wm_window_set(C, win_dst); + ED_screen_refresh(CTX_wm_manager(C), win_dst); + } - return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + return (win_dst != NULL) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } +int wm_window_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + wmWindow *win = CTX_wm_window(C); + WorkSpace *workspace = WM_window_get_active_workspace(win); + ListBase *listbase = BKE_workspace_layouts_get(workspace); + + if (BLI_listbase_count_ex(listbase, 2) == 1) { + RNA_enum_set(op->ptr, "screen", 0); + return wm_window_new_exec(C, op); + } + else { + return WM_enum_search_invoke_previews(C, op, 6, 2); + } +} + +const EnumPropertyItem *wm_window_new_screen_itemf( + bContext *C, struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), bool *r_free) +{ + if (C == NULL) { + return DummyRNA_NULL_items; + } + wmWindow *win = CTX_wm_window(C); + WorkSpace *workspace = WM_window_get_active_workspace(win); + ListBase *listbase = BKE_workspace_layouts_get(workspace); + EnumPropertyItem *item = NULL; + EnumPropertyItem tmp = {0, "", 0, "", ""}; + int value = 0, totitem = 0; + int count_act_screens = 0; + /* XXX setting max number of windows to 20. We'd need support + * for dynamic strings in EnumPropertyItem.name to avoid this. */ + static char active_screens[20][MAX_NAME + 12]; + + for (WorkSpaceLayout *layout = listbase->first; layout; layout = layout->next) { + bScreen *screen = BKE_workspace_layout_screen_get(layout); + const char *layout_name = BKE_workspace_layout_name_get(layout); + + if (screen->winid) { + BLI_snprintf(active_screens[count_act_screens], sizeof(*active_screens), "%s (Duplicate)", layout_name); + tmp.name = active_screens[count_act_screens++]; + } + else { + tmp.name = layout_name; + } + + tmp.value = value; + tmp.identifier = layout_name; + UI_id_icon_render(C, CTX_data_scene(C), &screen->id, true, false); + tmp.icon = BKE_icon_id_ensure(&screen->id); + + RNA_enum_item_add(&item, &totitem, &tmp); + value++; + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} /* fullscreen operator callback */ int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op)) @@ -881,8 +1022,11 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) if (G.debug & G_DEBUG_EVENTS) { printf("%s: set drawable %d\n", __func__, win->winid); } + + immDeactivate(); GHOST_ActivateWindowDrawingContext(win->ghostwin); - + immActivate(); + /* this can change per window */ WM_window_set_dpi(win); } @@ -1081,7 +1225,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* stop screencast if resize */ if (type == GHOST_kEventWindowSize) { - WM_jobs_stop(wm, win->screen, NULL); + WM_jobs_stop(wm, WM_window_get_active_screen(win), NULL); } WM_window_set_dpi(win); @@ -1116,6 +1260,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr win->posx != posx || win->posy != posy) { + const bScreen *screen = WM_window_get_active_screen(win); + win->sizex = sizex; win->sizey = sizey; win->posx = posx; @@ -1152,6 +1298,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wm_window_make_drawable(wm, win); wm_draw_window_clear(win); + BKE_icon_changed(screen->id.icon_id); WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL); @@ -1263,6 +1410,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr WM_window_set_dpi(win); if (U.pixelsize != prev_pixelsize) { + BKE_icon_changed(WM_window_get_active_screen(win)->id.icon_id); + // 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); @@ -1768,6 +1917,96 @@ bool WM_window_is_fullscreen(wmWindow *win) return win->windowstate == GHOST_kWindowStateFullScreen; } +/** + * Some editor data may need to be synced with scene data (3D View camera and layers). + * This function ensures data is synced for editors in visible workspaces and their visible layouts. + */ +void WM_windows_scene_data_sync(const ListBase *win_lb, Scene *scene) +{ + for (wmWindow *win = win_lb->first; win; win = win->next) { + if (WM_window_get_active_scene(win) == scene) { + ED_workspace_scene_data_sync(win->workspace_hook, scene); + } + } +} + +Scene *WM_windows_scene_get_from_screen(const wmWindowManager *wm, const bScreen *screen) +{ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (WM_window_get_active_screen(win) == screen) { + return WM_window_get_active_scene(win); + } + } + + return NULL; +} + +WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const bScreen *screen) +{ + for (wmWindow *win = wm->windows.first; win; win = win->next) { + if (WM_window_get_active_screen(win) == screen) { + return WM_window_get_active_workspace(win); + } + } + return NULL; +} + +Scene *WM_window_get_active_scene(const wmWindow *win) +{ + return win->scene; +} + +/** + * \warning Only call outside of area/region loops + */ +void WM_window_change_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene *scene_new) +{ + const bScreen *screen = WM_window_get_active_screen(win); + + ED_scene_exit(C); + win->scene = scene_new; + ED_scene_changed_update(bmain, C, scene_new, screen); +} + +WorkSpace *WM_window_get_active_workspace(const wmWindow *win) +{ + return BKE_workspace_active_get(win->workspace_hook); +} +void WM_window_set_active_workspace(wmWindow *win, WorkSpace *workspace) +{ + BKE_workspace_active_set(win->workspace_hook, workspace); +} + +WorkSpaceLayout *WM_window_get_active_layout(const wmWindow *win) +{ + const WorkSpace *workspace = WM_window_get_active_workspace(win); + return (LIKELY(workspace != NULL) ? BKE_workspace_active_layout_get(win->workspace_hook) : NULL); +} +void WM_window_set_active_layout(wmWindow *win, WorkSpace *workspace, WorkSpaceLayout *layout) +{ + BKE_workspace_hook_layout_for_workspace_set(win->workspace_hook, workspace, layout); +} + +/** + * Get the active screen of the active workspace in \a win. + */ +bScreen *WM_window_get_active_screen(const wmWindow *win) +{ + const WorkSpace *workspace = WM_window_get_active_workspace(win); + /* May be NULL in rare cases like closing Blender */ + return (LIKELY(workspace != NULL) ? BKE_workspace_active_screen_get(win->workspace_hook) : NULL); +} +void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *screen) +{ + BKE_workspace_active_screen_set(win->workspace_hook, workspace, screen); +} + +bool WM_window_is_temp_screen(const wmWindow *win) +{ + const bScreen *screen = WM_window_get_active_screen(win); + return (screen && screen->temp != 0); +} + #ifdef WITH_INPUT_IME /* note: keep in mind wm_window_IME_begin is also used to reposition the IME window */ diff --git a/source/blender/windowmanager/manipulators/WM_manipulator_api.h b/source/blender/windowmanager/manipulators/WM_manipulator_api.h new file mode 100644 index 00000000000..afc39fe1005 --- /dev/null +++ b/source/blender/windowmanager/manipulators/WM_manipulator_api.h @@ -0,0 +1,310 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/manipulators/WM_manipulator_api.h + * \ingroup wm + * + * \name Manipulator API + * \brief API for external use of wmManipulator types. + * + * Only included in WM_api.h + */ + + +#ifndef __WM_MANIPULATOR_API_H__ +#define __WM_MANIPULATOR_API_H__ + +struct ARegion; +struct GHashIterator; +struct IDProperty; +struct Main; +struct PropertyRNA; +struct wmKeyConfig; +struct wmManipulator; +struct wmManipulatorProperty; +struct wmManipulatorPropertyType; +struct wmManipulatorType; +struct wmManipulatorGroup; +struct wmManipulatorGroupType; +struct wmManipulatorMap; +struct wmManipulatorMapType; +struct wmManipulatorMapType_Params; + +#include "wm_manipulator_fn.h" + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + +struct wmManipulator *WM_manipulator_new_ptr( + const struct wmManipulatorType *wt, struct wmManipulatorGroup *mgroup, + struct PointerRNA *properties); +struct wmManipulator *WM_manipulator_new( + const char *idname, struct wmManipulatorGroup *mgroup, + struct PointerRNA *properties); +void WM_manipulator_free(struct wmManipulator *mpr); +void WM_manipulator_unlink( + ListBase *manipulatorlist, struct wmManipulatorMap *mmap, struct wmManipulator *mpr, + struct bContext *C); + +void WM_manipulator_name_set(struct wmManipulatorGroup *mgroup, struct wmManipulator *mpr, const char *name); + +bool WM_manipulator_select_unlink(struct wmManipulatorMap *mmap, struct wmManipulator *mpr); +bool WM_manipulator_select_set(struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select); + +struct wmManipulatorOpElem *WM_manipulator_operator_get( + struct wmManipulator *mpr, int part_index); +struct PointerRNA *WM_manipulator_operator_set( + struct wmManipulator *mpr, int part_index, + struct wmOperatorType *ot, struct IDProperty *properties); + +/* callbacks */ +void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulatorFnModal fn); + +void WM_manipulator_set_matrix_location( + struct wmManipulator *mpr, const float origin[3]); +void WM_manipulator_set_matrix_rotation_from_z_axis( + struct wmManipulator *mpr, const float z_axis[3]); +void WM_manipulator_set_matrix_rotation_from_yz_axis( + struct wmManipulator *mpr, const float y_axis[3], const float z_axis[3]); + +void WM_manipulator_set_matrix_offset_location( + struct wmManipulator *mpr, const float origin[3]); +void WM_manipulator_set_matrix_offset_rotation_from_z_axis( + struct wmManipulator *mpr, const float z_axis[3]); +void WM_manipulator_set_matrix_offset_rotation_from_yz_axis( + struct wmManipulator *mpr, const float y_axis[3], const float z_axis[3]); + +void WM_manipulator_set_flag(struct wmManipulator *mpr, const int flag, const bool enable); +void WM_manipulator_set_scale(struct wmManipulator *mpr, float scale); +void WM_manipulator_set_line_width(struct wmManipulator *mpr, const float line_width); + +void WM_manipulator_get_color(const struct wmManipulator *mpr, float color[4]); +void WM_manipulator_set_color(struct wmManipulator *mpr, const float color[4]); +void WM_manipulator_get_color_highlight(const struct wmManipulator *mpr, float color_hi[4]); +void WM_manipulator_set_color_highlight(struct wmManipulator *mpr, const float color[4]); + +/** + * Leaving values NULL use values from #wmManipulator. + */ +struct WM_ManipulatorMatrixParams { + const float(*matrix_space)[4]; + const float(*matrix_basis)[4]; + const float(*matrix_offset)[4]; + const float *scale_final; +}; + +void WM_manipulator_calc_matrix_final_params( + const struct wmManipulator *mpr, const struct WM_ManipulatorMatrixParams *params, + float r_mat[4][4]); + +void WM_manipulator_calc_matrix_final(const struct wmManipulator *mpr, float r_mat[4][4]); + +/* properties */ +void WM_manipulator_properties_create_ptr(struct PointerRNA *ptr, struct wmManipulatorType *wt); +void WM_manipulator_properties_create(struct PointerRNA *ptr, const char *opstring); +void WM_manipulator_properties_alloc(struct PointerRNA **ptr, struct IDProperty **properties, const char *wtstring); +void WM_manipulator_properties_sanitize(struct PointerRNA *ptr, const bool no_context); +bool WM_manipulator_properties_default(struct PointerRNA *ptr, const bool do_update); +void WM_manipulator_properties_reset(struct wmManipulator *op); +void WM_manipulator_properties_clear(struct PointerRNA *ptr); +void WM_manipulator_properties_free(struct PointerRNA *ptr); + + +/* wm_manipulator_type.c */ +const struct wmManipulatorType *WM_manipulatortype_find(const char *idname, bool quiet); +void WM_manipulatortype_append(void (*wtfunc)(struct wmManipulatorType *)); +void WM_manipulatortype_append_ptr(void (*mnpfunc)(struct wmManipulatorType *, void *), void *userdata); +bool WM_manipulatortype_remove(struct bContext *C, struct Main *bmain, const char *idname); +void WM_manipulatortype_remove_ptr(struct bContext *C, struct Main *bmain, struct wmManipulatorType *wt); +void WM_manipulatortype_iter(struct GHashIterator *ghi); + +/* wm_manipulator_group_type.c */ +struct wmManipulatorGroupType *WM_manipulatorgrouptype_find(const char *idname, bool quiet); +struct wmManipulatorGroupType *WM_manipulatorgrouptype_append(void (*wtfunc)(struct wmManipulatorGroupType *)); +struct wmManipulatorGroupType *WM_manipulatorgrouptype_append_ptr(void (*mnpfunc)(struct wmManipulatorGroupType *, void *), void *userdata); +bool WM_manipulatorgrouptype_free(const char *idname); +void WM_manipulatorgrouptype_free_ptr(struct wmManipulatorGroupType *wt); +void WM_manipulatorgrouptype_iter(struct GHashIterator *ghi); + +struct wmManipulatorGroupTypeRef *WM_manipulatorgrouptype_append_and_link( + struct wmManipulatorMapType *mmap_type, + void (*wtfunc)(struct wmManipulatorGroupType *)); + +/* wm_manipulator_map.c */ + +/* Dynamic Updates (for RNA runtime registration) */ +void WM_manipulatorconfig_update_tag_init(struct wmManipulatorMapType *mmap_type, struct wmManipulatorGroupType *wgt); +void WM_manipulatorconfig_update_tag_remove(struct wmManipulatorMapType *mmap_type, struct wmManipulatorGroupType *wgt); +void WM_manipulatorconfig_update(struct Main *bmain); + + +/* wm_maniulator_target_props.c */ +struct wmManipulatorProperty *WM_manipulator_target_property_array(struct wmManipulator *mpr); +struct wmManipulatorProperty *WM_manipulator_target_property_at_index( + struct wmManipulator *mpr, int index); +struct wmManipulatorProperty *WM_manipulator_target_property_find( + struct wmManipulator *mpr, const char *idname); + +void WM_manipulator_target_property_def_rna_ptr( + struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type, + struct PointerRNA *ptr, struct PropertyRNA *prop, int index); +void WM_manipulator_target_property_def_rna( + struct wmManipulator *mpr, const char *idname, + struct PointerRNA *ptr, const char *propname, int index); + +void WM_manipulator_target_property_def_func_ptr( + struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type, + const struct wmManipulatorPropertyFnParams *params); +void WM_manipulator_target_property_def_func( + struct wmManipulator *mpr, const char *idname, + const struct wmManipulatorPropertyFnParams *params); + +bool WM_manipulator_target_property_is_valid_any(struct wmManipulator *mpr); +bool WM_manipulator_target_property_is_valid( + const struct wmManipulatorProperty *mpr_prop); +float WM_manipulator_target_property_value_get( + const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop); +void WM_manipulator_target_property_value_set( + struct bContext *C, const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop, + const float value); + +void WM_manipulator_target_property_value_get_array( + const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop, + float *value); +void WM_manipulator_target_property_value_set_array( + struct bContext *C, const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop, + const float *value); + +bool WM_manipulator_target_property_range_get( + const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop, + float range[2]); + +int WM_manipulator_target_property_array_length( + const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop); + +/* definitions */ +const struct wmManipulatorPropertyType *WM_manipulatortype_target_property_find( + const struct wmManipulatorType *wt, const char *idname); +void WM_manipulatortype_target_property_def( + struct wmManipulatorType *wt, const char *idname, int data_type, int array_length); + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +/* Callbacks for 'wmManipulatorGroupType.setup_keymap' */ +struct wmKeyMap *WM_manipulatorgroup_keymap_common( + const struct wmManipulatorGroupType *wgt, struct wmKeyConfig *config); +struct wmKeyMap *WM_manipulatorgroup_keymap_common_select( + const struct wmManipulatorGroupType *wgt, struct wmKeyConfig *config); + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +struct wmManipulatorMap *WM_manipulatormap_new_from_type( + const struct wmManipulatorMapType_Params *mmap_params); +const struct ListBase *WM_manipulatormap_group_list(struct wmManipulatorMap *mmap); +void WM_manipulatormap_tag_refresh(struct wmManipulatorMap *mmap); +void WM_manipulatormap_draw( + struct wmManipulatorMap *mmap, const struct bContext *C, const eWM_ManipulatorMapDrawStep drawstep); +void WM_manipulatormap_add_handlers(struct ARegion *ar, struct wmManipulatorMap *mmap); +bool WM_manipulatormap_select_all(struct bContext *C, struct wmManipulatorMap *mmap, const int action); +bool WM_manipulatormap_cursor_set(const struct wmManipulatorMap *mmap, struct wmWindow *win); +bool WM_manipulatormap_is_any_selected(const struct wmManipulatorMap *mmap); +bool WM_manipulatormap_minmax( + const struct wmManipulatorMap *mmap, bool use_hidden, bool use_select, + float r_min[3], float r_max[3]); + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMapType */ + +struct wmManipulatorMapType *WM_manipulatormaptype_find( + const struct wmManipulatorMapType_Params *mmap_params); +struct wmManipulatorMapType *WM_manipulatormaptype_ensure( + const struct wmManipulatorMapType_Params *mmap_params); + +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find( + struct wmManipulatorMapType *mmap_type, + const char *idname); +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find_ptr( + struct wmManipulatorMapType *mmap_type, + const struct wmManipulatorGroupType *wgt); +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link( + struct wmManipulatorMapType *mmap_type, + const char *idname); +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link_ptr( + struct wmManipulatorMapType *mmap_type, + struct wmManipulatorGroupType *wgt); + +void WM_manipulatormaptype_group_init_runtime_keymap( + const struct Main *bmain, + struct wmManipulatorGroupType *wgt); +void WM_manipulatormaptype_group_init_runtime( + const struct Main *bmain, struct wmManipulatorMapType *mmap_type, + struct wmManipulatorGroupType *wgt); +void WM_manipulatormaptype_group_unlink( + struct bContext *C, struct Main *bmain, struct wmManipulatorMapType *mmap_type, + const struct wmManipulatorGroupType *wgt); + +void WM_manipulatormaptype_group_free(struct wmManipulatorGroupTypeRef *wgt); + +/* -------------------------------------------------------------------- */ +/* ManipulatorGroup */ + +/* Add/Ensure/Remove (High level API) */ + +void WM_manipulator_group_type_add_ptr_ex( + struct wmManipulatorGroupType *wgt, + struct wmManipulatorMapType *mmap_type); +void WM_manipulator_group_type_add_ptr( + struct wmManipulatorGroupType *wgt); +void WM_manipulator_group_type_add(const char *idname); + +void WM_manipulator_group_type_ensure_ptr_ex( + struct wmManipulatorGroupType *wgt, + struct wmManipulatorMapType *mmap_type); +void WM_manipulator_group_type_ensure_ptr( + struct wmManipulatorGroupType *wgt); +void WM_manipulator_group_type_ensure(const char *idname); + +void WM_manipulator_group_type_remove_ptr_ex( + struct Main *bmain, struct wmManipulatorGroupType *wgt, + struct wmManipulatorMapType *mmap_type); +void WM_manipulator_group_type_remove_ptr( + struct Main *bmain, struct wmManipulatorGroupType *wgt); +void WM_manipulator_group_type_remove(struct Main *bmain, const char *idname); + +void WM_manipulator_group_type_unlink_delayed_ptr_ex( + struct wmManipulatorGroupType *wgt, + struct wmManipulatorMapType *mmap_type); +void WM_manipulator_group_type_unlink_delayed_ptr( + struct wmManipulatorGroupType *wgt); +void WM_manipulator_group_type_unlink_delayed(const char *idname); + +/* Utilities */ +void WM_manipulator_group_type_is_any_selected(const char *idname); + +#endif /* __WM_MANIPULATOR_API_H__ */ diff --git a/source/blender/windowmanager/manipulators/WM_manipulator_types.h b/source/blender/windowmanager/manipulators/WM_manipulator_types.h new file mode 100644 index 00000000000..d4477b8e508 --- /dev/null +++ b/source/blender/windowmanager/manipulators/WM_manipulator_types.h @@ -0,0 +1,403 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/manipulators/WM_manipulator_types.h + * \ingroup wm + * + * \name Manipulator Types + * \brief Manipulator defines for external use. + * + * Only included in WM_types.h and lower level files. + */ + + +#ifndef __WM_MANIPULATOR_TYPES_H__ +#define __WM_MANIPULATOR_TYPES_H__ + +#include "BLI_compiler_attrs.h" + +struct wmManipulatorMapType; +struct wmManipulatorGroupType; +struct wmManipulatorGroup; +struct wmManipulator; +struct wmManipulatorProperty; +struct wmKeyConfig; + +#include "DNA_listBase.h" + + +/* -------------------------------------------------------------------- */ +/* Enum Typedef's */ + + +/** + * #wmManipulator.state + */ +typedef enum eWM_ManipulatorState { + WM_MANIPULATOR_STATE_HIGHLIGHT = (1 << 0), /* while hovered */ + WM_MANIPULATOR_STATE_MODAL = (1 << 1), /* while dragging */ + WM_MANIPULATOR_STATE_SELECT = (1 << 2), +} eWM_ManipulatorState; + + +/** + * #wmManipulator.flag + * Flags for individual manipulators. + */ +typedef enum eWM_ManipulatorFlag { + WM_MANIPULATOR_DRAW_HOVER = (1 << 0), /* draw *only* while hovering */ + WM_MANIPULATOR_DRAW_MODAL = (1 << 1), /* draw while dragging */ + WM_MANIPULATOR_DRAW_VALUE = (1 << 2), /* draw an indicator for the current value while dragging */ + WM_MANIPULATOR_HIDDEN = (1 << 3), + /** + * When set 'scale_final' value also scales the offset. + * Use when offset is to avoid screen-space overlap instead of absolute positioning. */ + WM_MANIPULATOR_DRAW_OFFSET_SCALE = (1 << 4), + /** + * User should still use 'scale_final' for any handles and UI elements. + * This simply skips scale when calculating the final matrix. + * Needed when the manipulator needs to align with the interface underneath it. */ + WM_MANIPULATOR_DRAW_NO_SCALE = (1 << 5), +} eWM_ManipulatorFlag; + +/** + * #wmManipulatorGroupType.flag + * Flags that influence the behavior of all manipulators in the group. + */ +typedef enum eWM_ManipulatorGroupTypeFlag { + /* Mark manipulator-group as being 3D */ + WM_MANIPULATORGROUPTYPE_3D = (1 << 0), + /* Scale manipulators as 3D object that respects zoom (otherwise zoom independent draw size). + * note: currently only for 3D views, 2D support needs adding. */ + WM_MANIPULATORGROUPTYPE_SCALE = (1 << 1), + /* Manipulators can be depth culled with scene objects (covered by other geometry - TODO) */ + WM_MANIPULATORGROUPTYPE_DEPTH_3D = (1 << 2), + /* Manipulators can be selected */ + WM_MANIPULATORGROUPTYPE_SELECT = (1 << 3), + /* The manipulator group is to be kept (not removed on loading a new file for eg). */ + WM_MANIPULATORGROUPTYPE_PERSISTENT = (1 << 4), + /* Show all other manipulators when interacting. */ + WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL = (1 << 5), +} eWM_ManipulatorGroupTypeFlag; + + +/** + * #wmManipulatorGroup.init_flag + */ +typedef enum eWM_ManipulatorGroupInitFlag { + /* mgroup has been initialized */ + WM_MANIPULATORGROUP_INIT_SETUP = (1 << 0), + WM_MANIPULATORGROUP_INIT_REFRESH = (1 << 1), +} eWM_ManipulatorGroupInitFlag; + +/** + * #wmManipulatorMapType.type_update_flag + * Manipulator-map type update flag + */ +typedef enum eWM_ManipulatorMapTypeUpdateFlag { + /* A new type has been added, needs to be initialized for all views. */ + WM_MANIPULATORMAPTYPE_UPDATE_INIT = (1 << 0), + WM_MANIPULATORMAPTYPE_UPDATE_REMOVE = (1 << 1), + + /* Needed because keymap may be registered before and after window initialization. + * So we need to keep track of keymap initialization separately. */ + WM_MANIPULATORMAPTYPE_KEYMAP_INIT = (1 << 2), +} eWM_ManipulatorMapTypeUpdateFlag; + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + +/** + * \brief Manipulator tweak flag. + * Bitflag passed to manipulator while tweaking. + * + * \note Manipulators are responsible for handling this #wmManipulator.modal callback!. + */ +typedef enum { + /* Drag with extra precision (Shift). */ + WM_MANIPULATOR_TWEAK_PRECISE = (1 << 0), + /* Drag with snap enabled (Ctrl). */ + WM_MANIPULATOR_TWEAK_SNAP = (1 << 1), +} eWM_ManipulatorTweak; + +#include "wm_manipulator_fn.h" + +typedef struct wmManipulatorOpElem { + struct wmOperatorType *type; + /* operator properties if manipulator spawns and controls an operator, + * or owner pointer if manipulator spawns and controls a property */ + PointerRNA ptr; +} wmManipulatorOpElem; + +/* manipulators are set per region by registering them on manipulator-maps */ +struct wmManipulator { + struct wmManipulator *next, *prev; + + /* While we don't have a real type, use this to put type-like vars. */ + const struct wmManipulatorType *type; + + /* Overrides 'type->modal' when set. + * Note that this is a workaround, remove if we can. */ + wmManipulatorFnModal custom_modal; + + /* pointer back to group this manipulator is in (just for quick access) */ + struct wmManipulatorGroup *parent_mgroup; + + void *py_instance; + + /* rna pointer to access properties */ + struct PointerRNA *ptr; + + /* flags that influence the behavior or how the manipulators are drawn */ + eWM_ManipulatorFlag flag; + /* state flags (active, highlighted, selected) */ + eWM_ManipulatorState state; + + /* Optional ID for highlighting different parts of this manipulator. + * -1 when unset, otherwise a valid index. (Used as index to 'op_data'). */ + int highlight_part; + + /* Transformation of the manipulator in 2d or 3d space. + * - Matrix axis are expected to be unit length (scale is applied after). + * - Behavior when axis aren't orthogonal depends on each manipulator. + * - Typically the +Z is the primary axis for manipulators to use. + * - 'matrix[3]' must be used for location, + * besides this it's up to the manipulators internal code how the + * rotation components are used for drawing and interaction. + */ + + /* The space this manipulator is being modified in. */ + float matrix_space[4][4]; + /* Transformation of this manipulator. */ + float matrix_basis[4][4]; + /* custom offset from origin */ + float matrix_offset[4][4]; + /* runtime property, set the scale while drawing on the viewport */ + float scale_final; + /* user defined scale, in addition to the original one */ + float scale_basis; + /* user defined width for line drawing */ + float line_width; + /* manipulator colors (uses default fallbacks if not defined) */ + float color[4], color_hi[4]; + + /* data used during interaction */ + void *interaction_data; + + /* Operator to spawn when activating the manipulator (overrides property editing), + * an array of items (aligned with #wmManipulator.highlight_part). */ + wmManipulatorOpElem *op_data; + int op_data_len; + + struct IDProperty *properties; + + /* over alloc target_properties after 'wmManipulatorType.struct_size' */ +}; + +/* Similar to PropertyElemRNA, but has an identifier. */ +typedef struct wmManipulatorProperty { + const struct wmManipulatorPropertyType *type; + + PointerRNA ptr; + PropertyRNA *prop; + int index; + + + /* Optional functions for converting to/from RNA */ + struct { + wmManipulatorPropertyFnGet value_get_fn; + wmManipulatorPropertyFnSet value_set_fn; + wmManipulatorPropertyFnRangeGet range_get_fn; + wmManipulatorPropertyFnFree free_fn; + void *user_data; + } custom_func; +} wmManipulatorProperty; + +typedef struct wmManipulatorPropertyType { + struct wmManipulatorPropertyType *next, *prev; + /* PropertyType, typically 'PROP_FLOAT' */ + int data_type; + int array_length; + + /* index within 'wmManipulatorType' */ + int index_in_type; + + /* over alloc */ + char idname[0]; +} wmManipulatorPropertyType; + + +/** + * Simple utility wrapper for storing a single manipulator as wmManipulatorGroup.customdata (which gets freed). + */ +typedef struct wmManipulatorWrapper { + struct wmManipulator *manipulator; +} wmManipulatorWrapper; + +struct wmManipulatorMapType_Params { + short spaceid; + short regionid; +}; + +typedef struct wmManipulatorType { + + const char *idname; /* MAX_NAME */ + + /* Set to 'sizeof(wmManipulator)' or larger for instances of this type, + * use so we can cant to other types without the hassle of a custom-data pointer. */ + uint struct_size; + + /* Initialize struct (calloc'd 'struct_size' region). */ + wmManipulatorFnSetup setup; + + /* draw manipulator */ + wmManipulatorFnDraw draw; + + /* determines 3d intersection by rendering the manipulator in a selection routine. */ + wmManipulatorFnDrawSelect draw_select; + + /* Determine if the mouse intersects with the manipulator. + * The calculation should be done in the callback itself, -1 for no seleciton. */ + wmManipulatorFnTestSelect test_select; + + /* handler used by the manipulator. Usually handles interaction tied to a manipulator type */ + wmManipulatorFnModal modal; + + /* manipulator-specific handler to update manipulator attributes based on the property value */ + wmManipulatorFnPropertyUpdate property_update; + + /* Returns the final transformation which may be different from the 'matrix', + * depending on the manipulator. + * Notes: + * - Scale isn't applied (wmManipulator.scale/user_scale). + * - Offset isn't applied (wmManipulator.matrix_offset). + */ + wmManipulatorFnMatrixBasisGet matrix_basis_get; + + /* activate a manipulator state when the user clicks on it */ + wmManipulatorFnInvoke invoke; + + /* called when manipulator tweaking is done - used to free data and reset property when cancelling */ + wmManipulatorFnExit exit; + + wmManipulatorFnCursorGet cursor_get; + + /* called when manipulator selection state changes */ + wmManipulatorFnSelectRefresh select_refresh; + + /* RNA for properties */ + struct StructRNA *srna; + + /* RNA integration */ + ExtensionRNA ext; + + ListBase target_property_defs; + int target_property_defs_len; + +} wmManipulatorType; + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +/* factory class for a manipulator-group type, gets called every time a new area is spawned */ +typedef struct wmManipulatorGroupTypeRef { + struct wmManipulatorGroupTypeRef *next, *prev; + struct wmManipulatorGroupType *type; +} wmManipulatorGroupTypeRef; + +/* factory class for a manipulator-group type, gets called every time a new area is spawned */ +typedef struct wmManipulatorGroupType { + const char *idname; /* MAX_NAME */ + const char *name; /* manipulator-group name - displayed in UI (keymap editor) */ + + /* poll if manipulator-map should be visible */ + wmManipulatorGroupFnPoll poll; + /* initially create manipulators and set permanent data - stuff you only need to do once */ + wmManipulatorGroupFnInit setup; + /* refresh data, only called if recreate flag is set (WM_manipulatormap_tag_refresh) */ + wmManipulatorGroupFnRefresh refresh; + /* refresh data for drawing, called before each redraw */ + wmManipulatorGroupFnDrawPrepare draw_prepare; + + /* Keymap init callback for this manipulator-group (optional), + * will fall back to default tweak keymap when left NULL. */ + wmManipulatorGroupFnSetupKeymap setup_keymap; + + /* keymap created with callback from above */ + struct wmKeyMap *keymap; + /* Only for convenient removal. */ + struct wmKeyConfig *keyconf; + + /* Disable for now, maybe some day we want properties. */ +#if 0 + /* rna for properties */ + struct StructRNA *srna; +#endif + + /* RNA integration */ + ExtensionRNA ext; + + eWM_ManipulatorGroupTypeFlag flag; + + /* So we know which group type to update. */ + eWM_ManipulatorMapTypeUpdateFlag type_update_flag; + + /* same as manipulator-maps, so registering/unregistering goes to the correct region */ + struct wmManipulatorMapType_Params mmap_params; + +} wmManipulatorGroupType; + +typedef struct wmManipulatorGroup { + struct wmManipulatorGroup *next, *prev; + + struct wmManipulatorGroupType *type; + ListBase manipulators; + + struct wmManipulatorMap *parent_mmap; + + void *py_instance; /* python stores the class instance here */ + struct ReportList *reports; /* errors and warnings storage */ + + void *customdata; + void (*customdata_free)(void *); /* for freeing customdata from above */ + eWM_ManipulatorGroupInitFlag init_flag; +} wmManipulatorGroup; + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +/** + * Pass a value of this enum to #WM_manipulatormap_draw to tell it what to draw. + */ +typedef enum eWM_ManipulatorMapDrawStep { + /** Draw 2D manipulator-groups (#WM_MANIPULATORGROUPTYPE_3D not set). */ + WM_MANIPULATORMAP_DRAWSTEP_2D = 0, + /** Draw 3D manipulator-groups (#WM_MANIPULATORGROUPTYPE_3D set). */ + WM_MANIPULATORMAP_DRAWSTEP_3D, +} eWM_ManipulatorMapDrawStep; +#define WM_MANIPULATORMAP_DRAWSTEP_MAX 2 + +#endif /* __WM_MANIPULATOR_TYPES_H__ */ diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator.c new file mode 100644 index 00000000000..3df4124ba65 --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator.c @@ -0,0 +1,726 @@ +/* + * ***** 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) 2014 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/manipulators/intern/wm_manipulator.c + * \ingroup wm + */ + +#include "BKE_context.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "GPU_batch.h" +#include "GPU_glew.h" +#include "GPU_immediate.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_idprop.h" + +#include "WM_api.h" +#include "WM_types.h" + +#ifdef WITH_PYTHON +#include "BPY_extern.h" +#endif + +/* only for own init/exit calls (wm_manipulatortype_init/wm_manipulatortype_free) */ +#include "wm.h" + +/* own includes */ +#include "wm_manipulator_wmapi.h" +#include "wm_manipulator_intern.h" + +static void wm_manipulator_register( + wmManipulatorGroup *mgroup, wmManipulator *mpr); + +/** + * \note Follow #wm_operator_create convention. + */ +static wmManipulator *wm_manipulator_create( + const wmManipulatorType *wt, + PointerRNA *properties) +{ + BLI_assert(wt != NULL); + BLI_assert(wt->struct_size >= sizeof(wmManipulator)); + + wmManipulator *mpr = MEM_callocN( + wt->struct_size + (sizeof(wmManipulatorProperty) * wt->target_property_defs_len), __func__); + mpr->type = wt; + + /* initialize properties, either copy or create */ + mpr->ptr = MEM_callocN(sizeof(PointerRNA), "wmManipulatorPtrRNA"); + if (properties && properties->data) { + mpr->properties = IDP_CopyProperty(properties->data); + } + else { + IDPropertyTemplate val = {0}; + mpr->properties = IDP_New(IDP_GROUP, &val, "wmManipulatorProperties"); + } + RNA_pointer_create(G.main->wm.first, wt->srna, mpr->properties, mpr->ptr); + + WM_manipulator_properties_sanitize(mpr->ptr, 0); + + unit_m4(mpr->matrix_space); + unit_m4(mpr->matrix_basis); + unit_m4(mpr->matrix_offset); + + return mpr; +} + +wmManipulator *WM_manipulator_new_ptr( + const wmManipulatorType *wt, wmManipulatorGroup *mgroup, + PointerRNA *properties) +{ + wmManipulator *mpr = wm_manipulator_create(wt, properties); + + wm_manipulator_register(mgroup, mpr); + + if (mpr->type->setup != NULL) { + mpr->type->setup(mpr); + } + + return mpr; +} + +/** + * \param wt: Must be valid, + * if you need to check it exists use #WM_manipulator_new_ptr + * because callers of this function don't NULL check the return value. + */ +wmManipulator *WM_manipulator_new( + const char *idname, wmManipulatorGroup *mgroup, + PointerRNA *properties) +{ + const wmManipulatorType *wt = WM_manipulatortype_find(idname, false); + return WM_manipulator_new_ptr(wt, mgroup, properties); +} + +/** + * Initialize default values and allocate needed memory for members. + */ +static void manipulator_init(wmManipulator *mpr) +{ + const float color_default[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + + mpr->scale_basis = 1.0f; + mpr->line_width = 1.0f; + + /* defaults */ + copy_v4_v4(mpr->color, color_default); + copy_v4_v4(mpr->color_hi, color_default); +} + +/** + * Register \a manipulator. + * + * \param name: name used to create a unique idname for \a manipulator in \a mgroup + * + * \note Not to be confused with type registration from RNA. + */ +static void wm_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr) +{ + manipulator_init(mpr); + wm_manipulatorgroup_manipulator_register(mgroup, mpr); +} + +/** + * \warning this doesn't check #wmManipulatorMap (highlight, selection etc). + * Typical use is when freeing the windowing data, + * where caller can manage clearing selection, highlight... etc. + */ +void WM_manipulator_free(wmManipulator *mpr) +{ +#ifdef WITH_PYTHON + if (mpr->py_instance) { + /* do this first in case there are any __del__ functions or + * similar that use properties */ + BPY_DECREF_RNA_INVALIDATE(mpr->py_instance); + } +#endif + + if (mpr->op_data) { + for (int i = 0; i < mpr->op_data_len; i++) { + WM_operator_properties_free(&mpr->op_data[i].ptr); + } + MEM_freeN(mpr->op_data); + } + + if (mpr->ptr != NULL) { + WM_manipulator_properties_free(mpr->ptr); + MEM_freeN(mpr->ptr); + } + + if (mpr->type->target_property_defs_len != 0) { + wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr); + for (int i = 0; i < mpr->type->target_property_defs_len; i++) { + wmManipulatorProperty *mpr_prop = &mpr_prop_array[i]; + if (mpr_prop->custom_func.free_fn) { + mpr_prop->custom_func.free_fn(mpr, mpr_prop); + } + } + } + + MEM_freeN(mpr); +} + +/** + * Free \a manipulator and unlink from \a manipulatorlist. + * \a manipulatorlist is allowed to be NULL. + */ +void WM_manipulator_unlink(ListBase *manipulatorlist, wmManipulatorMap *mmap, wmManipulator *mpr, bContext *C) +{ + if (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) { + wm_manipulatormap_highlight_set(mmap, C, NULL, 0); + } + if (mpr->state & WM_MANIPULATOR_STATE_MODAL) { + wm_manipulatormap_modal_set(mmap, C, mpr, NULL, false); + } + /* Unlink instead of setting so we don't run callbacks. */ + if (mpr->state & WM_MANIPULATOR_STATE_SELECT) { + WM_manipulator_select_unlink(mmap, mpr); + } + + if (manipulatorlist) { + BLI_remlink(manipulatorlist, mpr); + } + + BLI_assert(mmap->mmap_context.highlight != mpr); + BLI_assert(mmap->mmap_context.modal != mpr); + + WM_manipulator_free(mpr); +} + +/* -------------------------------------------------------------------- */ +/** \name Manipulator Creation API + * + * API for defining data on manipulator creation. + * + * \{ */ + +struct wmManipulatorOpElem *WM_manipulator_operator_get( + wmManipulator *mpr, int part_index) +{ + if (mpr->op_data && ((part_index >= 0) && (part_index < mpr->op_data_len))) { + return &mpr->op_data[part_index]; + } + return NULL; +} + +PointerRNA *WM_manipulator_operator_set( + wmManipulator *mpr, int part_index, + wmOperatorType *ot, IDProperty *properties) +{ + BLI_assert(part_index < 255); + /* We could pre-allocate these but using multiple is such a rare thing. */ + if (part_index >= mpr->op_data_len) { + mpr->op_data_len = part_index + 1; + mpr->op_data = MEM_recallocN(mpr->op_data, sizeof(*mpr->op_data) * mpr->op_data_len); + } + wmManipulatorOpElem *mpop = &mpr->op_data[part_index]; + mpop->type = ot; + + if (mpop->ptr.data) { + WM_operator_properties_free(&mpop->ptr); + } + WM_operator_properties_create_ptr(&mpop->ptr, ot); + + if (properties) { + mpop->ptr.data = properties; + } + + return &mpop->ptr; +} + +static void wm_manipulator_set_matrix_rotation_from_z_axis__internal( + float matrix[4][4], const float z_axis[3]) +{ + /* old code, seems we can use simpler method */ +#if 0 + const float z_global[3] = {0.0f, 0.0f, 1.0f}; + float rot[3][3]; + + rotation_between_vecs_to_mat3(rot, z_global, z_axis); + copy_v3_v3(matrix[0], rot[0]); + copy_v3_v3(matrix[1], rot[1]); + copy_v3_v3(matrix[2], rot[2]); +#else + normalize_v3_v3(matrix[2], z_axis); + ortho_basis_v3v3_v3(matrix[0], matrix[1], matrix[2]); +#endif + +} + +static void wm_manipulator_set_matrix_rotation_from_yz_axis__internal( + float matrix[4][4], const float y_axis[3], const float z_axis[3]) +{ + normalize_v3_v3(matrix[1], y_axis); + normalize_v3_v3(matrix[2], z_axis); + cross_v3_v3v3(matrix[0], matrix[1], matrix[2]); + normalize_v3(matrix[0]); +} + +/** + * wmManipulator.matrix utils. + */ +void WM_manipulator_set_matrix_rotation_from_z_axis( + wmManipulator *mpr, const float z_axis[3]) +{ + wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_basis, z_axis); +} +void WM_manipulator_set_matrix_rotation_from_yz_axis( + wmManipulator *mpr, const float y_axis[3], const float z_axis[3]) +{ + wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_basis, y_axis, z_axis); +} +void WM_manipulator_set_matrix_location(wmManipulator *mpr, const float origin[3]) +{ + copy_v3_v3(mpr->matrix_basis[3], origin); +} + +/** + * wmManipulator.matrix_offset utils. + */ +void WM_manipulator_set_matrix_offset_rotation_from_z_axis( + wmManipulator *mpr, const float z_axis[3]) +{ + wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_offset, z_axis); +} +void WM_manipulator_set_matrix_offset_rotation_from_yz_axis( + wmManipulator *mpr, const float y_axis[3], const float z_axis[3]) +{ + wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_offset, y_axis, z_axis); +} +void WM_manipulator_set_matrix_offset_location(wmManipulator *mpr, const float offset[3]) +{ + copy_v3_v3(mpr->matrix_offset[3], offset); +} + +void WM_manipulator_set_flag(wmManipulator *mpr, const int flag, const bool enable) +{ + if (enable) { + mpr->flag |= flag; + } + else { + mpr->flag &= ~flag; + } +} + +void WM_manipulator_set_scale(wmManipulator *mpr, const float scale) +{ + mpr->scale_basis = scale; +} + +void WM_manipulator_set_line_width(wmManipulator *mpr, const float line_width) +{ + mpr->line_width = line_width; +} + +/** + * Set manipulator rgba colors. + * + * \param col Normal state color. + * \param col_hi Highlighted state color. + */ +void WM_manipulator_get_color(const wmManipulator *mpr, float color[4]) +{ + copy_v4_v4(color, mpr->color); +} +void WM_manipulator_set_color(wmManipulator *mpr, const float color[4]) +{ + copy_v4_v4(mpr->color, color); +} + +void WM_manipulator_get_color_highlight(const wmManipulator *mpr, float color_hi[4]) +{ + copy_v4_v4(color_hi, mpr->color_hi); +} +void WM_manipulator_set_color_highlight(wmManipulator *mpr, const float color_hi[4]) +{ + copy_v4_v4(mpr->color_hi, color_hi); +} + + +/** \} */ // Manipulator Creation API + + +/* -------------------------------------------------------------------- */ +/** \name Manipulator Callback Assignment + * + * \{ */ + +void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulatorFnModal fn) +{ + mpr->custom_modal = fn; +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** + * Add/Remove \a manipulator to selection. + * Reallocates memory for selected manipulators so better not call for selecting multiple ones. + * + * \return if the selection has changed. + */ +bool wm_manipulator_select_set_ex( + wmManipulatorMap *mmap, wmManipulator *mpr, bool select, + bool use_array, bool use_callback) +{ + bool changed = false; + + if (select) { + if ((mpr->state & WM_MANIPULATOR_STATE_SELECT) == 0) { + if (use_array) { + wm_manipulatormap_select_array_push_back(mmap, mpr); + } + mpr->state |= WM_MANIPULATOR_STATE_SELECT; + changed = true; + } + } + else { + if (mpr->state & WM_MANIPULATOR_STATE_SELECT) { + if (use_array) { + wm_manipulatormap_select_array_remove(mmap, mpr); + } + mpr->state &= ~WM_MANIPULATOR_STATE_SELECT; + changed = true; + } + } + + /* In the case of unlinking we only want to remove from the array + * and not write to the external state */ + if (use_callback && changed) { + if (mpr->type->select_refresh) { + mpr->type->select_refresh(mpr); + } + } + + return changed; +} + +/* Remove from selection array without running callbacks. */ +bool WM_manipulator_select_unlink(wmManipulatorMap *mmap, wmManipulator *mpr) +{ + return wm_manipulator_select_set_ex(mmap, mpr, false, true, false); +} + +bool WM_manipulator_select_set(wmManipulatorMap *mmap, wmManipulator *mpr, bool select) +{ + return wm_manipulator_select_set_ex(mmap, mpr, select, true, true); +} + +bool wm_manipulator_select_and_highlight(bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr) +{ + if (WM_manipulator_select_set(mmap, mpr, true)) { + wm_manipulatormap_highlight_set(mmap, C, mpr, mpr->highlight_part); + return true; + } + else { + return false; + } +} + +void wm_manipulator_calculate_scale(wmManipulator *mpr, const bContext *C) +{ + const RegionView3D *rv3d = CTX_wm_region_view3d(C); + float scale = U.ui_scale; + + if ((mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SCALE) == 0) { + scale *= U.manipulator_size; + if (rv3d) { + /* 'ED_view3d_pixel_size' includes 'U.pixelsize', remove it. */ + float matrix_world[4][4]; + if (mpr->type->matrix_basis_get) { + float matrix_basis[4][4]; + mpr->type->matrix_basis_get(mpr, matrix_basis); + mul_m4_m4m4(matrix_world, mpr->matrix_space, matrix_basis); + } + else { + mul_m4_m4m4(matrix_world, mpr->matrix_space, mpr->matrix_basis); + } + + /* Exclude matrix_offset from scale. */ + scale *= ED_view3d_pixel_size(rv3d, matrix_world[3]) / U.pixelsize; + } + else { + scale *= 0.02f; + } + } + + mpr->scale_final = mpr->scale_basis * scale; +} + +static void manipulator_update_prop_data(wmManipulator *mpr) +{ + /* manipulator property might have been changed, so update manipulator */ + if (mpr->type->property_update) { + wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr); + for (int i = 0; i < mpr->type->target_property_defs_len; i++) { + wmManipulatorProperty *mpr_prop = &mpr_prop_array[i]; + if (WM_manipulator_target_property_is_valid(mpr_prop)) { + mpr->type->property_update(mpr, mpr_prop); + } + } + } +} + +void wm_manipulator_update(wmManipulator *mpr, const bContext *C, const bool refresh_map) +{ + if (refresh_map) { + manipulator_update_prop_data(mpr); + } + wm_manipulator_calculate_scale(mpr, C); +} + +int wm_manipulator_is_visible(wmManipulator *mpr) +{ + if (mpr->flag & WM_MANIPULATOR_HIDDEN) { + return 0; + } + if ((mpr->state & WM_MANIPULATOR_STATE_MODAL) && + !(mpr->flag & (WM_MANIPULATOR_DRAW_MODAL | WM_MANIPULATOR_DRAW_VALUE))) + { + /* don't draw while modal (dragging) */ + return 0; + } + if ((mpr->flag & WM_MANIPULATOR_DRAW_HOVER) && + !(mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) && + !(mpr->state & WM_MANIPULATOR_STATE_SELECT)) /* still draw selected manipulators */ + { + /* update but don't draw */ + return WM_MANIPULATOR_IS_VISIBLE_UPDATE; + } + + return WM_MANIPULATOR_IS_VISIBLE_UPDATE | WM_MANIPULATOR_IS_VISIBLE_DRAW; +} + +void WM_manipulator_calc_matrix_final_params( + const wmManipulator *mpr, + const struct WM_ManipulatorMatrixParams *params, + float r_mat[4][4]) +{ + const float (* const matrix_space)[4] = params->matrix_space ? params->matrix_space : mpr->matrix_space; + const float (* const matrix_basis)[4] = params->matrix_basis ? params->matrix_basis : mpr->matrix_basis; + const float (* const matrix_offset)[4] = params->matrix_offset ? params->matrix_offset : mpr->matrix_offset; + const float *scale_final = params->scale_final ? params->scale_final : &mpr->scale_final; + + float final_matrix[4][4]; + if (params->matrix_basis == NULL && mpr->type->matrix_basis_get) { + mpr->type->matrix_basis_get(mpr, final_matrix); + } + else { + copy_m4_m4(final_matrix, matrix_basis); + } + + if (mpr->flag & WM_MANIPULATOR_DRAW_NO_SCALE) { + mul_m4_m4m4(final_matrix, final_matrix, matrix_offset); + } + else { + if (mpr->flag & WM_MANIPULATOR_DRAW_OFFSET_SCALE) { + mul_mat3_m4_fl(final_matrix, *scale_final); + mul_m4_m4m4(final_matrix, final_matrix, matrix_offset); + } + else { + mul_m4_m4m4(final_matrix, final_matrix, matrix_offset); + mul_mat3_m4_fl(final_matrix, *scale_final); + } + } + + mul_m4_m4m4(r_mat, matrix_space, final_matrix); +} + +void WM_manipulator_calc_matrix_final(const wmManipulator *mpr, float r_mat[4][4]) +{ + WM_manipulator_calc_matrix_final_params( + mpr, + &((struct WM_ManipulatorMatrixParams) { + .matrix_space = NULL, + .matrix_basis = NULL, + .matrix_offset = NULL, + .scale_final = NULL, + }), r_mat + ); +} + +/** \name Manipulator Propery Access + * + * Matches `WM_operator_properties` conventions. + * + * \{ */ + + +void WM_manipulator_properties_create_ptr(PointerRNA *ptr, wmManipulatorType *wt) +{ + RNA_pointer_create(NULL, wt->srna, NULL, ptr); +} + +void WM_manipulator_properties_create(PointerRNA *ptr, const char *wtstring) +{ + const wmManipulatorType *wt = WM_manipulatortype_find(wtstring, false); + + if (wt) + WM_manipulator_properties_create_ptr(ptr, (wmManipulatorType *)wt); + else + RNA_pointer_create(NULL, &RNA_ManipulatorProperties, NULL, ptr); +} + +/* similar to the function above except its uses ID properties + * used for keymaps and macros */ +void WM_manipulator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *wtstring) +{ + if (*properties == NULL) { + IDPropertyTemplate val = {0}; + *properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp"); + } + + if (*ptr == NULL) { + *ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr"); + WM_manipulator_properties_create(*ptr, wtstring); + } + + (*ptr)->data = *properties; + +} + +void WM_manipulator_properties_sanitize(PointerRNA *ptr, const bool no_context) +{ + RNA_STRUCT_BEGIN (ptr, prop) + { + switch (RNA_property_type(prop)) { + case PROP_ENUM: + if (no_context) + RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT); + else + RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT); + break; + case PROP_POINTER: + { + StructRNA *ptype = RNA_property_pointer_type(ptr, prop); + + /* recurse into manipulator properties */ + if (RNA_struct_is_a(ptype, &RNA_ManipulatorProperties)) { + PointerRNA opptr = RNA_property_pointer_get(ptr, prop); + WM_manipulator_properties_sanitize(&opptr, no_context); + } + break; + } + default: + break; + } + } + RNA_STRUCT_END; +} + + +/** set all props to their default, + * \param do_update Only update un-initialized props. + * + * \note, theres nothing specific to manipulators here. + * this could be made a general function. + */ +bool WM_manipulator_properties_default(PointerRNA *ptr, const bool do_update) +{ + bool changed = false; + RNA_STRUCT_BEGIN (ptr, prop) + { + switch (RNA_property_type(prop)) { + case PROP_POINTER: + { + StructRNA *ptype = RNA_property_pointer_type(ptr, prop); + if (ptype != &RNA_Struct) { + PointerRNA opptr = RNA_property_pointer_get(ptr, prop); + changed |= WM_manipulator_properties_default(&opptr, do_update); + } + break; + } + default: + if ((do_update == false) || (RNA_property_is_set(ptr, prop) == false)) { + if (RNA_property_reset(ptr, prop, -1)) { + changed = true; + } + } + break; + } + } + RNA_STRUCT_END; + + return changed; +} + +/* remove all props without PROP_SKIP_SAVE */ +void WM_manipulator_properties_reset(wmManipulator *mpr) +{ + if (mpr->ptr->data) { + PropertyRNA *iterprop; + iterprop = RNA_struct_iterator_property(mpr->type->srna); + + RNA_PROP_BEGIN (mpr->ptr, itemptr, iterprop) + { + PropertyRNA *prop = itemptr.data; + + if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) { + const char *identifier = RNA_property_identifier(prop); + RNA_struct_idprops_unset(mpr->ptr, identifier); + } + } + RNA_PROP_END; + } +} + +void WM_manipulator_properties_clear(PointerRNA *ptr) +{ + IDProperty *properties = ptr->data; + + if (properties) { + IDP_ClearProperty(properties); + } +} + +void WM_manipulator_properties_free(PointerRNA *ptr) +{ + IDProperty *properties = ptr->data; + + if (properties) { + IDP_FreeProperty(properties); + MEM_freeN(properties); + ptr->data = NULL; /* just in case */ + } +} + +/** \} */ diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_group.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_group.c new file mode 100644 index 00000000000..e0b12289b9e --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_group.c @@ -0,0 +1,788 @@ +/* + * ***** 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) 2014 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/manipulators/intern/wm_manipulator_group.c + * \ingroup wm + * + * \name Manipulator-Group + * + * Manipulator-groups store and manage groups of manipulators. They can be + * attached to modal handlers and have own keymaps. + */ + +#include <stdlib.h> +#include <string.h> + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "ED_screen.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm_event_system.h" + +/* own includes */ +#include "wm_manipulator_wmapi.h" +#include "wm_manipulator_intern.h" + +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorGroup + * + * \{ */ + +/** + * Create a new manipulator-group from \a wgt. + */ +wmManipulatorGroup *wm_manipulatorgroup_new_from_type( + wmManipulatorMap *mmap, wmManipulatorGroupType *wgt) +{ + wmManipulatorGroup *mgroup = MEM_callocN(sizeof(*mgroup), "manipulator-group"); + mgroup->type = wgt; + + /* keep back-link */ + mgroup->parent_mmap = mmap; + + BLI_addtail(&mmap->groups, mgroup); + + return mgroup; +} + +void wm_manipulatorgroup_free(bContext *C, wmManipulatorGroup *mgroup) +{ + wmManipulatorMap *mmap = mgroup->parent_mmap; + + /* Similar to WM_manipulator_unlink, but only to keep mmap state correct, + * we don't want to run callbacks. */ + if (mmap->mmap_context.highlight && mmap->mmap_context.highlight->parent_mgroup == mgroup) { + wm_manipulatormap_highlight_set(mmap, C, NULL, 0); + } + if (mmap->mmap_context.modal && mmap->mmap_context.modal->parent_mgroup == mgroup) { + wm_manipulatormap_modal_set(mmap, C, mmap->mmap_context.modal, NULL, false); + } + + for (wmManipulator *mpr = mgroup->manipulators.first, *mpr_next; mpr; mpr = mpr_next) { + mpr_next = mpr->next; + if (mmap->mmap_context.select.len) { + WM_manipulator_select_unlink(mmap, mpr); + } + WM_manipulator_free(mpr); + } + BLI_listbase_clear(&mgroup->manipulators); + +#ifdef WITH_PYTHON + if (mgroup->py_instance) { + /* do this first in case there are any __del__ functions or + * similar that use properties */ + BPY_DECREF_RNA_INVALIDATE(mgroup->py_instance); + } +#endif + + if (mgroup->reports && (mgroup->reports->flag & RPT_FREE)) { + BKE_reports_clear(mgroup->reports); + MEM_freeN(mgroup->reports); + } + + if (mgroup->customdata_free) { + mgroup->customdata_free(mgroup->customdata); + } + else { + MEM_SAFE_FREE(mgroup->customdata); + } + + BLI_remlink(&mmap->groups, mgroup); + + MEM_freeN(mgroup); +} + +/** + * Add \a manipulator to \a mgroup and make sure its name is unique within the group. + */ +void wm_manipulatorgroup_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr) +{ + BLI_assert(BLI_findindex(&mgroup->manipulators, mpr) == -1); + BLI_addtail(&mgroup->manipulators, mpr); + mpr->parent_mgroup = mgroup; +} + +wmManipulator *wm_manipulatorgroup_find_intersected_mainpulator( + const wmManipulatorGroup *mgroup, bContext *C, const wmEvent *event, + int *r_part) +{ + for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + if (mpr->type->test_select && (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) { + if ((*r_part = mpr->type->test_select(C, mpr, event)) != -1) { + return mpr; + } + } + } + + return NULL; +} + +/** + * Adds all manipulators of \a mgroup that can be selected to the head of \a listbase. Added items need freeing! + */ +void wm_manipulatorgroup_intersectable_manipulators_to_list(const wmManipulatorGroup *mgroup, ListBase *listbase) +{ + for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + if ((mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) { + if (((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) && mpr->type->draw_select) || + ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0 && mpr->type->test_select)) + { + BLI_addhead(listbase, BLI_genericNodeN(mpr)); + } + } + } +} + +void wm_manipulatorgroup_ensure_initialized(wmManipulatorGroup *mgroup, const bContext *C) +{ + /* prepare for first draw */ + if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0)) { + mgroup->type->setup(C, mgroup); + + /* Not ideal, initialize keymap here, needed for RNA runtime generated manipulators. */ + wmManipulatorGroupType *wgt = mgroup->type; + if (wgt->keymap == NULL) { + wmWindowManager *wm = CTX_wm_manager(C); + wm_manipulatorgrouptype_setup_keymap(wgt, wm->defaultconf); + BLI_assert(wgt->keymap != NULL); + } + mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_SETUP; + } + + /* refresh may be called multiple times, this just ensures its called at least once before we draw. */ + if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_REFRESH) == 0)) { + if (mgroup->type->refresh) { + mgroup->type->refresh(C, mgroup); + } + mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_REFRESH; + } +} + +bool wm_manipulatorgroup_is_visible(const wmManipulatorGroup *mgroup, const bContext *C) +{ + /* Check for poll function, if manipulator-group belongs to an operator, also check if the operator is running. */ + return (!mgroup->type->poll || mgroup->type->poll(C, mgroup->type)); +} + +bool wm_manipulatorgroup_is_visible_in_drawstep( + const wmManipulatorGroup *mgroup, const eWM_ManipulatorMapDrawStep drawstep) +{ + switch (drawstep) { + case WM_MANIPULATORMAP_DRAWSTEP_2D: + return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0; + case WM_MANIPULATORMAP_DRAWSTEP_3D: + return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D); + default: + BLI_assert(0); + return false; + } +} + +bool wm_manipulatorgroup_is_any_selected(const wmManipulatorGroup *mgroup) +{ + if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT) { + for (const wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + if (mpr->state & WM_MANIPULATOR_STATE_SELECT) { + return true; + } + } + } + return false; +} + +/** \} */ + +/** \name Manipulator operators + * + * Basic operators for manipulator interaction with user configurable keymaps. + * + * \{ */ + +static int manipulator_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + ARegion *ar = CTX_wm_region(C); + wmManipulatorMap *mmap = ar->manipulator_map; + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + wmManipulator *highlight = mmap->mmap_context.highlight; + + bool extend = RNA_boolean_get(op->ptr, "extend"); + bool deselect = RNA_boolean_get(op->ptr, "deselect"); + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + + /* deselect all first */ + if (extend == false && deselect == false && toggle == false) { + wm_manipulatormap_deselect_all(mmap); + BLI_assert(msel->items == NULL && msel->len == 0); + UNUSED_VARS_NDEBUG(msel); + } + + if (highlight) { + const bool is_selected = (highlight->state & WM_MANIPULATOR_STATE_SELECT); + bool redraw = false; + + if (toggle) { + /* toggle: deselect if already selected, else select */ + deselect = is_selected; + } + + if (deselect) { + if (is_selected && WM_manipulator_select_set(mmap, highlight, false)) { + redraw = true; + } + } + else if (wm_manipulator_select_and_highlight(C, mmap, highlight)) { + redraw = true; + } + + if (redraw) { + ED_region_tag_redraw(ar); + } + + return OPERATOR_FINISHED; + } + else { + BLI_assert(0); + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + + return OPERATOR_PASS_THROUGH; +} + +void MANIPULATORGROUP_OT_manipulator_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Manipulator Select"; + ot->description = "Select the currently highlighted manipulator"; + ot->idname = "MANIPULATORGROUP_OT_manipulator_select"; + + /* api callbacks */ + ot->invoke = manipulator_select_invoke; + + ot->flag = OPTYPE_UNDO; + + WM_operator_properties_mouse_select(ot); +} + +typedef struct ManipulatorTweakData { + wmManipulatorMap *mmap; + wmManipulator *mpr_modal; + + int init_event; /* initial event type */ + int flag; /* tweak flags */ +} ManipulatorTweakData; + +static void manipulator_tweak_finish(bContext *C, wmOperator *op, const bool cancel) +{ + ManipulatorTweakData *mtweak = op->customdata; + if (mtweak->mpr_modal->type->exit) { + mtweak->mpr_modal->type->exit(C, mtweak->mpr_modal, cancel); + } + wm_manipulatormap_modal_set(mtweak->mmap, C, mtweak->mpr_modal, NULL, false); + MEM_freeN(mtweak); +} + +static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ManipulatorTweakData *mtweak = op->customdata; + wmManipulator *mpr = mtweak->mpr_modal; + + if (mpr == NULL) { + BLI_assert(0); + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + + if (event->type == mtweak->init_event && event->val == KM_RELEASE) { + manipulator_tweak_finish(C, op, false); + return OPERATOR_FINISHED; + } + + + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case TWEAK_MODAL_CANCEL: + manipulator_tweak_finish(C, op, true); + return OPERATOR_CANCELLED; + case TWEAK_MODAL_CONFIRM: + manipulator_tweak_finish(C, op, false); + return OPERATOR_FINISHED; + + case TWEAK_MODAL_PRECISION_ON: + mtweak->flag |= WM_MANIPULATOR_TWEAK_PRECISE; + break; + case TWEAK_MODAL_PRECISION_OFF: + mtweak->flag &= ~WM_MANIPULATOR_TWEAK_PRECISE; + break; + + case TWEAK_MODAL_SNAP_ON: + mtweak->flag |= WM_MANIPULATOR_TWEAK_SNAP; + break; + case TWEAK_MODAL_SNAP_OFF: + mtweak->flag &= ~WM_MANIPULATOR_TWEAK_SNAP; + break; + } + } + + /* handle manipulator */ + wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal; + int retval = modal_fn(C, mpr, event, mtweak->flag); + + if ((retval & OPERATOR_RUNNING_MODAL) == 0) { + manipulator_tweak_finish(C, op, (retval & OPERATOR_CANCELLED) != 0); + return OPERATOR_FINISHED; + } + + /* Ugly hack to send manipulator events */ + ((wmEvent *)event)->type = EVT_MANIPULATOR_UPDATE; + + /* always return PASS_THROUGH so modal handlers + * with manipulators attached can update */ + return OPERATOR_PASS_THROUGH; +} + +static int manipulator_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + wmManipulatorMap *mmap = ar->manipulator_map; + wmManipulator *mpr = mmap->mmap_context.highlight; + + if (!mpr) { + /* wm_handlers_do_intern shouldn't let this happen */ + BLI_assert(0); + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); + } + + + /* activate highlighted manipulator */ + wm_manipulatormap_modal_set(mmap, C, mpr, event, true); + + /* XXX temporary workaround for modal manipulator operator + * conflicting with modal operator attached to manipulator */ + wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part); + if (mpop && mpop->type) { + if (mpop->type->modal) { + return OPERATOR_FINISHED; + } + } + + /* Couldn't start the manipulator. */ + if ((mpr->state & WM_MANIPULATOR_STATE_MODAL) == 0) { + return OPERATOR_PASS_THROUGH; + } + + ManipulatorTweakData *mtweak = MEM_mallocN(sizeof(ManipulatorTweakData), __func__); + + mtweak->init_event = WM_userdef_event_type_from_keymap_type(event->type); + mtweak->mpr_modal = mmap->mmap_context.highlight; + mtweak->mmap = mmap; + mtweak->flag = 0; + + op->customdata = mtweak; + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +void MANIPULATORGROUP_OT_manipulator_tweak(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Manipulator Tweak"; + ot->description = "Tweak the active manipulator"; + ot->idname = "MANIPULATORGROUP_OT_manipulator_tweak"; + + /* api callbacks */ + ot->invoke = manipulator_tweak_invoke; + ot->modal = manipulator_tweak_modal; + + /* TODO(campbell) This causes problems tweaking settings for operators, + * need to find a way to support this. */ +#if 0 + ot->flag = OPTYPE_UNDO; +#endif +} + +/** \} */ // Manipulator operators + + +static wmKeyMap *manipulatorgroup_tweak_modal_keymap(wmKeyConfig *keyconf, const char *mgroupname) +{ + wmKeyMap *keymap; + char name[KMAP_MAX_NAME]; + + static EnumPropertyItem modal_items[] = { + {TWEAK_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, + {TWEAK_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, + {TWEAK_MODAL_PRECISION_ON, "PRECISION_ON", 0, "Enable Precision", ""}, + {TWEAK_MODAL_PRECISION_OFF, "PRECISION_OFF", 0, "Disable Precision", ""}, + {TWEAK_MODAL_SNAP_ON, "SNAP_ON", 0, "Enable Snap", ""}, + {TWEAK_MODAL_SNAP_OFF, "SNAP_OFF", 0, "Disable Snap", ""}, + {0, NULL, 0, NULL, NULL} + }; + + + BLI_snprintf(name, sizeof(name), "%s Tweak Modal Map", mgroupname); + keymap = WM_modalkeymap_get(keyconf, name); + + /* this function is called for each spacetype, only needs to add map once */ + if (keymap && keymap->modal_items) + return NULL; + + keymap = WM_modalkeymap_add(keyconf, name, modal_items); + + + /* items for modal map */ + WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL); + WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL); + + WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM); + + WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON); + WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF); + WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON); + WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF); + + WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON); + WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF); + WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON); + WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF); + + WM_modalkeymap_assign(keymap, "MANIPULATORGROUP_OT_manipulator_tweak"); + + return keymap; +} + +/** + * Common default keymap for manipulator groups + */ +wmKeyMap *WM_manipulatorgroup_keymap_common( + const wmManipulatorGroupType *wgt, wmKeyConfig *config) +{ + /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */ + wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid); + + WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", LEFTMOUSE, KM_PRESS, KM_ANY, 0); + manipulatorgroup_tweak_modal_keymap(config, wgt->name); + + return km; +} + +/** + * Variation of #WM_manipulatorgroup_keymap_common but with keymap items for selection + */ +wmKeyMap *WM_manipulatorgroup_keymap_common_select( + const wmManipulatorGroupType *wgt, wmKeyConfig *config) +{ + /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */ + wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid); + + WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0); + WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", EVT_TWEAK_S, KM_ANY, 0, 0); + manipulatorgroup_tweak_modal_keymap(config, wgt->name); + + wmKeyMapItem *kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "extend", false); + RNA_boolean_set(kmi->ptr, "deselect", false); + RNA_boolean_set(kmi->ptr, "toggle", false); + kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "extend", false); + RNA_boolean_set(kmi->ptr, "deselect", false); + RNA_boolean_set(kmi->ptr, "toggle", true); + + return km; +} + +/** \} */ /* wmManipulatorGroup */ + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorGroupType + * + * \{ */ + +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find_ptr( + struct wmManipulatorMapType *mmap_type, + const wmManipulatorGroupType *wgt) +{ + /* could use hash lookups as operator types do, for now simple search. */ + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; + wgt_ref; + wgt_ref = wgt_ref->next) + { + if (wgt_ref->type == wgt) { + return wgt_ref; + } + } + return NULL; +} + +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find( + struct wmManipulatorMapType *mmap_type, + const char *idname) +{ + /* could use hash lookups as operator types do, for now simple search. */ + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; + wgt_ref; + wgt_ref = wgt_ref->next) + { + if (STREQ(idname, wgt_ref->type->idname)) { + return wgt_ref; + } + } + return NULL; +} + +/** + * Use this for registering manipulators on startup. For runtime, use #WM_manipulatormaptype_group_link_runtime. + */ +wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link( + wmManipulatorMapType *mmap_type, const char *idname) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + BLI_assert(wgt != NULL); + return WM_manipulatormaptype_group_link_ptr(mmap_type, wgt); +} + +wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link_ptr( + wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt) +{ + wmManipulatorGroupTypeRef *wgt_ref = MEM_callocN(sizeof(wmManipulatorGroupTypeRef), "manipulator-group-ref"); + wgt_ref->type = wgt; + BLI_addtail(&mmap_type->grouptype_refs, wgt_ref); + return wgt_ref; +} + +void WM_manipulatormaptype_group_init_runtime_keymap( + const Main *bmain, + wmManipulatorGroupType *wgt) +{ + /* init keymap - on startup there's an extra call to init keymaps for 'permanent' manipulator-groups */ + wm_manipulatorgrouptype_setup_keymap(wgt, ((wmWindowManager *)bmain->wm.first)->defaultconf); +} + +void WM_manipulatormaptype_group_init_runtime( + const Main *bmain, wmManipulatorMapType *mmap_type, + wmManipulatorGroupType *wgt) +{ + /* now create a manipulator for all existing areas */ + for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) { + for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + for (ARegion *ar = lb->first; ar; ar = ar->next) { + wmManipulatorMap *mmap = ar->manipulator_map; + if (mmap && mmap->type == mmap_type) { + wm_manipulatorgroup_new_from_type(mmap, wgt); + + /* just add here, drawing will occur on next update */ + wm_manipulatormap_highlight_set(mmap, NULL, NULL, 0); + ED_region_tag_redraw(ar); + } + } + } + } + } +} + + +/** + * Unlike #WM_manipulatormaptype_group_unlink this doesn't maintain correct state, simply free. + */ +void WM_manipulatormaptype_group_free(wmManipulatorGroupTypeRef *wgt_ref) +{ + MEM_freeN(wgt_ref); +} + +void WM_manipulatormaptype_group_unlink( + bContext *C, Main *bmain, wmManipulatorMapType *mmap_type, + const wmManipulatorGroupType *wgt) +{ + /* Free instances. */ + for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) { + for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + for (ARegion *ar = lb->first; ar; ar = ar->next) { + wmManipulatorMap *mmap = ar->manipulator_map; + if (mmap && mmap->type == mmap_type) { + wmManipulatorGroup *mgroup, *mgroup_next; + for (mgroup = mmap->groups.first; mgroup; mgroup = mgroup_next) { + mgroup_next = mgroup->next; + if (mgroup->type == wgt) { + BLI_assert(mgroup->parent_mmap == mmap); + wm_manipulatorgroup_free(C, mgroup); + ED_region_tag_redraw(ar); + } + } + } + } + } + } + } + + /* Free types. */ + wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt); + if (wgt_ref) { + BLI_remlink(&mmap_type->grouptype_refs, wgt_ref); + WM_manipulatormaptype_group_free(wgt_ref); + } + + /* Note, we may want to keep this keymap for editing */ + WM_keymap_remove(wgt->keyconf, wgt->keymap); + + BLI_assert(WM_manipulatormaptype_group_find_ptr(mmap_type, wgt) == NULL); +} + +void wm_manipulatorgrouptype_setup_keymap( + wmManipulatorGroupType *wgt, wmKeyConfig *keyconf) +{ + /* Use flag since setup_keymap may return NULL, + * in that case we better not keep calling it. */ + if (wgt->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) { + wgt->keymap = wgt->setup_keymap(wgt, keyconf); + wgt->keyconf = keyconf; + wgt->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT; + } +} + +/** \} */ /* wmManipulatorGroupType */ + +/* -------------------------------------------------------------------- */ +/** \name High Level Add/Remove API + * + * For use directly from operators & RNA registration. + * + * \note In context of manipulator API these names are a bit misleading, + * but for general use terms its OK. + * `WM_manipulator_group_type_add` would be more correctly called: + * `WM_manipulatormaptype_grouptype_reference_link` + * but for general purpose API this is too detailed & annoying. + * + * \note We may want to return a value if there is nothing to remove. + * + * \{ */ + +void WM_manipulator_group_type_add_ptr_ex( + wmManipulatorGroupType *wgt, + wmManipulatorMapType *mmap_type) +{ + WM_manipulatormaptype_group_link_ptr(mmap_type, wgt); + + WM_manipulatorconfig_update_tag_init(mmap_type, wgt); +} +void WM_manipulator_group_type_add_ptr( + wmManipulatorGroupType *wgt) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params); + WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type); +} +void WM_manipulator_group_type_add(const char *idname) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + BLI_assert(wgt != NULL); + WM_manipulator_group_type_add_ptr(wgt); +} + +void WM_manipulator_group_type_ensure_ptr_ex( + wmManipulatorGroupType *wgt, + wmManipulatorMapType *mmap_type) +{ + wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt); + if (wgt_ref == NULL) { + WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type); + } +} +void WM_manipulator_group_type_ensure_ptr( + wmManipulatorGroupType *wgt) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params); + WM_manipulator_group_type_ensure_ptr_ex(wgt, mmap_type); +} +void WM_manipulator_group_type_ensure(const char *idname) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + BLI_assert(wgt != NULL); + WM_manipulator_group_type_ensure_ptr(wgt); +} + +void WM_manipulator_group_type_remove_ptr_ex( + struct Main *bmain, wmManipulatorGroupType *wgt, + wmManipulatorMapType *mmap_type) +{ + WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt); + WM_manipulatorgrouptype_free_ptr(wgt); +} +void WM_manipulator_group_type_remove_ptr( + struct Main *bmain, wmManipulatorGroupType *wgt) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params); + WM_manipulator_group_type_remove_ptr_ex(bmain, wgt, mmap_type); +} +void WM_manipulator_group_type_remove(struct Main *bmain, const char *idname) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + BLI_assert(wgt != NULL); + WM_manipulator_group_type_remove_ptr(bmain, wgt); +} + +/* delayed versions */ + +void WM_manipulator_group_type_unlink_delayed_ptr_ex( + wmManipulatorGroupType *wgt, + wmManipulatorMapType *mmap_type) +{ + WM_manipulatorconfig_update_tag_remove(mmap_type, wgt); +} + +void WM_manipulator_group_type_unlink_delayed_ptr( + wmManipulatorGroupType *wgt) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params); + WM_manipulator_group_type_unlink_delayed_ptr_ex(wgt, mmap_type); +} + +void WM_manipulator_group_type_unlink_delayed(const char *idname) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false); + BLI_assert(wgt != NULL); + WM_manipulator_group_type_unlink_delayed_ptr(wgt); +} + +/** \} */ diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_group_type.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_group_type.c new file mode 100644 index 00000000000..4c17ffda09c --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_group_type.c @@ -0,0 +1,197 @@ +/* + * ***** 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/manipulators/intern/wm_manipulator_group_type.c + * \ingroup wm + */ + +#include "BKE_context.h" + +#include "BLI_ghash.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* only for own init/exit calls (wm_manipulatorgrouptype_init/wm_manipulatorgrouptype_free) */ +#include "wm.h" + +/* own includes */ +#include "wm_manipulator_wmapi.h" +#include "wm_manipulator_intern.h" + + +/** \name ManipulatorGroup Type Append + * + * \note This follows conventions from #WM_operatortype_find #WM_operatortype_append & friends. + * \{ */ + +static GHash *global_manipulatorgrouptype_hash = NULL; + +wmManipulatorGroupType *WM_manipulatorgrouptype_find(const char *idname, bool quiet) +{ + if (idname[0]) { + wmManipulatorGroupType *wgt; + + wgt = BLI_ghash_lookup(global_manipulatorgrouptype_hash, idname); + if (wgt) { + return wgt; + } + + if (!quiet) { + printf("search for unknown manipulator group '%s'\n", idname); + } + } + else { + if (!quiet) { + printf("search for empty manipulator group\n"); + } + } + + return NULL; +} + +/* caller must free */ +void WM_manipulatorgrouptype_iter(GHashIterator *ghi) +{ + BLI_ghashIterator_init(ghi, global_manipulatorgrouptype_hash); +} + +static wmManipulatorGroupType *wm_manipulatorgrouptype_append__begin(void) +{ + wmManipulatorGroupType *wgt = MEM_callocN(sizeof(wmManipulatorGroupType), "manipulatorgrouptype"); + + return wgt; +} +static void wm_manipulatorgrouptype_append__end(wmManipulatorGroupType *wgt) +{ + BLI_assert(wgt->name != NULL); + BLI_assert(wgt->idname != NULL); + + wgt->type_update_flag |= WM_MANIPULATORMAPTYPE_KEYMAP_INIT; + + /* if not set, use default */ + if (wgt->setup_keymap == NULL) { + if (wgt->flag & WM_MANIPULATORGROUPTYPE_SELECT) { + wgt->setup_keymap = WM_manipulatorgroup_keymap_common_select; + } + else { + wgt->setup_keymap = WM_manipulatorgroup_keymap_common; + } + } + + BLI_ghash_insert(global_manipulatorgrouptype_hash, (void *)wgt->idname, wgt); +} + +wmManipulatorGroupType *WM_manipulatorgrouptype_append( + void (*wtfunc)(struct wmManipulatorGroupType *)) +{ + wmManipulatorGroupType *wgt = wm_manipulatorgrouptype_append__begin(); + wtfunc(wgt); + wm_manipulatorgrouptype_append__end(wgt); + return wgt; +} + +wmManipulatorGroupType *WM_manipulatorgrouptype_append_ptr( + void (*wtfunc)(struct wmManipulatorGroupType *, void *), void *userdata) +{ + wmManipulatorGroupType *wgt = wm_manipulatorgrouptype_append__begin(); + wtfunc(wgt, userdata); + wm_manipulatorgrouptype_append__end(wgt); + return wgt; +} + +/** + * Append and insert into a manipulator typemap. + * This is most common for C manipulators which are enabled by default. + */ +wmManipulatorGroupTypeRef *WM_manipulatorgrouptype_append_and_link( + wmManipulatorMapType *mmap_type, + void (*wtfunc)(struct wmManipulatorGroupType *)) +{ + wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_append(wtfunc); + + wgt->mmap_params.spaceid = mmap_type->spaceid; + wgt->mmap_params.regionid = mmap_type->regionid; + + return WM_manipulatormaptype_group_link_ptr(mmap_type, wgt); +} + +/** + * Free but don't remove from ghash. + */ +static void manipulatorgrouptype_free(wmManipulatorGroupType *wgt) +{ + if (wgt->ext.srna) { /* python manipulator group, allocs own string */ + MEM_freeN((void *)wgt->idname); + } + + MEM_freeN(wgt); +} + +void WM_manipulatorgrouptype_free_ptr(wmManipulatorGroupType *wgt) +{ + BLI_assert(wgt == WM_manipulatorgrouptype_find(wgt->idname, false)); + + BLI_ghash_remove(global_manipulatorgrouptype_hash, wgt->idname, NULL, NULL); + + manipulatorgrouptype_free(wgt); + + /* XXX, TODO, update the world! */ +} + +bool WM_manipulatorgrouptype_free(const char *idname) +{ + wmManipulatorGroupType *wgt = BLI_ghash_lookup(global_manipulatorgrouptype_hash, idname); + + if (wgt == NULL) { + return false; + } + + WM_manipulatorgrouptype_free_ptr(wgt); + + return true; +} + +static void wm_manipulatorgrouptype_ghash_free_cb(wmManipulatorGroupType *mt) +{ + manipulatorgrouptype_free(mt); +} + +void wm_manipulatorgrouptype_free(void) +{ + BLI_ghash_free(global_manipulatorgrouptype_hash, NULL, (GHashValFreeFP)wm_manipulatorgrouptype_ghash_free_cb); + global_manipulatorgrouptype_hash = NULL; +} + +/* called on initialize WM_init() */ +void wm_manipulatorgrouptype_init(void) +{ + /* reserve size is set based on blender default setup */ + global_manipulatorgrouptype_hash = BLI_ghash_str_new_ex("wm_manipulatorgrouptype_init gh", 128); +} + +/** \} */ + diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h b/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h new file mode 100644 index 00000000000..bf5c38b9e39 --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h @@ -0,0 +1,140 @@ +/* + * ***** 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. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/manipulators/intern/wm_manipulator_intern.h + * \ingroup wm + */ + + +#ifndef __WM_MANIPULATOR_INTERN_H__ +#define __WM_MANIPULATOR_INTERN_H__ + +struct wmKeyConfig; +struct wmManipulatorMap; +struct ManipulatorGeomInfo; +struct GHashIterator; + +#include "wm_manipulator_fn.h" + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + + +bool wm_manipulator_select_set_ex( + struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select, + bool use_array, bool use_callback); +bool wm_manipulator_select_and_highlight(bContext *C, struct wmManipulatorMap *mmap, struct wmManipulator *mpr); + +void wm_manipulator_calculate_scale(struct wmManipulator *mpr, const bContext *C); +void wm_manipulator_update(struct wmManipulator *mpr, const bContext *C, const bool refresh_map); + +int wm_manipulator_is_visible(struct wmManipulator *mpr); +enum { + WM_MANIPULATOR_IS_VISIBLE_UPDATE = (1 << 0), + WM_MANIPULATOR_IS_VISIBLE_DRAW = (1 << 1), +}; + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +enum { + TWEAK_MODAL_CANCEL = 1, + TWEAK_MODAL_CONFIRM, + TWEAK_MODAL_PRECISION_ON, + TWEAK_MODAL_PRECISION_OFF, + TWEAK_MODAL_SNAP_ON, + TWEAK_MODAL_SNAP_OFF, +}; + +struct wmManipulatorGroup *wm_manipulatorgroup_new_from_type( + struct wmManipulatorMap *mmap, struct wmManipulatorGroupType *wgt); +void wm_manipulatorgroup_free(bContext *C, struct wmManipulatorGroup *mgroup); +void wm_manipulatorgroup_manipulator_register(struct wmManipulatorGroup *mgroup, struct wmManipulator *mpr); +struct wmManipulator *wm_manipulatorgroup_find_intersected_mainpulator( + const struct wmManipulatorGroup *mgroup, struct bContext *C, const struct wmEvent *event, + int *r_part); +void wm_manipulatorgroup_intersectable_manipulators_to_list( + const struct wmManipulatorGroup *mgroup, struct ListBase *listbase); +void wm_manipulatorgroup_ensure_initialized(struct wmManipulatorGroup *mgroup, const struct bContext *C); +bool wm_manipulatorgroup_is_visible(const struct wmManipulatorGroup *mgroup, const struct bContext *C); +bool wm_manipulatorgroup_is_visible_in_drawstep( + const struct wmManipulatorGroup *mgroup, const eWM_ManipulatorMapDrawStep drawstep); + +void wm_manipulatorgrouptype_setup_keymap( + struct wmManipulatorGroupType *wgt, struct wmKeyConfig *keyconf); + + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +typedef struct wmManipulatorMapSelectState { + struct wmManipulator **items; + int len, len_alloc; +} wmManipulatorMapSelectState; + +struct wmManipulatorMap { + + struct wmManipulatorMapType *type; + ListBase groups; /* wmManipulatorGroup */ + + /* private, update tagging (enum defined in C source). */ + char update_flag[WM_MANIPULATORMAP_DRAWSTEP_MAX]; + + /** + * \brief Manipulator map runtime context + * + * Contains information about this manipulator-map. Currently + * highlighted manipulator, currently selected manipulators, ... + */ + struct { + /* we redraw the manipulator-map when this changes */ + struct wmManipulator *highlight; + /* User has clicked this manipulator and it gets all input. */ + struct wmManipulator *modal; + /* array for all selected manipulators */ + struct wmManipulatorMapSelectState select; + } mmap_context; +}; + +/** + * This is a container for all manipulator types that can be instantiated in a region. + * (similar to dropboxes). + * + * \note There is only ever one of these for every (area, region) combination. + */ +struct wmManipulatorMapType { + struct wmManipulatorMapType *next, *prev; + short spaceid, regionid; + /* types of manipulator-groups for this manipulator-map type */ + ListBase grouptype_refs; + + /* eManipulatorMapTypeUpdateFlags */ + eWM_ManipulatorMapTypeUpdateFlag type_update_flag; +}; + +void wm_manipulatormap_select_array_clear(struct wmManipulatorMap *mmap); +bool wm_manipulatormap_deselect_all(struct wmManipulatorMap *mmap); +void wm_manipulatormap_select_array_shrink(struct wmManipulatorMap *mmap, int len_subtract); +void wm_manipulatormap_select_array_push_back(struct wmManipulatorMap *mmap, wmManipulator *mpr); +void wm_manipulatormap_select_array_remove(struct wmManipulatorMap *mmap, wmManipulator *mpr); + +#endif diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c new file mode 100644 index 00000000000..1d8ee35cd32 --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c @@ -0,0 +1,1094 @@ +/* + * ***** 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) 2014 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/manipulators/intern/wm_manipulator_map.c + * \ingroup wm + */ + +#include <string.h> + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_ghash.h" + +#include "BKE_context.h" +#include "BKE_global.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "GPU_glew.h" +#include "GPU_matrix.h" +#include "GPU_select.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm_event_system.h" + +#include "DEG_depsgraph.h" + +/* own includes */ +#include "wm_manipulator_wmapi.h" +#include "wm_manipulator_intern.h" + +/** + * Store all manipulator-maps here. Anyone who wants to register a manipulator for a certain + * area type can query the manipulator-map to do so. + */ +static ListBase manipulatormaptypes = {NULL, NULL}; + +/** + * Update when manipulator-map types change. + */ +/* so operator removal can trigger update */ +typedef enum eWM_ManipulatorGroupTypeGlobalFlag { + WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT = (1 << 0), + WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE = (1 << 1), +} eWM_ManipulatorGroupTypeGlobalFlag; + +static eWM_ManipulatorGroupTypeGlobalFlag wm_mmap_type_update_flag = 0; + +/** + * Manipulator-map update tagging. + */ +enum { + /** #manipulatormap_prepare_drawing has run */ + MANIPULATORMAP_IS_PREPARE_DRAW = (1 << 0), + MANIPULATORMAP_IS_REFRESH_CALLBACK = (1 << 1), +}; + + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorMap Selection Array API + * + * Just handle ``wm_manipulatormap_select_array_*``, not flags or callbacks. + * + * \{ */ + +static void wm_manipulatormap_select_array_ensure_len_alloc(wmManipulatorMap *mmap, int len) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + if (len <= msel->len_alloc) { + return; + } + msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * len); + msel->len_alloc = len; +} + +void wm_manipulatormap_select_array_clear(wmManipulatorMap *mmap) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + MEM_SAFE_FREE(msel->items); + msel->len = 0; + msel->len_alloc = 0; +} + +void wm_manipulatormap_select_array_shrink(wmManipulatorMap *mmap, int len_subtract) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + msel->len -= len_subtract; + if (msel->len <= 0) { + wm_manipulatormap_select_array_clear(mmap); + } + else { + if (msel->len < msel->len_alloc / 2) { + msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len); + msel->len_alloc = msel->len; + } + } +} + +void wm_manipulatormap_select_array_push_back(wmManipulatorMap *mmap, wmManipulator *mpr) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + BLI_assert(msel->len <= msel->len_alloc); + if (msel->len == msel->len_alloc) { + msel->len_alloc = (msel->len + 1) * 2; + msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len_alloc); + } + msel->items[msel->len++] = mpr; +} + +void wm_manipulatormap_select_array_remove(wmManipulatorMap *mmap, wmManipulator *mpr) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + /* remove manipulator from selected_manipulators array */ + for (int i = 0; i < msel->len; i++) { + if (msel->items[i] == mpr) { + for (int j = i; j < (msel->len - 1); j++) { + msel->items[j] = msel->items[j + 1]; + } + wm_manipulatormap_select_array_shrink(mmap, 1); + break; + } + } + +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorMap + * + * \{ */ + +/** + * Creates a manipulator-map with all registered manipulators for that type + */ +wmManipulatorMap *WM_manipulatormap_new_from_type( + const struct wmManipulatorMapType_Params *mmap_params) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(mmap_params); + wmManipulatorMap *mmap; + + mmap = MEM_callocN(sizeof(wmManipulatorMap), "ManipulatorMap"); + mmap->type = mmap_type; + WM_manipulatormap_tag_refresh(mmap); + + /* create all manipulator-groups for this manipulator-map. We may create an empty one + * too in anticipation of manipulators from operators etc */ + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) { + wm_manipulatorgroup_new_from_type(mmap, wgt_ref->type); + } + + return mmap; +} + +void wm_manipulatormap_remove(wmManipulatorMap *mmap) +{ + /* Clear first so further calls don't waste time trying to maintain correct array state. */ + wm_manipulatormap_select_array_clear(mmap); + + for (wmManipulatorGroup *mgroup = mmap->groups.first, *mgroup_next; mgroup; mgroup = mgroup_next) { + mgroup_next = mgroup->next; + BLI_assert(mgroup->parent_mmap == mmap); + wm_manipulatorgroup_free(NULL, mgroup); + } + BLI_assert(BLI_listbase_is_empty(&mmap->groups)); + + MEM_freeN(mmap); +} + +const ListBase *WM_manipulatormap_group_list(wmManipulatorMap *mmap) +{ + return &mmap->groups; +} + +bool WM_manipulatormap_is_any_selected(const wmManipulatorMap *mmap) +{ + return mmap->mmap_context.select.len != 0; +} + +/** + * \note We could use a callback to define bounds, for now just use matrix location. + */ +bool WM_manipulatormap_minmax( + const wmManipulatorMap *mmap, bool UNUSED(use_hidden), bool use_select, + float r_min[3], float r_max[3]) +{ + if (use_select) { + int i; + for (i = 0; i < mmap->mmap_context.select.len; i++) { + minmax_v3v3_v3(r_min, r_max, mmap->mmap_context.select.items[i]->matrix_basis[3]); + } + return i != 0; + } + else { + bool ok = false; + BLI_assert(!"TODO"); + return ok; + } +} + +/** + * Creates and returns idname hash table for (visible) manipulators in \a mmap + * + * \param poll Polling function for excluding manipulators. + * \param data Custom data passed to \a poll + * + * TODO(campbell): this uses unreliable order, + * best we use an iterator function instead of a hash. + */ +static GHash *WM_manipulatormap_manipulator_hash_new( + const bContext *C, wmManipulatorMap *mmap, + bool (*poll)(const wmManipulator *, void *), + void *data, const bool include_hidden) +{ + GHash *hash = BLI_ghash_ptr_new(__func__); + + /* collect manipulators */ + for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) { + if (!mgroup->type->poll || mgroup->type->poll(C, mgroup->type)) { + for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + if ((include_hidden || (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) && + (!poll || poll(mpr, data))) + { + BLI_ghash_insert(hash, mpr, mpr); + } + } + } + } + + return hash; +} + +void WM_manipulatormap_tag_refresh(wmManipulatorMap *mmap) +{ + if (mmap) { + /* We might want only to refresh some, for tag all steps. */ + for (int i = 0; i < WM_MANIPULATORMAP_DRAWSTEP_MAX; i++) { + mmap->update_flag[i] |= ( + MANIPULATORMAP_IS_PREPARE_DRAW | + MANIPULATORMAP_IS_REFRESH_CALLBACK); + } + } +} + +static bool manipulator_prepare_drawing( + wmManipulatorMap *mmap, wmManipulator *mpr, + const bContext *C, ListBase *draw_manipulators, + const eWM_ManipulatorMapDrawStep drawstep) +{ + int do_draw = wm_manipulator_is_visible(mpr); + if (do_draw == 0) { + /* skip */ + } + else { + if (do_draw & WM_MANIPULATOR_IS_VISIBLE_UPDATE) { + /* hover manipulators need updating, even if we don't draw them */ + wm_manipulator_update(mpr, C, (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_PREPARE_DRAW) != 0); + } + if (do_draw & WM_MANIPULATOR_IS_VISIBLE_DRAW) { + BLI_addhead(draw_manipulators, BLI_genericNodeN(mpr)); + } + return true; + } + + return false; +} + +/** + * Update manipulators of \a mmap to prepare for drawing. Adds all manipulators that + * should be drawn to list \a draw_manipulators, note that added items need freeing. + */ +static void manipulatormap_prepare_drawing( + wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators, + const eWM_ManipulatorMapDrawStep drawstep) +{ + if (!mmap || BLI_listbase_is_empty(&mmap->groups)) + return; + wmManipulator *mpr_modal = mmap->mmap_context.modal; + + /* only active manipulator needs updating */ + if (mpr_modal) { + if ((mpr_modal->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL) == 0) { + if (wm_manipulatorgroup_is_visible_in_drawstep(mpr_modal->parent_mgroup, drawstep)) { + if (manipulator_prepare_drawing(mmap, mpr_modal, C, draw_manipulators, drawstep)) { + mmap->update_flag[drawstep] &= ~MANIPULATORMAP_IS_PREPARE_DRAW; + } + } + /* don't draw any other manipulators */ + return; + } + } + + for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) { + /* check group visibility - drawstep first to avoid unnecessary call of group poll callback */ + if (!wm_manipulatorgroup_is_visible_in_drawstep(mgroup, drawstep) || + !wm_manipulatorgroup_is_visible(mgroup, C)) + { + continue; + } + + /* needs to be initialized on first draw */ + /* XXX weak: Manipulator-group may skip refreshing if it's invisible (map gets untagged nevertheless) */ + if (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_REFRESH_CALLBACK) { + /* force refresh again. */ + mgroup->init_flag &= ~WM_MANIPULATORGROUP_INIT_REFRESH; + } + /* Calls `setup`, `setup_keymap` and `refresh` if they're defined. */ + wm_manipulatorgroup_ensure_initialized(mgroup, C); + + /* prepare drawing */ + if (mgroup->type->draw_prepare) { + mgroup->type->draw_prepare(C, mgroup); + } + + for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) { + manipulator_prepare_drawing(mmap, mpr, C, draw_manipulators, drawstep); + } + } + + mmap->update_flag[drawstep] &= + ~(MANIPULATORMAP_IS_REFRESH_CALLBACK | + MANIPULATORMAP_IS_PREPARE_DRAW); +} + +/** + * Draw all visible manipulators in \a mmap. + * Uses global draw_manipulators listbase. + */ +static void manipulators_draw_list(const wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators) +{ + /* Can be empty if we're dynamically added and removed. */ + if ((mmap == NULL) || BLI_listbase_is_empty(&mmap->groups)) { + return; + } + + const bool draw_multisample = (U.ogl_multisamples != USER_MULTISAMPLE_NONE); + + /* TODO this will need it own shader probably? don't think it can be handled from that point though. */ +/* const bool use_lighting = (U.manipulator_flag & V3D_MANIPULATOR_SHADED) != 0; */ + + /* enable multisampling */ + if (draw_multisample) { + glEnable(GL_MULTISAMPLE); + } + + bool is_depth_prev = false; + + /* draw_manipulators contains all visible manipulators - draw them */ + for (LinkData *link = draw_manipulators->first, *link_next; link; link = link_next) { + wmManipulator *mpr = link->data; + link_next = link->next; + + bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0; + + /* Weak! since we don't 100% support depth yet (select ignores depth) always show highlighted */ + if (is_depth && (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT)) { + is_depth = false; + } + + if (is_depth == is_depth_prev) { + /* pass */ + } + else { + if (is_depth) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + is_depth_prev = is_depth; + } + + mpr->type->draw(C, mpr); + /* free/remove manipulator link after drawing */ + BLI_freelinkN(draw_manipulators, link); + } + + if (is_depth_prev) { + glDisable(GL_DEPTH_TEST); + } + + if (draw_multisample) { + glDisable(GL_MULTISAMPLE); + } +} + +void WM_manipulatormap_draw( + wmManipulatorMap *mmap, const bContext *C, + const eWM_ManipulatorMapDrawStep drawstep) +{ + ListBase draw_manipulators = {NULL}; + + manipulatormap_prepare_drawing(mmap, C, &draw_manipulators, drawstep); + manipulators_draw_list(mmap, C, &draw_manipulators); + BLI_assert(BLI_listbase_is_empty(&draw_manipulators)); +} + +static void manipulator_draw_select_3D_loop(const bContext *C, ListBase *visible_manipulators) +{ + int select_id = 0; + wmManipulator *mpr; + + /* TODO(campbell): this depends on depth buffer being written to, currently broken for the 3D view. */ + bool is_depth_prev = false; + + for (LinkData *link = visible_manipulators->first; link; link = link->next) { + mpr = link->data; + + bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0; + if (is_depth == is_depth_prev) { + /* pass */ + } + else { + if (is_depth) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + is_depth_prev = is_depth; + } + + /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected manipulator part id */ + + mpr->type->draw_select(C, mpr, select_id << 8); + + + select_id++; + } + + if (is_depth_prev) { + glDisable(GL_DEPTH_TEST); + } +} + +static int manipulator_find_intersected_3d_intern( + ListBase *visible_manipulators, const bContext *C, const int co[2], + const int hotspot) +{ + EvaluationContext eval_ctx; + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + View3D *v3d = sa->spacedata.first; + rcti rect; + /* Almost certainly overkill, but allow for many custom manipulators. */ + GLuint buffer[MAXPICKBUF]; + short hits; + const bool do_passes = GPU_select_query_check_active(); + + BLI_rcti_init_pt_radius(&rect, co, hotspot); + + CTX_data_eval_ctx(C, &eval_ctx); + + ED_view3d_draw_setup_view(CTX_wm_window(C), &eval_ctx, CTX_data_scene(C), ar, v3d, NULL, NULL, &rect); + + if (do_passes) + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0); + else + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_ALL, 0); + /* do the drawing */ + manipulator_draw_select_3D_loop(C, visible_manipulators); + + hits = GPU_select_end(); + + if (do_passes && (hits > 0)) { + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits); + manipulator_draw_select_3D_loop(C, visible_manipulators); + GPU_select_end(); + } + + ED_view3d_draw_setup_view(CTX_wm_window(C), &eval_ctx, CTX_data_scene(C), ar, v3d, NULL, NULL, NULL); + + const GLuint *hit_near = GPU_select_buffer_near(buffer, hits); + + return hit_near ? hit_near[3] : -1; +} + +/** + * Try to find a 3D manipulator at screen-space coordinate \a co. Uses OpenGL picking. + */ +static wmManipulator *manipulator_find_intersected_3d( + bContext *C, const int co[2], ListBase *visible_manipulators, + int *r_part) +{ + wmManipulator *result = NULL; + int hit = -1; + + int hotspot_radii[] = { + 3 * U.pixelsize, +#if 0 /* We may want to enable when selection doesn't run on mousemove! */ + 7 * U.pixelsize, +#endif + }; + + *r_part = 0; + /* set up view matrices */ + view3d_operator_needs_opengl(C); + + hit = -1; + + for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) { + hit = manipulator_find_intersected_3d_intern(visible_manipulators, C, co, hotspot_radii[i]); + if (hit != -1) { + break; + } + } + + if (hit != -1) { + LinkData *link = BLI_findlink(visible_manipulators, hit >> 8); + if (link != NULL) { + *r_part = hit & 255; + result = link->data; + } + else { + /* All manipulators should use selection ID they're given as part of the callback, + * if they don't it will attempt tp lookup non-existing index. */ + BLI_assert(0); + } + } + + return result; +} + +/** + * Try to find a manipulator under the mouse position. 2D intersections have priority over + * 3D ones (could check for smallest screen-space distance but not needed right now). + */ +wmManipulator *wm_manipulatormap_highlight_find( + wmManipulatorMap *mmap, bContext *C, const wmEvent *event, + int *r_part) +{ + wmManipulator *mpr = NULL; + ListBase visible_3d_manipulators = {NULL}; + + for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) { + + /* If it were important we could initialize here, + * but this only happens when events are handled before drawing, + * just skip to keep code-path for initializing manipulators simple. */ + if ((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0) { + continue; + } + + if (wm_manipulatorgroup_is_visible(mgroup, C)) { + if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) { + if ((mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_3D] & MANIPULATORMAP_IS_REFRESH_CALLBACK) && + mgroup->type->refresh) + { + mgroup->type->refresh(C, mgroup); + /* cleared below */ + } + wm_manipulatorgroup_intersectable_manipulators_to_list(mgroup, &visible_3d_manipulators); + } + else { + if ((mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_2D] & MANIPULATORMAP_IS_REFRESH_CALLBACK) && + mgroup->type->refresh) + { + mgroup->type->refresh(C, mgroup); + /* cleared below */ + } + + if ((mpr = wm_manipulatorgroup_find_intersected_mainpulator(mgroup, C, event, r_part))) { + break; + } + } + } + } + + if (!BLI_listbase_is_empty(&visible_3d_manipulators)) { + /* 2D manipulators get priority. */ + if (mpr == NULL) { + mpr = manipulator_find_intersected_3d(C, event->mval, &visible_3d_manipulators, r_part); + } + BLI_freelistN(&visible_3d_manipulators); + } + + mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_3D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK; + mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_2D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK; + + return mpr; +} + +void WM_manipulatormap_add_handlers(ARegion *ar, wmManipulatorMap *mmap) +{ + wmEventHandler *handler; + + for (handler = ar->handlers.first; handler; handler = handler->next) { + if (handler->manipulator_map == mmap) { + return; + } + } + + handler = MEM_callocN(sizeof(wmEventHandler), "manipulator handler"); + + BLI_assert(mmap == ar->manipulator_map); + handler->manipulator_map = mmap; + BLI_addtail(&ar->handlers, handler); +} + +void wm_manipulatormaps_handled_modal_update( + bContext *C, wmEvent *event, wmEventHandler *handler) +{ + const bool modal_running = (handler->op != NULL); + + /* happens on render or when joining areas */ + if (!handler->op_region || !handler->op_region->manipulator_map) { + return; + } + + wmManipulatorMap *mmap = handler->op_region->manipulator_map; + wmManipulator *mpr = wm_manipulatormap_modal_get(mmap); + ScrArea *area = CTX_wm_area(C); + ARegion *region = CTX_wm_region(C); + + wm_manipulatormap_handler_context(C, handler); + + /* regular update for running operator */ + if (modal_running) { + wmManipulatorOpElem *mpop = mpr ? WM_manipulator_operator_get(mpr, mpr->highlight_part) : NULL; + if (mpr && mpop && (mpop->type != NULL) && (mpop->type == handler->op->type)) { + wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal; + if (modal_fn != NULL) { + int retval = modal_fn(C, mpr, event, 0); + /* The manipulator is tried to the operator, we can't choose when to exit. */ + BLI_assert(retval & OPERATOR_RUNNING_MODAL); + UNUSED_VARS_NDEBUG(retval); + } + } + } + /* operator not running anymore */ + else { + wm_manipulatormap_highlight_set(mmap, C, NULL, 0); + if (mpr) { + /* This isn't defined if it ends because of success of cancel, we may want to change. */ + bool cancel = true; + if (mpr->type->exit) { + mpr->type->exit(C, mpr, cancel); + } + wm_manipulatormap_modal_set(mmap, C, mpr, NULL, false); + } + } + + /* restore the area */ + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); +} + +/** + * Deselect all selected manipulators in \a mmap. + * \return if selection has changed. + */ +bool wm_manipulatormap_deselect_all(wmManipulatorMap *mmap) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + + if (msel->items == NULL || msel->len == 0) { + return false; + } + + for (int i = 0; i < msel->len; i++) { + wm_manipulator_select_set_ex(mmap, msel->items[i], false, false, true); + } + + wm_manipulatormap_select_array_clear(mmap); + + /* always return true, we already checked + * if there's anything to deselect */ + return true; +} + +BLI_INLINE bool manipulator_selectable_poll(const wmManipulator *mpr, void *UNUSED(data)) +{ + return (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT); +} + +/** + * Select all selectable manipulators in \a mmap. + * \return if selection has changed. + */ +static bool wm_manipulatormap_select_all_intern( + bContext *C, wmManipulatorMap *mmap) +{ + wmManipulatorMapSelectState *msel = &mmap->mmap_context.select; + /* GHash is used here to avoid having to loop over all manipulators twice (once to + * get tot_sel for allocating, once for actually selecting). Instead we collect + * selectable manipulators in hash table and use this to get tot_sel and do selection */ + + GHash *hash = WM_manipulatormap_manipulator_hash_new(C, mmap, manipulator_selectable_poll, NULL, true); + GHashIterator gh_iter; + int i; + bool changed = false; + + wm_manipulatormap_select_array_ensure_len_alloc(mmap, BLI_ghash_size(hash)); + + GHASH_ITER_INDEX (gh_iter, hash, i) { + wmManipulator *mpr_iter = BLI_ghashIterator_getValue(&gh_iter); + WM_manipulator_select_set(mmap, mpr_iter, true); + } + /* highlight first manipulator */ + wm_manipulatormap_highlight_set(mmap, C, msel->items[0], msel->items[0]->highlight_part); + + BLI_assert(BLI_ghash_size(hash) == msel->len); + + BLI_ghash_free(hash, NULL, NULL); + return changed; +} + +/** + * Select/Deselect all selectable manipulators in \a mmap. + * \return if selection has changed. + * + * TODO select all by type + */ +bool WM_manipulatormap_select_all(bContext *C, wmManipulatorMap *mmap, const int action) +{ + bool changed = false; + + switch (action) { + case SEL_SELECT: + changed = wm_manipulatormap_select_all_intern(C, mmap); + break; + case SEL_DESELECT: + changed = wm_manipulatormap_deselect_all(mmap); + break; + default: + BLI_assert(0); + break; + } + + if (changed) + WM_event_add_mousemove(C); + + return changed; +} + +/** + * Prepare context for manipulator handling (but only if area/region is + * part of screen). Version of #wm_handler_op_context for manipulators. + */ +void wm_manipulatormap_handler_context(bContext *C, wmEventHandler *handler) +{ + bScreen *screen = CTX_wm_screen(C); + + if (screen) { + if (handler->op_area == NULL) { + /* do nothing in this context */ + } + else { + ScrArea *sa; + + for (sa = screen->areabase.first; sa; sa = sa->next) + if (sa == handler->op_area) + break; + if (sa == NULL) { + /* when changing screen layouts with running modal handlers (like render display), this + * is not an error to print */ + if (handler->manipulator_map == NULL) + printf("internal error: modal manipulator-map handler has invalid area\n"); + } + else { + ARegion *ar; + CTX_wm_area_set(C, sa); + for (ar = sa->regionbase.first; ar; ar = ar->next) + if (ar == handler->op_region) + break; + /* XXX no warning print here, after full-area and back regions are remade */ + if (ar) + CTX_wm_region_set(C, ar); + } + } + } +} + +bool WM_manipulatormap_cursor_set(const wmManipulatorMap *mmap, wmWindow *win) +{ + wmManipulator *mpr = mmap->mmap_context.highlight; + if (mpr && mpr->type->cursor_get) { + WM_cursor_set(win, mpr->type->cursor_get(mpr)); + return true; + } + + return false; +} + +void wm_manipulatormap_highlight_set( + wmManipulatorMap *mmap, const bContext *C, wmManipulator *mpr, int part) +{ + if ((mpr != mmap->mmap_context.highlight) || + (mpr && part != mpr->highlight_part)) + { + if (mmap->mmap_context.highlight) { + mmap->mmap_context.highlight->state &= ~WM_MANIPULATOR_STATE_HIGHLIGHT; + mmap->mmap_context.highlight->highlight_part = -1; + } + + mmap->mmap_context.highlight = mpr; + + if (mpr) { + mpr->state |= WM_MANIPULATOR_STATE_HIGHLIGHT; + mpr->highlight_part = part; + + if (C && mpr->type->cursor_get) { + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, mpr->type->cursor_get(mpr)); + } + } + else { + if (C) { + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, CURSOR_STD); + } + } + + /* tag the region for redraw */ + if (C) { + ARegion *ar = CTX_wm_region(C); + ED_region_tag_redraw(ar); + } + } +} + +wmManipulator *wm_manipulatormap_highlight_get(wmManipulatorMap *mmap) +{ + return mmap->mmap_context.highlight; +} + +/** + * Caller should call exit when (enable == False). + */ +void wm_manipulatormap_modal_set( + wmManipulatorMap *mmap, bContext *C, wmManipulator *mpr, const wmEvent *event, bool enable) +{ + if (enable) { + BLI_assert(mmap->mmap_context.modal == NULL); + + /* For now only grab cursor for 3D manipulators. */ + bool grab_cursor = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) != 0; + int retval = OPERATOR_RUNNING_MODAL; + + if (mpr->type->invoke && + (mpr->type->modal || mpr->custom_modal)) + { + retval = mpr->type->invoke(C, mpr, event); + } + + if ((retval & OPERATOR_RUNNING_MODAL) == 0) { + return; + } + + mpr->state |= WM_MANIPULATOR_STATE_MODAL; + mmap->mmap_context.modal = mpr; + + struct wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part); + if (mpop && mpop->type) { + WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr); + + /* we failed to hook the manipulator to the operator handler or operator was cancelled, return */ + if (!mmap->mmap_context.modal) { + mpr->state &= ~WM_MANIPULATOR_STATE_MODAL; + MEM_SAFE_FREE(mpr->interaction_data); + } + return; + } + + if (grab_cursor) { + WM_cursor_grab_enable(CTX_wm_window(C), true, true, NULL); + } + } + else { + BLI_assert(ELEM(mmap->mmap_context.modal, NULL, mpr)); + + /* deactivate, manipulator but first take care of some stuff */ + if (mpr) { + mpr->state &= ~WM_MANIPULATOR_STATE_MODAL; + MEM_SAFE_FREE(mpr->interaction_data); + } + mmap->mmap_context.modal = NULL; + + if (C) { + WM_cursor_grab_disable(CTX_wm_window(C), NULL); + ED_region_tag_redraw(CTX_wm_region(C)); + WM_event_add_mousemove(C); + } + } +} + +wmManipulator *wm_manipulatormap_modal_get(wmManipulatorMap *mmap) +{ + return mmap->mmap_context.modal; +} + +wmManipulator **wm_manipulatormap_selected_get(wmManipulatorMap *mmap, int *r_selected_len) +{ + *r_selected_len = mmap->mmap_context.select.len; + return mmap->mmap_context.select.items; +} + +ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap) +{ + return &mmap->groups; +} + +/** \} */ /* wmManipulatorMap */ + + +/* -------------------------------------------------------------------- */ +/** \name wmManipulatorMapType + * + * \{ */ + +wmManipulatorMapType *WM_manipulatormaptype_find( + const struct wmManipulatorMapType_Params *mmap_params) +{ + for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) { + if (mmap_type->spaceid == mmap_params->spaceid && + mmap_type->regionid == mmap_params->regionid) + { + return mmap_type; + } + } + + return NULL; +} + +wmManipulatorMapType *WM_manipulatormaptype_ensure( + const struct wmManipulatorMapType_Params *mmap_params) +{ + wmManipulatorMapType *mmap_type = WM_manipulatormaptype_find(mmap_params); + + if (mmap_type) { + return mmap_type; + } + + mmap_type = MEM_callocN(sizeof(wmManipulatorMapType), "manipulatortype list"); + mmap_type->spaceid = mmap_params->spaceid; + mmap_type->regionid = mmap_params->regionid; + BLI_addhead(&manipulatormaptypes, mmap_type); + + return mmap_type; +} + +void wm_manipulatormaptypes_free(void) +{ + for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first, *mmap_type_next; + mmap_type; + mmap_type = mmap_type_next) + { + mmap_type_next = mmap_type->next; + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_next; + wgt_ref; + wgt_ref = wgt_next) + { + wgt_next = wgt_ref->next; + WM_manipulatormaptype_group_free(wgt_ref); + } + MEM_freeN(mmap_type); + } +} + +/** + * Initialize keymaps for all existing manipulator-groups + */ +void wm_manipulators_keymap(wmKeyConfig *keyconf) +{ + /* we add this item-less keymap once and use it to group manipulator-group keymaps into it */ + WM_keymap_find(keyconf, "Manipulators", 0, 0); + + for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) { + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) { + wm_manipulatorgrouptype_setup_keymap(wgt_ref->type, keyconf); + } + } +} + +/** \} */ /* wmManipulatorMapType */ + +/* -------------------------------------------------------------------- */ +/** \name Updates for Dynamic Type Registraion + * + * \{ */ + + +void WM_manipulatorconfig_update_tag_init( + wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt) +{ + /* tag for update on next use */ + mmap_type->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT); + wgt->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT); + + wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT; +} + +void WM_manipulatorconfig_update_tag_remove( + wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt) +{ + /* tag for update on next use */ + mmap_type->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE; + wgt->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE; + + wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE; +} + +/** + * Run incase new types have been added (runs often, early exit where possible). + * Follows #WM_keyconfig_update concentions. + */ +void WM_manipulatorconfig_update(struct Main *bmain) +{ + if (G.background) + return; + + if (wm_mmap_type_update_flag == 0) + return; + + if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) { + for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; + mmap_type; + mmap_type = mmap_type->next) + { + if (mmap_type->type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) { + mmap_type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_REMOVE; + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_ref_next; + wgt_ref; + wgt_ref = wgt_ref_next) + { + wgt_ref_next = wgt_ref->next; + if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_REMOVE) { + WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt_ref->type); + } + } + } + } + + wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE; + } + + if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT) { + for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; + mmap_type; + mmap_type = mmap_type->next) + { + const uchar type_update_all = WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT; + if (mmap_type->type_update_flag & type_update_all) { + mmap_type->type_update_flag &= ~type_update_all; + for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; + wgt_ref; + wgt_ref = wgt_ref->next) + { + if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) { + WM_manipulatormaptype_group_init_runtime_keymap(bmain, wgt_ref->type); + wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT; + } + + if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_INIT) { + WM_manipulatormaptype_group_init_runtime(bmain, mmap_type, wgt_ref->type); + wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_INIT; + } + } + } + } + + wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT; + } +} + +/** \} */ diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c new file mode 100644 index 00000000000..b4f65807341 --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_target_props.c @@ -0,0 +1,290 @@ +/* + * ***** 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/manipulators/intern/wm_manipulator_target_props.c + * \ingroup wm + */ + +#include "BKE_context.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm.h" + +/* own includes */ +#include "wm_manipulator_wmapi.h" +#include "wm_manipulator_intern.h" + +/* -------------------------------------------------------------------- */ + +/** \name Property Definition + * \{ */ + +BLI_INLINE wmManipulatorProperty *wm_manipulator_target_property_array(wmManipulator *mpr) +{ + return (wmManipulatorProperty *)(POINTER_OFFSET(mpr, mpr->type->struct_size)); +} + +wmManipulatorProperty *WM_manipulator_target_property_array(wmManipulator *mpr) +{ + return wm_manipulator_target_property_array(mpr); +} + +wmManipulatorProperty *WM_manipulator_target_property_at_index(wmManipulator *mpr, int index) +{ + BLI_assert(index < mpr->type->target_property_defs_len); + BLI_assert(index != -1); + wmManipulatorProperty *mpr_prop_array = wm_manipulator_target_property_array(mpr); + return &mpr_prop_array[index]; +} + +wmManipulatorProperty *WM_manipulator_target_property_find(wmManipulator *mpr, const char *idname) +{ + int index = BLI_findstringindex( + &mpr->type->target_property_defs, idname, offsetof(wmManipulatorPropertyType, idname)); + if (index != -1) { + return WM_manipulator_target_property_at_index(mpr, index); + } + else { + return NULL; + } +} + +void WM_manipulator_target_property_def_rna_ptr( + wmManipulator *mpr, const wmManipulatorPropertyType *mpr_prop_type, + PointerRNA *ptr, PropertyRNA *prop, int index) +{ + wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_at_index(mpr, mpr_prop_type->index_in_type); + + /* if manipulator evokes an operator we cannot use it for property manipulation */ + BLI_assert(mpr->op_data == NULL); + + mpr_prop->type = mpr_prop_type; + + mpr_prop->ptr = *ptr; + mpr_prop->prop = prop; + mpr_prop->index = index; + + if (mpr->type->property_update) { + mpr->type->property_update(mpr, mpr_prop); + } +} + +void WM_manipulator_target_property_def_rna( + wmManipulator *mpr, const char *idname, + PointerRNA *ptr, const char *propname, int index) +{ + const wmManipulatorPropertyType *mpr_prop_type = WM_manipulatortype_target_property_find(mpr->type, idname); + PropertyRNA *prop = RNA_struct_find_property(ptr, propname); + WM_manipulator_target_property_def_rna_ptr(mpr, mpr_prop_type, ptr, prop, index); +} + +void WM_manipulator_target_property_def_func_ptr( + wmManipulator *mpr, const wmManipulatorPropertyType *mpr_prop_type, + const wmManipulatorPropertyFnParams *params) +{ + wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_at_index(mpr, mpr_prop_type->index_in_type); + + /* if manipulator evokes an operator we cannot use it for property manipulation */ + BLI_assert(mpr->op_data == NULL); + + mpr_prop->type = mpr_prop_type; + + mpr_prop->custom_func.value_get_fn = params->value_get_fn; + mpr_prop->custom_func.value_set_fn = params->value_set_fn; + mpr_prop->custom_func.range_get_fn = params->range_get_fn; + mpr_prop->custom_func.free_fn = params->free_fn; + mpr_prop->custom_func.user_data = params->user_data; + + if (mpr->type->property_update) { + mpr->type->property_update(mpr, mpr_prop); + } +} + +void WM_manipulator_target_property_def_func( + wmManipulator *mpr, const char *idname, + const wmManipulatorPropertyFnParams *params) +{ + const wmManipulatorPropertyType *mpr_prop_type = WM_manipulatortype_target_property_find(mpr->type, idname); + WM_manipulator_target_property_def_func_ptr(mpr, mpr_prop_type, params); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Property Access + * \{ */ + +bool WM_manipulator_target_property_is_valid_any(wmManipulator *mpr) +{ + wmManipulatorProperty *mpr_prop_array = wm_manipulator_target_property_array(mpr); + for (int i = 0; i < mpr->type->target_property_defs_len; i++) { + wmManipulatorProperty *mpr_prop = &mpr_prop_array[i]; + if (WM_manipulator_target_property_is_valid(mpr_prop)) { + return true; + } + } + return false; +} + +bool WM_manipulator_target_property_is_valid(const wmManipulatorProperty *mpr_prop) +{ + return ((mpr_prop->prop != NULL) || + (mpr_prop->custom_func.value_get_fn && mpr_prop->custom_func.value_set_fn)); +} + +float WM_manipulator_target_property_value_get( + const wmManipulator *mpr, wmManipulatorProperty *mpr_prop) +{ + if (mpr_prop->custom_func.value_get_fn) { + float value = 0.0f; + BLI_assert(mpr_prop->type->array_length == 1); + mpr_prop->custom_func.value_get_fn(mpr, mpr_prop, &value); + return value; + } + + if (mpr_prop->index == -1) { + return RNA_property_float_get(&mpr_prop->ptr, mpr_prop->prop); + } + else { + return RNA_property_float_get_index(&mpr_prop->ptr, mpr_prop->prop, mpr_prop->index); + } +} + +void WM_manipulator_target_property_value_set( + bContext *C, const wmManipulator *mpr, + wmManipulatorProperty *mpr_prop, const float value) +{ + if (mpr_prop->custom_func.value_set_fn) { + BLI_assert(mpr_prop->type->array_length == 1); + mpr_prop->custom_func.value_set_fn(mpr, mpr_prop, &value); + return; + } + + /* reset property */ + if (mpr_prop->index == -1) { + RNA_property_float_set(&mpr_prop->ptr, mpr_prop->prop, value); + } + else { + RNA_property_float_set_index(&mpr_prop->ptr, mpr_prop->prop, mpr_prop->index, value); + } + RNA_property_update(C, &mpr_prop->ptr, mpr_prop->prop); +} + +void WM_manipulator_target_property_value_get_array( + const wmManipulator *mpr, wmManipulatorProperty *mpr_prop, + float *value) +{ + if (mpr_prop->custom_func.value_get_fn) { + mpr_prop->custom_func.value_get_fn(mpr, mpr_prop, value); + return; + } + RNA_property_float_get_array(&mpr_prop->ptr, mpr_prop->prop, value); +} + +void WM_manipulator_target_property_value_set_array( + bContext *C, const wmManipulator *mpr, wmManipulatorProperty *mpr_prop, + const float *value) +{ + if (mpr_prop->custom_func.value_set_fn) { + mpr_prop->custom_func.value_set_fn(mpr, mpr_prop, value); + return; + } + RNA_property_float_set_array(&mpr_prop->ptr, mpr_prop->prop, value); + + RNA_property_update(C, &mpr_prop->ptr, mpr_prop->prop); +} + +bool WM_manipulator_target_property_range_get( + const wmManipulator *mpr, wmManipulatorProperty *mpr_prop, + float range[2]) +{ + if (mpr_prop->custom_func.value_get_fn) { + if (mpr_prop->custom_func.range_get_fn) { + mpr_prop->custom_func.range_get_fn(mpr, mpr_prop, range); + return true; + } + else { + return false; + + } + } + + float step, precision; + RNA_property_float_ui_range(&mpr_prop->ptr, mpr_prop->prop, &range[0], &range[1], &step, &precision); + return true; +} + +int WM_manipulator_target_property_array_length( + const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop) +{ + if (mpr_prop->custom_func.value_get_fn) { + return mpr_prop->type->array_length; + } + return RNA_property_array_length(&mpr_prop->ptr, mpr_prop->prop); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Property Define + * \{ */ + +const wmManipulatorPropertyType *WM_manipulatortype_target_property_find( + const wmManipulatorType *wt, const char *idname) +{ + return BLI_findstring(&wt->target_property_defs, idname, offsetof(wmManipulatorPropertyType, idname)); +} + +void WM_manipulatortype_target_property_def( + wmManipulatorType *wt, const char *idname, int data_type, int array_length) +{ + wmManipulatorPropertyType *mpt; + + BLI_assert(WM_manipulatortype_target_property_find(wt, idname) == NULL); + + const uint idname_size = strlen(idname) + 1; + mpt = MEM_callocN(sizeof(wmManipulatorPropertyType) + idname_size, __func__); + memcpy(mpt->idname, idname, idname_size); + mpt->data_type = data_type; + mpt->array_length = array_length; + mpt->index_in_type = wt->target_property_defs_len; + wt->target_property_defs_len += 1; + BLI_addtail(&wt->target_property_defs, mpt); +} + +/** \} */ diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_type.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator_type.c new file mode 100644 index 00000000000..18944a9ab9d --- /dev/null +++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_type.c @@ -0,0 +1,211 @@ +/* + * ***** 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/manipulators/intern/wm_manipulator_type.c + * \ingroup wm + */ + +#include "BKE_context.h" + +#include "BLI_ghash.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "BKE_main.h" + +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" + +/* only for own init/exit calls (wm_manipulatortype_init/wm_manipulatortype_free) */ +#include "wm.h" + +/* own includes */ +#include "wm_manipulator_wmapi.h" +#include "wm_manipulator_intern.h" + + +/** \name Manipulator Type Append + * + * \note This follows conventions from #WM_operatortype_find #WM_operatortype_append & friends. + * \{ */ + +static GHash *global_manipulatortype_hash = NULL; + +const wmManipulatorType *WM_manipulatortype_find(const char *idname, bool quiet) +{ + if (idname[0]) { + wmManipulatorType *wt; + + wt = BLI_ghash_lookup(global_manipulatortype_hash, idname); + if (wt) { + return wt; + } + + if (!quiet) { + printf("search for unknown manipulator '%s'\n", idname); + } + } + else { + if (!quiet) { + printf("search for empty manipulator\n"); + } + } + + return NULL; +} + +/* caller must free */ +void WM_manipulatortype_iter(GHashIterator *ghi) +{ + BLI_ghashIterator_init(ghi, global_manipulatortype_hash); +} + +static wmManipulatorType *wm_manipulatortype_append__begin(void) +{ + wmManipulatorType *wt = MEM_callocN(sizeof(wmManipulatorType), "manipulatortype"); + wt->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_ManipulatorProperties); +#if 0 + /* Set the default i18n context now, so that opfunc can redefine it if needed! */ + RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); + ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; +#endif + return wt; +} +static void wm_manipulatortype_append__end(wmManipulatorType *wt) +{ + BLI_assert(wt->struct_size >= sizeof(wmManipulator)); + + RNA_def_struct_identifier(&BLENDER_RNA, wt->srna, wt->idname); + + BLI_ghash_insert(global_manipulatortype_hash, (void *)wt->idname, wt); +} + +void WM_manipulatortype_append(void (*wtfunc)(struct wmManipulatorType *)) +{ + wmManipulatorType *wt = wm_manipulatortype_append__begin(); + wtfunc(wt); + wm_manipulatortype_append__end(wt); +} + +void WM_manipulatortype_append_ptr(void (*wtfunc)(struct wmManipulatorType *, void *), void *userdata) +{ + wmManipulatorType *mt = wm_manipulatortype_append__begin(); + wtfunc(mt, userdata); + wm_manipulatortype_append__end(mt); +} + +/** + * Free but don't remove from ghash. + */ +static void manipulatortype_free(wmManipulatorType *wt) +{ + if (wt->ext.srna) { /* python manipulator, allocs own string */ + MEM_freeN((void *)wt->idname); + } + + BLI_freelistN(&wt->target_property_defs); + MEM_freeN(wt); +} + +/** + * \param C: May be NULL. + */ +static void manipulatortype_unlink( + bContext *C, Main *bmain, wmManipulatorType *wt) +{ + /* Free instances. */ + for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) { + for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + for (ARegion *ar = lb->first; ar; ar = ar->next) { + wmManipulatorMap *mmap = ar->manipulator_map; + if (mmap) { + wmManipulatorGroup *mgroup; + for (mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) { + for (wmManipulator *mpr = mgroup->manipulators.first, *mpr_next; mpr; mpr = mpr_next) { + mpr_next = mpr->next; + BLI_assert(mgroup->parent_mmap == mmap); + if (mpr->type == wt) { + WM_manipulator_unlink(&mgroup->manipulators, mgroup->parent_mmap, mpr, C); + ED_region_tag_redraw(ar); + } + } + } + } + } + } + } + } +} + +void WM_manipulatortype_remove_ptr(bContext *C, Main *bmain, wmManipulatorType *wt) +{ + BLI_assert(wt == WM_manipulatortype_find(wt->idname, false)); + + BLI_ghash_remove(global_manipulatortype_hash, wt->idname, NULL, NULL); + + manipulatortype_unlink(C, bmain, wt); + + manipulatortype_free(wt); +} + +bool WM_manipulatortype_remove(bContext *C, Main *bmain, const char *idname) +{ + wmManipulatorType *wt = BLI_ghash_lookup(global_manipulatortype_hash, idname); + + if (wt == NULL) { + return false; + } + + WM_manipulatortype_remove_ptr(C, bmain, wt); + + return true; +} + +static void wm_manipulatortype_ghash_free_cb(wmManipulatorType *mt) +{ + manipulatortype_free(mt); +} + +void wm_manipulatortype_free(void) +{ + BLI_ghash_free(global_manipulatortype_hash, NULL, (GHashValFreeFP)wm_manipulatortype_ghash_free_cb); + global_manipulatortype_hash = NULL; +} + +/* called on initialize WM_init() */ +void wm_manipulatortype_init(void) +{ + /* reserve size is set based on blender default setup */ + global_manipulatortype_hash = BLI_ghash_str_new_ex("wm_manipulatortype_init gh", 128); +} + +/** \} */ diff --git a/source/blender/windowmanager/manipulators/wm_manipulator_fn.h b/source/blender/windowmanager/manipulators/wm_manipulator_fn.h new file mode 100644 index 00000000000..c54024529c3 --- /dev/null +++ b/source/blender/windowmanager/manipulators/wm_manipulator_fn.h @@ -0,0 +1,85 @@ +/* + * ***** 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/manipulators/wm_manipulator_fn.h + * \ingroup wm + * + * Callback function definitions, needed for both Types & API headers. + */ + +#ifndef __WM_MANIPULATOR_FN_H__ +#define __WM_MANIPULATOR_FN_H__ + +#include "BLI_compiler_attrs.h" + +/* wmManipulatorGroup */ +typedef bool (*wmManipulatorGroupFnPoll)( + const struct bContext *, struct wmManipulatorGroupType *) + ATTR_WARN_UNUSED_RESULT; +typedef void (*wmManipulatorGroupFnInit)( + const struct bContext *, struct wmManipulatorGroup *); +typedef void (*wmManipulatorGroupFnRefresh)( + const struct bContext *, struct wmManipulatorGroup *); +typedef void (*wmManipulatorGroupFnDrawPrepare)( + const struct bContext *, struct wmManipulatorGroup *); +typedef struct wmKeyMap *(*wmManipulatorGroupFnSetupKeymap)( + const struct wmManipulatorGroupType *, struct wmKeyConfig *) + ATTR_WARN_UNUSED_RESULT; + +/* wmManipulator */ +/* See: wmManipulatorType for docs on each type. */ + +typedef void (*wmManipulatorFnSetup)(struct wmManipulator *); +typedef void (*wmManipulatorFnDraw)(const struct bContext *, struct wmManipulator *); +typedef void (*wmManipulatorFnDrawSelect)(const struct bContext *, struct wmManipulator *, int); +typedef int (*wmManipulatorFnTestSelect)(struct bContext *, struct wmManipulator *, const struct wmEvent *); +typedef int (*wmManipulatorFnModal)(struct bContext *, struct wmManipulator *, const struct wmEvent *, eWM_ManipulatorTweak); +typedef void (*wmManipulatorFnPropertyUpdate)(struct wmManipulator *, struct wmManipulatorProperty *); +typedef void (*wmManipulatorFnMatrixBasisGet)(const struct wmManipulator *, float[4][4]); +typedef int (*wmManipulatorFnInvoke)(struct bContext *, struct wmManipulator *, const struct wmEvent *); +typedef void (*wmManipulatorFnExit)(struct bContext *, struct wmManipulator *, const bool); +typedef int (*wmManipulatorFnCursorGet)(struct wmManipulator *); +typedef void (*wmManipulatorFnSelectRefresh)(struct wmManipulator *); + +/* wmManipulatorProperty ('value' type defined by 'wmManipulatorProperty.data_type') */ +typedef void (*wmManipulatorPropertyFnGet)( + const struct wmManipulator *, struct wmManipulatorProperty *, + /* typically 'float *' */ + void *value); +typedef void (*wmManipulatorPropertyFnSet)( + const struct wmManipulator *, struct wmManipulatorProperty *, + /* typically 'const float *' */ + const void *value); +typedef void (*wmManipulatorPropertyFnRangeGet)( + const struct wmManipulator *, struct wmManipulatorProperty *, + /* typically 'float[2]' */ + void *range); +typedef void (*wmManipulatorPropertyFnFree)( + const struct wmManipulator *, struct wmManipulatorProperty *); + +typedef struct wmManipulatorPropertyFnParams { + wmManipulatorPropertyFnGet value_get_fn; + wmManipulatorPropertyFnSet value_set_fn; + wmManipulatorPropertyFnRangeGet range_get_fn; + wmManipulatorPropertyFnFree free_fn; + void *user_data; +} wmManipulatorPropertyFnParams; + +#endif /* __WM_MANIPULATOR_FN_H__ */ diff --git a/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h b/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h new file mode 100644 index 00000000000..87cf711a60b --- /dev/null +++ b/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h @@ -0,0 +1,98 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/manipulators/wm_manipulator_wmapi.h + * \ingroup wm + * + * \name Manipulators Window Manager API + * API for usage in window manager code only. It should contain all functionality + * needed to hook up the manipulator system with Blender's window manager. It's + * mostly the event system that needs to communicate with manipulator code. + * + * Only included in wm.h and lower level files. + */ + + +#ifndef __WM_MANIPULATOR_WMAPI_H__ +#define __WM_MANIPULATOR_WMAPI_H__ + +struct wmEventHandler; +struct wmManipulatorMap; +struct wmOperatorType; +struct wmOperator; + + +/* -------------------------------------------------------------------- */ +/* wmManipulator */ + +/* wm_manipulator_type.c, for init/exit */ +void wm_manipulatortype_free(void); +void wm_manipulatortype_init(void); + +/* wm_manipulatorgroup_type.c, for init/exit */ +void wm_manipulatorgrouptype_free(void); +void wm_manipulatorgrouptype_init(void); + +/* -------------------------------------------------------------------- */ +/* wmManipulatorGroup */ + +void MANIPULATORGROUP_OT_manipulator_select(struct wmOperatorType *ot); +void MANIPULATORGROUP_OT_manipulator_tweak(struct wmOperatorType *ot); + +bool wm_manipulatorgroup_is_any_selected(const struct wmManipulatorGroup *mgroup); + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMap */ + +void wm_manipulatormap_remove(struct wmManipulatorMap *mmap); + +void wm_manipulators_keymap(struct wmKeyConfig *keyconf); + +void wm_manipulatormaps_handled_modal_update( + bContext *C, struct wmEvent *event, struct wmEventHandler *handler); +void wm_manipulatormap_handler_context(bContext *C, struct wmEventHandler *handler); + +struct wmManipulator *wm_manipulatormap_highlight_find( + struct wmManipulatorMap *mmap, bContext *C, const struct wmEvent *event, + int *r_part); +void wm_manipulatormap_highlight_set( + struct wmManipulatorMap *mmap, const bContext *C, + struct wmManipulator *mpr, int part); +struct wmManipulator *wm_manipulatormap_highlight_get(struct wmManipulatorMap *mmap); +void wm_manipulatormap_modal_set( + struct wmManipulatorMap *mmap, bContext *C, struct wmManipulator *mpr, + const struct wmEvent *event, bool enable); + +struct wmManipulator *wm_manipulatormap_modal_get(struct wmManipulatorMap *mmap); +struct wmManipulator **wm_manipulatormap_selected_get(wmManipulatorMap *mmap, int *r_selected_len); +struct ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap); + +/* -------------------------------------------------------------------- */ +/* wmManipulatorMapType */ + +void wm_manipulatormaptypes_free(void); + +#endif /* __WM_MANIPULATOR_WMAPI_H__ */ + diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index a417a719b8d..83d20aa2612 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -34,6 +34,8 @@ struct wmWindow; struct ReportList; +#include "manipulators/wm_manipulator_wmapi.h" + typedef struct wmPaintCursor { struct wmPaintCursor *next, *prev; diff --git a/source/blender/windowmanager/wm_draw.h b/source/blender/windowmanager/wm_draw.h index 5257bba45ff..56dff304719 100644 --- a/source/blender/windowmanager/wm_draw.h +++ b/source/blender/windowmanager/wm_draw.h @@ -34,6 +34,8 @@ #include "GPU_glew.h" +#define USE_TEXTURE_RECTANGLE 1 + typedef struct wmDrawTriple { GLuint bind; int x, y; @@ -56,7 +58,7 @@ void wm_draw_region_clear (struct wmWindow *win, struct ARegion *ar); void wm_tag_redraw_overlay (struct wmWindow *win, struct ARegion *ar); -void wm_triple_draw_textures (struct wmWindow *win, struct wmDrawTriple *triple, float alpha, bool is_interlace); +void wm_triple_draw_textures (struct wmWindow *win, struct wmDrawTriple *triple, float alpha); void wm_draw_data_free (struct wmWindow *win); diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index 9977e73f2fd..c14517f1662 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -68,7 +68,8 @@ typedef struct wmEventHandler { /* drop box handler */ ListBase *dropboxes; - + /* manipulator handler */ + struct wmManipulatorMap *manipulator_map; } wmEventHandler; /* custom types for handlers, for signaling, freeing */ diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 090c8c90280..e327bd81d81 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -339,6 +339,8 @@ enum { EVT_DROP = 0x5023, EVT_BUT_CANCEL = 0x5024, + /* could become manipulator callback */ + EVT_MANIPULATOR_UPDATE = 0x5025, /* ********** End of Blender internal events. ********** */ }; diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index b102b6c7cc7..6f63e55e8ab 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -45,6 +45,7 @@ void WM_OT_save_homefile(struct wmOperatorType *ot); void WM_OT_userpref_autoexec_path_add(struct wmOperatorType *ot); void WM_OT_userpref_autoexec_path_remove(struct wmOperatorType *ot); void WM_OT_save_userpref(struct wmOperatorType *ot); +void WM_OT_save_workspace_file(struct wmOperatorType *ot); void WM_OT_read_history(struct wmOperatorType *ot); void WM_OT_read_homefile(struct wmOperatorType *ot); void WM_OT_read_factory_settings(struct wmOperatorType *ot); diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h index f70ec6b47f6..0264955a4c5 100644 --- a/source/blender/windowmanager/wm_window.h +++ b/source/blender/windowmanager/wm_window.h @@ -32,7 +32,11 @@ #ifndef __WM_WINDOW_H__ #define __WM_WINDOW_H__ +struct EnumPropertyItem; +struct wmEvent; struct wmOperator; +struct PointerRNA; +struct PropertyRNA; /* *************** internal api ************** */ void wm_ghost_init (bContext *C); @@ -42,8 +46,8 @@ void wm_get_screensize(int *r_width, int *r_height); void wm_get_desktopsize(int *r_width, int *r_height); wmWindow *wm_window_new (bContext *C); -wmWindow *wm_window_copy (bContext *C, wmWindow *win_src); -wmWindow *wm_window_copy_test (bContext *C, wmWindow *win_src); +wmWindow *wm_window_copy (bContext *C, wmWindow *win_src, const bool duplicate_layout); +wmWindow *wm_window_copy_test (bContext *C, wmWindow *win_src, const bool duplicate_layout); void wm_window_free (bContext *C, wmWindowManager *wm, wmWindow *win); void wm_window_close (bContext *C, wmWindowManager *wm, wmWindow *win); @@ -76,9 +80,13 @@ void wm_window_IME_end (wmWindow *win); /* *************** window operators ************** */ 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); +const struct EnumPropertyItem *wm_window_new_screen_itemf( + bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); +int wm_window_new_exec(bContext *C, struct wmOperator *op); +int wm_window_new_invoke(bContext *C, struct wmOperator *op, const struct wmEvent *event); + /* Initial (unmaximized) size to start with for * systems that can't find it for themselves (X11). * Clamped by real desktop limits */ |