diff options
Diffstat (limited to 'source/blender/windowmanager/intern')
-rw-r--r-- | source/blender/windowmanager/intern/wm.c | 2 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_event_system.c | 106 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_generic_widgets.c | 1109 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_init_exit.c | 5 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_operators.c | 39 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_widgets.c | 909 |
6 files changed, 2166 insertions, 4 deletions
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 34069e0b873..80da6b7490f 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -459,7 +459,7 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) BLI_freelistN(&wm->paintcursors); WM_drag_free_list(&wm->drags); - + wm_reports_free(wm); if (C && CTX_wm_manager(C) == wm) CTX_wm_manager_set(C, NULL); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index d8ca80a62cb..67bce10fa9d 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1370,6 +1370,40 @@ static void wm_handler_op_context(bContext *C, wmEventHandler *handler) } } +static void wm_handler_widgetmap_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->widgetmap == NULL) + printf("internal error: modal widgetmap 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); + } + } + } +} + /* called on exit or remove area, only here call cancel callback */ void WM_event_remove_handlers(bContext *C, ListBase *handlers) { @@ -1392,7 +1426,7 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers) if (handler->op->type->flag & OPTYPE_UNDO) wm->op_undo_depth--; - + CTX_wm_area_set(C, area); CTX_wm_region_set(C, region); } @@ -1578,8 +1612,8 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand /* warning, after this call all context data and 'event' may be freed. see check below */ retval = ot->modal(C, op, event); + OPERATOR_RETVAL_CHECK(retval); - /* when this is _not_ the case the modal modifier may have loaded * a new blend file (demo mode does this), so we have to assume * the event, operator etc have all been freed. - campbell */ @@ -1969,7 +2003,72 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers } } } + else if (handler->widgetmap) { + struct wmWidgetMap *wmap = handler->widgetmap; + unsigned char part; + short event_processed = 0; + wmWidget *widget = wm_widgetmap_get_active_widget(wmap); + ScrArea *area = CTX_wm_area(C); + ARegion *region = CTX_wm_region(C); + + wm_handler_widgetmap_context(C, handler); + wm_region_mouse_co(C, event); + + switch (event->type) { + case MOUSEMOVE: + if (widget) { + widget->handler(C, event, widget); + event_processed = EVT_WIDGET_UPDATE; + action |= WM_HANDLER_BREAK; + } + else if (wm_widgetmap_is_3d(wmap)) { + widget = wm_widget_find_highlighted_3D(wmap, C, event, &part); + wm_widgetmap_set_highlighted_widget(wmap, C, widget, part); + } + else { + widget = wm_widget_find_highlighted(wmap, C, event, &part); + wm_widgetmap_set_highlighted_widget(wmap, C, widget, part); + } + break; + + case LEFTMOUSE: + { + if (widget) { + if (event->val == KM_RELEASE) { + wm_widgetmap_set_active_widget(wmap, C, event, NULL, false); + event_processed = EVT_WIDGET_RELEASED; + action |= WM_HANDLER_BREAK; + } + else { + action |= WM_HANDLER_BREAK; + } + } + else if (event->val == KM_PRESS) { + widget = wm_widgetmap_get_highlighted_widget(wmap); + + if (widget) { + wm_widgetmap_set_active_widget(wmap, C, event, widget, handler->op == NULL); + action |= WM_HANDLER_BREAK; + } + } + break; + } + } + + /* restore the area */ + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + + if (handler->op) { + /* if event was processed by an active widget pass the modified event to the operator */ + if (event_processed) { + event->type = event_processed; + } + action |= wm_handler_operator_call(C, handlers, handler, event, NULL); + } + } else { + /* handle the widget first, before passing the event down */ /* modal, swallows all */ action |= wm_handler_operator_call(C, handlers, handler, event, NULL); } @@ -2516,7 +2615,7 @@ wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op) { wmEventHandler *handler = MEM_callocN(sizeof(wmEventHandler), "event modal handler"); wmWindow *win = CTX_wm_window(C); - + /* operator was part of macro */ if (op->opm) { /* give the mother macro to the handler */ @@ -3492,6 +3591,7 @@ void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]) angle = WM_event_ndof_to_axis_angle(ndof, axis); axis_angle_to_quat(q, axis, angle); } +/** \} */ /* if this is a tablet event, return tablet pressure and set *pen_flip * to 1 if the eraser tool is being used, 0 otherwise */ diff --git a/source/blender/windowmanager/intern/wm_generic_widgets.c b/source/blender/windowmanager/intern/wm_generic_widgets.c new file mode 100644 index 00000000000..247bd6a601b --- /dev/null +++ b/source/blender/windowmanager/intern/wm_generic_widgets.c @@ -0,0 +1,1109 @@ +/* + * ***** 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) 2009 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/interface/interface_generic_widgets.c + * \ingroup edinterface + */ + +#include "RNA_types.h" +#include "RNA_access.h" + +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_object_types.h" +#include "DNA_view3d_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_userdef_types.h" +#include "DNA_widget_types.h" + +#include "BLI_utildefines.h" +#include "BLI_math_matrix.h" +#include "BLI_math.h" +#include "BLI_rect.h" + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_object.h" + +#include "ED_view3d.h" +#include "ED_screen.h" + +#include "WM_types.h" +#include "WM_api.h" + +#include "GL/glew.h" +#include "GPU_select.h" + +#include "BIF_glutil.h" + +#include "MEM_guardedalloc.h" + +#include "UI_interface.h" + +#include "3d_widgets/ui_widget_library.h" + +#include "wm.h" +#include "WM_types.h" + + +/****************************************************** + * GENERIC WIDGET LIBRARY * + ******************************************************/ + +typedef struct WidgetDrawInfo { + int nverts; + int ntris; + float (*verts)[3]; + float (*normals)[3]; + unsigned short *indices; + bool init; +} WidgetDrawInfo; + + +WidgetDrawInfo arraw_head_draw_info = {0}; +WidgetDrawInfo dial_draw_info = {0}; + +static void widget_draw_intern(WidgetDrawInfo *info, bool select) +{ + GLuint buf[3]; + + bool use_lighting = !select && ((U.tw_flag & V3D_SHADED_WIDGETS) != 0); + + if (use_lighting) + glGenBuffers(3, buf); + else + glGenBuffers(2, buf); + + glEnableClientState(GL_VERTEX_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, buf[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * info->nverts, info->verts, GL_STATIC_DRAW); + glVertexPointer(3, GL_FLOAT, 0, NULL); + + if (use_lighting) { + glEnableClientState(GL_NORMAL_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, buf[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * info->nverts, info->normals, GL_STATIC_DRAW); + glNormalPointer(GL_FLOAT, 0, NULL); + glShadeModel(GL_SMOOTH); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * (3 * info->ntris), info->indices, GL_STATIC_DRAW); + + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + glDrawElements(GL_TRIANGLES, info->ntris * 3, GL_UNSIGNED_SHORT, NULL); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + glDisableClientState(GL_VERTEX_ARRAY); + + if (use_lighting) { + glDisableClientState(GL_NORMAL_ARRAY); + glShadeModel(GL_FLAT); + glDeleteBuffers(3, buf); + } + else { + glDeleteBuffers(2, buf); + } +} + +/********* Arrow widget ************/ + +#define ARROW_UP_VECTOR_SET 1 + +typedef struct ArrowWidget { + wmWidget widget; + int style; + int flag; + float direction[3]; + float up[3]; + float color[4]; + float offset; + /* property range and minimum for constrained arrows */ + float range, min; +} ArrowWidget; + +typedef struct ArrowInteraction { + float orig_origin[3]; + float orig_mouse[2]; + float orig_offset; + float orig_scale; + + /* direction vector, projected in screen space */ + float proj_direction[2]; +} ArrowInteraction; + + +static void widget_arrow_get_final_pos(struct wmWidget *widget, float pos[3]) +{ + ArrowWidget *arrow = (ArrowWidget *)widget; + + mul_v3_v3fl(pos, arrow->direction, arrow->offset); + add_v3_v3(pos, arrow->widget.origin); +} + +static void arrow_draw_geom(ArrowWidget *arrow, bool select) +{ + if (arrow->style & WIDGET_ARROW_STYLE_CROSS) { + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_LIGHTING); + glBegin(GL_LINES); + glVertex2f(-1.0, 0.f); + glVertex2f(1.0, 0.f); + glVertex2f(0.f, -1.0); + glVertex2f(0.f, 1.0); + glEnd(); + + glPopAttrib(); + } + else { + widget_draw_intern(&arraw_head_draw_info, select); + } +} + +static void arrow_draw_intern(ArrowWidget *arrow, bool select, bool highlight) +{ + float rot[3][3]; + float mat[4][4]; + float up[3] = {0.0f, 0.0f, 1.0f}; + float final_pos[3]; + + widget_arrow_get_final_pos(&arrow->widget, final_pos); + + if (arrow->flag & ARROW_UP_VECTOR_SET) { + copy_v3_v3(rot[2], arrow->direction); + copy_v3_v3(rot[1], arrow->up); + cross_v3_v3v3(rot[0], arrow->up, arrow->direction); + } + else { + rotation_between_vecs_to_mat3(rot, up, arrow->direction); + } + copy_m4_m3(mat, rot); + copy_v3_v3(mat[3], final_pos); + mul_mat3_m4_fl(mat, arrow->widget.scale); + + glPushMatrix(); + glMultMatrixf(&mat[0][0]); + + if (highlight && !(arrow->widget.flag & WM_WIDGET_DRAW_HOVER)) + glColor4f(1.0, 1.0, 0.0, 1.0); + else + glColor4fv(arrow->color); + + arrow_draw_geom(arrow, select); + + glPopMatrix(); + + if (arrow->widget.interaction_data) { + ArrowInteraction *data = arrow->widget.interaction_data; + + copy_m4_m3(mat, rot); + copy_v3_v3(mat[3], data->orig_origin); + mul_mat3_m4_fl(mat, data->orig_scale); + + glPushMatrix(); + glMultMatrixf(&mat[0][0]); + + glEnable(GL_BLEND); + glColor4f(0.5f, 0.5f, 0.5f, 0.5f); + arrow_draw_geom(arrow, select); + + glDisable(GL_BLEND); + + glPopMatrix(); + } +} + +static void widget_arrow_render_3d_intersect(const struct bContext *UNUSED(C), struct wmWidget *widget, int selectionbase) +{ + GPU_select_load_id(selectionbase); + arrow_draw_intern((ArrowWidget *)widget, true, false); +} + +static void widget_arrow_draw(struct wmWidget *widget, const struct bContext *UNUSED(C)) +{ + arrow_draw_intern((ArrowWidget *)widget, false, (widget->flag & WM_WIDGET_HIGHLIGHT) != 0); +} + +#define ARROW_RANGE 1.5f + +static int widget_arrow_handler(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget) +{ + ArrowWidget *arrow = (ArrowWidget *)widget; + ArrowInteraction *data = widget->interaction_data; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + float orig_origin[4]; + float viewvec[3], tangent[3], plane[3]; + float offset[4]; + float m_diff[2]; + float dir_2d[2], dir2d_final[2]; + float fac, zfac; + float facdir = 1.0f; + bool use_vertical = false; + + copy_v3_v3(orig_origin, data->orig_origin); + orig_origin[3] = 1.0f; + add_v3_v3v3(offset, orig_origin, arrow->direction); + offset[3] = 1.0f; + + /* calculate view vector */ + if (rv3d->is_persp) { + sub_v3_v3v3(viewvec, orig_origin, rv3d->viewinv[3]); + } + else { + copy_v3_v3(viewvec, rv3d->viewinv[2]); + } + normalize_v3(viewvec); + + zfac = ED_view3d_calc_zfac(rv3d, orig_origin, NULL); + + /* first determine if view vector is really close to the direction. If it is, we use vertical movement to determine offset, + * just like transform system does */ + if (RAD2DEG(acos(dot_v3v3(viewvec, arrow->direction))) > 5.0f) { + /* multiply to projection space */ + mul_m4_v4(rv3d->persmat, orig_origin); + mul_v4_fl(orig_origin, 1.0f/orig_origin[3]); + mul_m4_v4(rv3d->persmat, offset); + mul_v4_fl(offset, 1.0f/offset[3]); + + sub_v2_v2v2(dir_2d, offset, orig_origin); + dir_2d[0] *= ar->winx; + dir_2d[1] *= ar->winy; + normalize_v2(dir_2d); + } + else { + dir_2d[0] = 0.0f; + dir_2d[1] = 1.0f; + use_vertical = true; + } + + /* find mouse difference */ + m_diff[0] = event->mval[0] - data->orig_mouse[0]; + m_diff[1] = event->mval[1] - data->orig_mouse[1]; + + /* project the displacement on the screen space arrow direction */ + project_v2_v2v2(dir2d_final, m_diff, dir_2d); + + ED_view3d_win_to_delta(ar, dir2d_final, offset, zfac); + + add_v3_v3v3(orig_origin, offset, data->orig_origin); + + /* calculate view vector for the new position */ + if (rv3d->is_persp) { + sub_v3_v3v3(viewvec, orig_origin, rv3d->viewinv[3]); + } + else { + copy_v3_v3(viewvec, rv3d->viewinv[2]); + } + + normalize_v3(viewvec); + if (!use_vertical) { + /* now find a plane parallel to the view vector so we can intersect with the arrow direction */ + cross_v3_v3v3(tangent, viewvec, offset); + cross_v3_v3v3(plane, tangent, viewvec); + fac = dot_v3v3(plane, offset) / dot_v3v3(arrow->direction, plane); + + facdir = (fac < 0.0) ? -1.0 : 1.0; + mul_v3_v3fl(offset, arrow->direction, fac); + } + else { + facdir = (m_diff[1] < 0.0) ? -1.0 : 1.0; + } + + /* set the property for the operator and call its modal function */ + if (widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) { + float value; + + value = data->orig_offset + facdir * len_v3(offset); + if (arrow->style & WIDGET_ARROW_STYLE_CONSTRAINED) { + if (arrow->style & WIDGET_ARROW_STYLE_INVERTED) + value = arrow->min + arrow->range - (value * arrow->range / ARROW_RANGE); + else + value = arrow->min + (value * arrow->range / ARROW_RANGE); + } + + RNA_property_float_set(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE], value); + RNA_property_update(C, &widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]); + + /* accounts for clamping properly */ + if (arrow->style & WIDGET_ARROW_STYLE_CONSTRAINED) { + if (arrow->style & WIDGET_ARROW_STYLE_INVERTED) + arrow->offset = ARROW_RANGE * (arrow->min + arrow->range - (RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]))) / arrow->range; + else + arrow->offset = ARROW_RANGE * ((RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) - arrow->min) / arrow->range); + } + else + arrow->offset = RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]); + } + else { + arrow->offset = facdir * len_v3(offset); + } + + /* tag the region for redraw */ + ED_region_tag_redraw(ar); + + return OPERATOR_PASS_THROUGH; +} + + +static int widget_arrow_invoke(struct bContext *UNUSED(C), const struct wmEvent *event, struct wmWidget *widget) +{ + ArrowWidget *arrow = (ArrowWidget *) widget; + ArrowInteraction *data = MEM_callocN(sizeof (ArrowInteraction), "arrow_interaction"); + + data->orig_offset = arrow->offset; + + data->orig_mouse[0] = event->mval[0]; + data->orig_mouse[1] = event->mval[1]; + + data->orig_scale = widget->scale; + + widget_arrow_get_final_pos(widget, data->orig_origin); + + widget->interaction_data = data; + + return OPERATOR_RUNNING_MODAL; +} + +static void widget_arrow_bind_to_prop(struct wmWidget *widget, int UNUSED(slot)) +{ + ArrowWidget *arrow = (ArrowWidget *) widget; + + if (widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) { + if (arrow->style & WIDGET_ARROW_STYLE_CONSTRAINED) { + float min, max, step, precision; + + RNA_property_float_ui_range(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE], &min, &max, &step, &precision); + arrow->range = max - min; + arrow->min = min; + if (arrow->style & WIDGET_ARROW_STYLE_INVERTED) + arrow->offset = ARROW_RANGE * (max - (RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]))) / arrow->range; + else + arrow->offset = ARROW_RANGE * ((RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) - arrow->min) / arrow->range); + } + else { + /* we'd need to check the property type here but for now assume always float */ + arrow->offset = RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]); + } + } + else + arrow->offset = 0.0f; +} + +wmWidget *WIDGET_arrow_new(wmWidgetGroup *wgroup, int style) +{ + float dir_default[3] = {0.0f, 0.0f, 1.0f}; + ArrowWidget *arrow; + + if (!arraw_head_draw_info.init) { + arraw_head_draw_info.nverts = _WIDGET_nverts_arrow, + arraw_head_draw_info.ntris = _WIDGET_ntris_arrow, + arraw_head_draw_info.verts = _WIDGET_verts_arrow, + arraw_head_draw_info.normals = _WIDGET_normals_arrow, + arraw_head_draw_info.indices = _WIDGET_indices_arrow, + arraw_head_draw_info.init = true; + } + + /* inverted only makes sense in a constrained arrow */ + if (style & WIDGET_ARROW_STYLE_INVERTED) + style |= WIDGET_ARROW_STYLE_CONSTRAINED; + + arrow = MEM_callocN(sizeof(ArrowWidget), "arrowwidget"); + + + arrow->widget.draw = widget_arrow_draw; + arrow->widget.get_final_position = widget_arrow_get_final_pos; + arrow->widget.intersect = NULL; + arrow->widget.handler = widget_arrow_handler; + arrow->widget.invoke = widget_arrow_invoke; + arrow->widget.render_3d_intersection = widget_arrow_render_3d_intersect; + arrow->widget.bind_to_prop = widget_arrow_bind_to_prop; + arrow->widget.flag |= WM_WIDGET_SCALE_3D; + arrow->style = style; + copy_v3_v3(arrow->direction, dir_default); + + wm_widget_register(wgroup, &arrow->widget); + + return (wmWidget *)arrow; +} + +void WIDGET_arrow_set_color(struct wmWidget *widget, float color[4]) +{ + ArrowWidget *arrow = (ArrowWidget *)widget; + + copy_v4_v4(arrow->color, color); +} + +void WIDGET_arrow_set_direction(struct wmWidget *widget, float direction[3]) +{ + ArrowWidget *arrow = (ArrowWidget *)widget; + + copy_v3_v3(arrow->direction, direction); + normalize_v3(arrow->direction); +} + +void WIDGET_arrow_set_up_vector(struct wmWidget *widget, float direction[3]) +{ + ArrowWidget *arrow = (ArrowWidget *)widget; + + if (direction) { + copy_v3_v3(arrow->up, direction); + normalize_v3(arrow->up); + arrow->flag |= ARROW_UP_VECTOR_SET; + } + else { + arrow->flag &= ~ARROW_UP_VECTOR_SET; + } +} + + +/********* Dial widget ************/ + +typedef struct DialWidget { + wmWidget widget; + int style; + float direction[3]; + float color[4]; +} DialWidget; + +static void dial_draw_intern(DialWidget *dial, bool select, bool highlight, float scale) +{ + float rot[3][3]; + float mat[4][4]; + float up[3] = {0.0f, 0.0f, 1.0f}; + + rotation_between_vecs_to_mat3(rot, up, dial->direction); + copy_m4_m3(mat, rot); + copy_v3_v3(mat[3], dial->widget.origin); + mul_mat3_m4_fl(mat, scale); + + glPushMatrix(); + glMultMatrixf(&mat[0][0]); + + if (highlight) + glColor4f(1.0, 1.0, 0.0, 1.0); + else + glColor4fv(dial->color); + + widget_draw_intern(&dial_draw_info, select); + + glPopMatrix(); + +} + +static void widget_dial_render_3d_intersect(const struct bContext *C, struct wmWidget *widget, int selectionbase) +{ + DialWidget *dial = (DialWidget *)widget; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + /* enable clipping if needed */ + if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) { + double plane[4]; + copy_v3db_v3fl(plane, rv3d->viewinv[2]); + plane[3] = -dot_v3v3(rv3d->viewinv[2], widget->origin); + glClipPlane(GL_CLIP_PLANE0, plane); + glEnable(GL_CLIP_PLANE0); + } + + GPU_select_load_id(selectionbase); + dial_draw_intern(dial, true, false, dial->widget.scale); + + if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) { + glDisable(GL_CLIP_PLANE0); + } +} + +static void widget_dial_draw(struct wmWidget *widget, const struct bContext *C) +{ + DialWidget *dial = (DialWidget *)widget; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + /* enable clipping if needed */ + if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) { + double plane[4]; + copy_v3db_v3fl(plane, rv3d->viewinv[2]); + plane[3] = -dot_v3v3(rv3d->viewinv[2], widget->origin); + glClipPlane(GL_CLIP_PLANE0, plane); + glEnable(GL_CLIP_PLANE0); + } + + dial_draw_intern(dial, false, (widget->flag & WM_WIDGET_HIGHLIGHT) != 0, widget->scale); + + if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) { + glDisable(GL_CLIP_PLANE0); + } +} + +wmWidget *WIDGET_dial_new(int style) +{ + float dir_default[3] = {0.0f, 0.0f, 1.0f}; + DialWidget *dial; + + if (!dial_draw_info.init) { + dial_draw_info.nverts = _WIDGET_nverts_dial, + dial_draw_info.ntris = _WIDGET_ntris_dial, + dial_draw_info.verts = _WIDGET_verts_dial, + dial_draw_info.normals = _WIDGET_normals_dial, + dial_draw_info.indices = _WIDGET_indices_dial, + dial_draw_info.init = true; + } + + dial = MEM_callocN(sizeof(ArrowWidget), "arrowwidget"); + + dial->widget.draw = widget_dial_draw; + dial->widget.intersect = NULL; + dial->widget.render_3d_intersection = widget_dial_render_3d_intersect; + + dial->style = style; + copy_v3_v3(dial->direction, dir_default); + + return (wmWidget *)dial; +} + +void WIDGET_dial_set_color(struct wmWidget *widget, float color[4]) +{ + DialWidget *arrow = (DialWidget *)widget; + + copy_v4_v4(arrow->color, color); +} + +void WIDGET_dial_set_direction(struct wmWidget *widget, float direction[3]) +{ + DialWidget *dial = (DialWidget *)widget; + + copy_v3_v3(dial->direction, direction); + normalize_v3(dial->direction); +} + +/********* Cage widget ************/ + +enum { + WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE = 1, + WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT = 2, + WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT = 3, + WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP = 4, + WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN = 5 +}; + +#define WIDGET_RECT_MIN_WIDTH 15.0f +#define WIDGET_RESIZER_WIDTH 20.0f + +typedef struct RectTransformWidget { + wmWidget widget; + float offset[2]; /* position of widget */ + float w, h; /* dimensions of widget */ + float rotation; /* rotation of the rectangle */ + float scale[2]; /* scaling for the widget for non-destructive editing. */ + int style; +} RectTransformWidget; + +static void rect_transform_draw_corners(rctf *r, float offsetx, float offsety) +{ + glBegin(GL_LINES); + glVertex2f(r->xmin, r->ymin + offsety); + glVertex2f(r->xmin, r->ymin); + glVertex2f(r->xmin, r->ymin); + glVertex2f(r->xmin + offsetx, r->ymin); + + glVertex2f(r->xmax, r->ymin + offsety); + glVertex2f(r->xmax, r->ymin); + glVertex2f(r->xmax, r->ymin); + glVertex2f(r->xmax - offsetx, r->ymin); + + glVertex2f(r->xmax, r->ymax - offsety); + glVertex2f(r->xmax, r->ymax); + glVertex2f(r->xmax, r->ymax); + glVertex2f(r->xmax - offsetx, r->ymax); + + glVertex2f(r->xmin, r->ymax - offsety); + glVertex2f(r->xmin, r->ymax); + glVertex2f(r->xmin, r->ymax); + glVertex2f(r->xmin + offsetx, r->ymax); + glEnd(); +} + +static void rect_transform_draw_interaction(int highlighted, float half_w, float half_h, float w, float h) +{ + float verts[4][2]; +#if 0 + unsigned short elems[4] = {0, 1, 3, 2}; +#endif + + switch (highlighted) { + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT: + verts[0][0] = -half_w + w; + verts[0][1] = -half_h; + verts[1][0] = -half_w; + verts[1][1] = -half_h; + verts[2][0] = -half_w; + verts[2][1] = half_h; + verts[3][0] = -half_w + w; + verts[3][1] = half_h; + break; + + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT: + verts[0][0] = half_w - w; + verts[0][1] = -half_h; + verts[1][0] = half_w; + verts[1][1] = -half_h; + verts[2][0] = half_w; + verts[2][1] = half_h; + verts[3][0] = half_w - w; + verts[3][1] = half_h; + break; + + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN: + verts[0][0] = -half_w; + verts[0][1] = -half_h + h; + verts[1][0] = -half_w; + verts[1][1] = -half_h; + verts[2][0] = half_w; + verts[2][1] = -half_h; + verts[3][0] = half_w; + verts[3][1] = -half_h + h; + break; + + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP: + verts[0][0] = -half_w; + verts[0][1] = half_h - h; + verts[1][0] = -half_w; + verts[1][1] = half_h; + verts[2][0] = half_w; + verts[2][1] = half_h; + verts[3][0] = half_w; + verts[3][1] = half_h - h; + break; + + default: + return; + } + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, verts); + glLineWidth(3.0); + glColor3f(0.0, 0.0, 0.0); + glDrawArrays(GL_LINE_STRIP, 0, 3); + glLineWidth(1.0); + glColor3f(1.0, 1.0, 1.0); + glDrawArrays(GL_LINE_STRIP, 0, 3); +} + +static void widget_rect_transform_draw(struct wmWidget *widget, const struct bContext *UNUSED(C)) +{ + RectTransformWidget *cage = (RectTransformWidget *)widget; + rctf r; + float w = cage->w; + float h = cage->h; + float half_w = w / 2.0f; + float half_h = h / 2.0f; + float aspx = 1.0f, aspy = 1.0f; + + r.xmin = -half_w; + r.ymin = -half_h; + r.xmax = half_w; + r.ymax = half_h; + + glPushMatrix(); + glTranslatef(widget->origin[0] + cage->offset[0], widget->origin[1] + cage->offset[1], 0.0f); + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) + glScalef(cage->scale[0], cage->scale[0], 1.0); + else + glScalef(cage->scale[0], cage->scale[1], 1.0); + + if (w > h) + aspx = h / w; + else + aspy = w / h; + w = min_ff(aspx * w / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH / cage->scale[0]); + h = min_ff(aspy * h / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH / + ((cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) ? cage->scale[0] : cage->scale[1])); + + /* corner widgets */ + glColor3f(0.0, 0.0, 0.0); + glLineWidth(3.0); + + rect_transform_draw_corners(&r, w, h); + + /* corner widgets */ + glColor3f(1.0, 1.0, 1.0); + glLineWidth(1.0); + rect_transform_draw_corners(&r, w, h); + + rect_transform_draw_interaction(widget->highlighted_part, half_w, half_h, w, h); + glPopMatrix(); +} + +static int widget_rect_tranfrorm_get_cursor(wmWidget *widget) +{ + switch (widget->highlighted_part) { + case WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE: + return BC_HANDCURSOR; + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT: + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT: + return CURSOR_X_MOVE; + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN: + case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP: + return CURSOR_Y_MOVE; + default: + return CURSOR_STD; + } +} + +static int widget_rect_tranfrorm_intersect(struct bContext *UNUSED(C), const struct wmEvent *event, struct wmWidget *widget) +{ + RectTransformWidget *cage = (RectTransformWidget *)widget; + float mouse[2] = {event->mval[0], event->mval[1]}; + float point_local[2]; + float w = cage->w; + float h = cage->h; + float half_w = w / 2.0f; + float half_h = h / 2.0f; + //float matrot[2][2]; + bool isect; + rctf r; + float aspx = 1.0f, aspy = 1.0f; + + /* rotate mouse in relation to the center and relocate it */ + sub_v2_v2v2(point_local, mouse, widget->origin); + point_local[0] -= cage->offset[0]; + point_local[1] -= cage->offset[1]; + //rotate_m2(matrot, -cage->transform.rotation); + + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) + mul_v2_fl(point_local, 1.0f/cage->scale[0]); + else { + point_local[0] /= cage->scale[0]; + point_local[1] /= cage->scale[0]; + } + + if (cage->w > cage->h) + aspx = h / w; + else + aspy = w / h; + w = min_ff(aspx * w / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH / cage->scale[0]); + h = min_ff(aspy * h / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH / + ((cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) ? cage->scale[0] : cage->scale[1])); + + r.xmin = -half_w + w; + r.ymin = -half_h + h; + r.xmax = half_w - w; + r.ymax = half_h - h; + + isect = BLI_rctf_isect_pt_v(&r, point_local); + + if (isect) + return WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE; + + /* if widget does not have a scale intersection, don't do it */ + if (cage->style & (WIDGET_RECT_TRANSFORM_STYLE_SCALE | WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM)) { + r.xmin = -half_w; + r.ymin = -half_h; + r.xmax = -half_w + w; + r.ymax = half_h; + + isect = BLI_rctf_isect_pt_v(&r, point_local); + + if (isect) + return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT; + + r.xmin = half_w - w; + r.ymin = -half_h; + r.xmax = half_w; + r.ymax = half_h; + + isect = BLI_rctf_isect_pt_v(&r, point_local); + + if (isect) + return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT; + + r.xmin = -half_w; + r.ymin = -half_h; + r.xmax = half_w; + r.ymax = -half_h + h; + + isect = BLI_rctf_isect_pt_v(&r, point_local); + + if (isect) + return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN; + + r.xmin = -half_w; + r.ymin = half_h - h; + r.xmax = half_w; + r.ymax = half_h; + + isect = BLI_rctf_isect_pt_v(&r, point_local); + + if (isect) + return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP; + } + + return 0; +} + +typedef struct RectTransformInteraction { + float orig_mouse[2]; + float orig_offset[2]; + float orig_scale[2]; +} RectTransformInteraction; + +static bool widget_rect_transform_get_property(struct wmWidget *widget, int slot, float *value) +{ + PropertyType type = RNA_property_type(widget->props[slot]); + + if (type != PROP_FLOAT) { + fprintf(stderr, "Rect Transform widget can only be bound to float properties"); + return false; + } + else { + if (slot == RECT_TRANSFORM_SLOT_OFFSET) { + if (RNA_property_array_length(&widget->ptr[slot], widget->props[slot]) != 2) { + fprintf(stderr, "Rect Transform widget offset not only be bound to array float property"); + return false; + } + + RNA_property_float_get_array(&widget->ptr[slot], widget->props[slot], value); + } + else if (slot == RECT_TRANSFORM_SLOT_SCALE) { + RectTransformWidget *cage = (RectTransformWidget *)widget; + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) + *value = RNA_property_float_get(&widget->ptr[slot], widget->props[slot]); + else { + if (RNA_property_array_length(&widget->ptr[slot], widget->props[slot]) != 2) { + fprintf(stderr, "Rect Transform widget scale not only be bound to array float property"); + return false; + } + RNA_property_float_get_array(&widget->ptr[slot], widget->props[slot], value); + } + } + } + + return true; +} + +static int widget_rect_transform_invoke(struct bContext *UNUSED(C), const struct wmEvent *event, struct wmWidget *widget) +{ + RectTransformWidget *cage = (RectTransformWidget *) widget; + RectTransformInteraction *data = MEM_callocN(sizeof (RectTransformInteraction), "cage_interaction"); + + copy_v2_v2(data->orig_offset, cage->offset); + copy_v2_v2(data->orig_scale, cage->scale); + + data->orig_mouse[0] = event->mval[0]; + data->orig_mouse[1] = event->mval[1]; + + widget->interaction_data = data; + + return OPERATOR_RUNNING_MODAL; +} + +static int widget_rect_transform_handler(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget) +{ + RectTransformWidget *cage = (RectTransformWidget *) widget; + RectTransformInteraction *data = widget->interaction_data; + ARegion *ar = CTX_wm_region(C); + float valuex, valuey; + /* needed here as well in case clamping occurs */ + float orig_ofx = cage->offset[0], orig_ofy = cage->offset[1]; + + valuex = (event->mval[0] - data->orig_mouse[0]); + valuey = (event->mval[1] - data->orig_mouse[1]); + + if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE) { + cage->offset[0] = data->orig_offset[0] + valuex; + cage->offset[1] = data->orig_offset[1] + valuey; + } + else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT) { + cage->offset[0] = data->orig_offset[0] + valuex / 2.0; + cage->scale[0] = (cage->w * data->orig_scale[0] - valuex) / cage->w; + } + else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT) { + cage->offset[0] = data->orig_offset[0] + valuex / 2.0; + cage->scale[0] = (cage->w * data->orig_scale[0] + valuex) / cage->w; + } + else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN) { + cage->offset[1] = data->orig_offset[1] + valuey / 2.0; + + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) { + cage->scale[0] = (cage->h * data->orig_scale[0] - valuey) / cage->h; + } + else { + cage->scale[1] = (cage->h * data->orig_scale[1] - valuey) / cage->h; + } + } + else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP) { + cage->offset[1] = data->orig_offset[1] + valuey / 2.0; + + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) { + cage->scale[0] = (cage->h * data->orig_scale[0] + valuey) / cage->h; + } + else { + cage->scale[1] = (cage->h * data->orig_scale[1] + valuey) / cage->h; + } + } + + /* clamping - make sure widget is at least 5 pixels wide */ + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) { + if (cage->scale[0] < WIDGET_RECT_MIN_WIDTH / cage->h || + cage->scale[0] < WIDGET_RECT_MIN_WIDTH / cage->w) + { + cage->scale[0] = max_ff(WIDGET_RECT_MIN_WIDTH / cage->h, WIDGET_RECT_MIN_WIDTH / cage->w); + cage->offset[0] = orig_ofx; + cage->offset[1] = orig_ofy; + } + } + else { + if (cage->scale[0] < WIDGET_RECT_MIN_WIDTH / cage->w) { + cage->scale[0] = WIDGET_RECT_MIN_WIDTH / cage->w; + cage->offset[0] = orig_ofx; + } + if (cage->scale[1] < WIDGET_RECT_MIN_WIDTH / cage->h) { + cage->scale[1] = WIDGET_RECT_MIN_WIDTH / cage->h; + cage->offset[1] = orig_ofy; + } + } + + if (widget->props[RECT_TRANSFORM_SLOT_OFFSET]) { + RNA_property_float_set_array(&widget->ptr[RECT_TRANSFORM_SLOT_OFFSET], widget->props[RECT_TRANSFORM_SLOT_OFFSET], cage->offset); + RNA_property_update(C, &widget->ptr[RECT_TRANSFORM_SLOT_OFFSET], widget->props[RECT_TRANSFORM_SLOT_OFFSET]); + } + + if (widget->props[RECT_TRANSFORM_SLOT_SCALE]) { + if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) + RNA_property_float_set(&widget->ptr[RECT_TRANSFORM_SLOT_SCALE], widget->props[RECT_TRANSFORM_SLOT_SCALE], cage->scale[0]); + else + RNA_property_float_set_array(&widget->ptr[RECT_TRANSFORM_SLOT_SCALE], widget->props[RECT_TRANSFORM_SLOT_SCALE], cage->scale); + RNA_property_update(C, &widget->ptr[RECT_TRANSFORM_SLOT_SCALE], widget->props[RECT_TRANSFORM_SLOT_SCALE]); + } + + /* tag the region for redraw */ + ED_region_tag_redraw(ar); + + return OPERATOR_PASS_THROUGH; +} + +static void widget_rect_transform_bind_to_prop(struct wmWidget *widget, int slot) +{ + RectTransformWidget *cage = (RectTransformWidget *) widget; + + if (slot == RECT_TRANSFORM_SLOT_OFFSET) + widget_rect_transform_get_property(widget, RECT_TRANSFORM_SLOT_OFFSET, cage->offset); + if (slot == RECT_TRANSFORM_SLOT_SCALE) + widget_rect_transform_get_property(widget, RECT_TRANSFORM_SLOT_SCALE, cage->scale); +} + +struct wmWidget *WIDGET_rect_transform_new(struct wmWidgetGroup *wgroup, int style, float width, float height) +{ + RectTransformWidget *cage = MEM_callocN(sizeof(RectTransformWidget), "CageWidget"); + + cage->widget.draw = widget_rect_transform_draw; + cage->widget.invoke = widget_rect_transform_invoke; + cage->widget.bind_to_prop = widget_rect_transform_bind_to_prop; + cage->widget.handler = widget_rect_transform_handler; + cage->widget.intersect = widget_rect_tranfrorm_intersect; + cage->widget.get_cursor = widget_rect_tranfrorm_get_cursor; + cage->widget.max_prop = 2; + cage->scale[0] = cage->scale[1] = 1.0f; + cage->style = style; + cage->w = width; + cage->h = height; + + wm_widget_register(wgroup, &cage->widget); + + return (wmWidget *)cage; +} + +void WIDGET_rect_transform_set_offset(struct wmWidget *widget, float offset[2]) +{ + RectTransformWidget *cage = (RectTransformWidget *)widget; + + copy_v2_v2(cage->offset, offset); +} + +/********* Facemap widget ************/ + +typedef struct FacemapWidget { + wmWidget widget; + Object *ob; + int facemap; + int style; + float color[4]; +} FacemapWidget; + + +static void widget_facemap_draw(struct wmWidget *widget, const struct bContext *C) +{ + FacemapWidget *fmap_widget = (FacemapWidget *)widget; + glPushMatrix(); + glMultMatrixf(&fmap_widget->ob->obmat[0][0]); + ED_draw_object_facemap(CTX_data_scene(C), fmap_widget->ob, fmap_widget->facemap); + glPopMatrix(); +} + +static void widget_facemap_render_3d_intersect(const struct bContext *C, struct wmWidget *widget, int selectionbase) +{ + GPU_select_load_id(selectionbase); + widget_facemap_draw(widget, C); +} + + +void WIDGET_facemap_set_color(struct wmWidget *widget, float color[4]) +{ + FacemapWidget *fmap_widget = (FacemapWidget *)widget; + copy_v4_v4(fmap_widget->color, color); +} + +struct wmWidget *WIDGET_facemap_new(struct wmWidgetGroup *wgroup, int style, struct Object *ob, int facemap) +{ + FacemapWidget *fmap_widget = MEM_callocN(sizeof(RectTransformWidget), "CageWidget"); + + fmap_widget->widget.draw = widget_facemap_draw; +// fmap_widget->widget.invoke = NULL; +// fmap_widget->widget.bind_to_prop = NULL; +// fmap_widget->widget.handler = NULL; + fmap_widget->widget.render_3d_intersection = widget_facemap_render_3d_intersect; + fmap_widget->ob = ob; + fmap_widget->facemap = facemap; + fmap_widget->style = style; + + wm_widget_register(wgroup, &fmap_widget->widget); + + return (wmWidget *)fmap_widget; +} + + +void fix_linking_widget_lib(void) +{ + (void) 0; +} diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 4f019c0a913..8edccbce25d 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -165,6 +165,8 @@ void WM_init(bContext *C, int argc, const char **argv) /* Enforce loading the UI for the initial homefile */ G.fileflags &= ~G_FILE_NO_UI; + ED_spacedropwidgets_init(); + /* get the default database, plus a wm */ wm_homefile_read(C, NULL, G.factory_startup, NULL); @@ -481,6 +483,9 @@ void WM_exit_ext(bContext *C, const bool do_python) ED_clipboard_posebuf_free(); BKE_node_clipboard_clear(); + /* widgetmaps after freeing blender, so no deleted data get accessed during cleaning up of areas */ + WM_widgetmaptypes_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 c97b8e588e8..3ee93cf3f13 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -4563,6 +4563,36 @@ static void WM_OT_radial_control(wmOperatorType *ot) RNA_def_boolean(ot->srna, "secondary_tex", false, "Secondary Texture", "Tweak brush secondary/mask texture"); } +/* ************************** widget property tweak *************** */ + +static int widget_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static int widget_tweak_modal(bContext *UNUSED(C), wmOperator *UNUSED(op), const wmEvent *event) +{ + if (event->type == EVT_WIDGET_RELEASED) + return OPERATOR_FINISHED; + + return OPERATOR_RUNNING_MODAL; +} + +static void WM_OT_widget_tweak(wmOperatorType *ot) +{ + ot->name = "Widget Tweak"; + ot->idname = "WM_OT_widget_tweak"; + ot->description = "Tweak property attached to a widget"; + + ot->invoke = widget_tweak_invoke; + ot->modal = widget_tweak_modal; + + ot->flag = OPTYPE_REGISTER | OPTYPE_BLOCKING | OPTYPE_UNDO; +} + + + /* ************************** timer for testing ***************** */ /* uses no type defines, fully local testing function anyway... ;) */ @@ -4920,6 +4950,7 @@ void wm_operatortype_init(void) WM_operatortype_append(WM_OT_call_menu); WM_operatortype_append(WM_OT_call_menu_pie); WM_operatortype_append(WM_OT_radial_control); + WM_operatortype_append(WM_OT_widget_tweak); WM_operatortype_append(WM_OT_stereo3d_set); #if defined(WIN32) WM_operatortype_append(WM_OT_console_toggle); @@ -5309,3 +5340,11 @@ EnumPropertyItem *RNA_mask_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->mask.first : NULL, true); } +EnumPropertyItem *RNA_cachelibrary_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +{ + return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->cache_library.first : NULL, false); +} +EnumPropertyItem *RNA_cachelibrary_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +{ + return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->cache_library.first : NULL, true); +} diff --git a/source/blender/windowmanager/intern/wm_widgets.c b/source/blender/windowmanager/intern/wm_widgets.c new file mode 100644 index 00000000000..ad915311015 --- /dev/null +++ b/source/blender/windowmanager/intern/wm_widgets.c @@ -0,0 +1,909 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation but based + * on ghostwinlay.c (C) 2001-2002 by NaN Holding BV + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2008 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/intern/wm_widgets.c + * \ingroup wm + * + * Window management, widget API. + */ + +#include <stdlib.h> +#include <string.h> + +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_view3d_types.h" +#include "DNA_userdef_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_idprop.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" + +#include "ED_view3d.h" +#include "ED_screen.h" +#include "ED_util.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm.h" +#include "wm_window.h" +#include "wm_event_system.h" +#include "wm_event_types.h" +#include "wm_draw.h" + +#include "GL/glew.h" +#include "GPU_select.h" + +#include "RNA_access.h" +#include "BPY_extern.h" + +/** + * This is a container for all widget types that can be instantiated in a region. + * (similar to dropboxes). + * + * \note There is only ever one of these for every (area, region) combination. + */ +typedef struct wmWidgetMapType { + struct wmWidgetMapType *next, *prev; + char idname[64]; + short spaceid, regionid; + /** + * Check if widgetmap does 3D drawing + * (uses a different kind of interaction), + * - 3d: use glSelect buffer. + * - 2d: use simple cursor position intersection test. */ + bool is_3d; + /* types of widgetgroups for this widgetmap type */ + ListBase widgetgrouptypes; +} wmWidgetMapType; + + +/* store all widgetboxmaps here. Anyone who wants to register a widget for a certain + * area type can query the widgetbox to do so */ +static ListBase widgetmaptypes = {NULL, NULL}; + + +struct wmWidgetGroupType *WM_widgetgrouptype_new( + int (*poll)(const struct bContext *C, struct wmWidgetGroupType *), + void (*draw)(const struct bContext *, struct wmWidgetGroup *), + struct Main *bmain, const char *mapidname, short spaceid, short regionid, bool is_3d + ) +{ + bScreen *sc; + struct wmWidgetMapType *wmaptype = WM_widgetmaptype_find(mapidname, spaceid, regionid, is_3d, false); + wmWidgetGroupType *wgrouptype; + + if (!wmaptype) { + fprintf(stderr, "widgetgrouptype creation: widgetmap type does not exist"); + return NULL; + } + + wgrouptype = MEM_callocN(sizeof(wmWidgetGroupType), "widgetgroup"); + + wgrouptype->poll = poll; + wgrouptype->draw = draw; + wgrouptype->spaceid = spaceid; + wgrouptype->regionid = regionid; + wgrouptype->is_3d = is_3d; + BLI_strncpy(wgrouptype->mapidname, mapidname, 64); + + /* add the type for future created areas of the same type */ + BLI_addtail(&wmaptype->widgetgrouptypes, wgrouptype); + + /* now create a widget for all existing areas. (main is missing when we create new areas so not needed) */ + if (bmain) { + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + + for (sl = sa->spacedata.first; sl; sl = sl->next) { + ARegion *ar; + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + + for (ar = lb->first; ar; ar = ar->next) { + wmWidgetMap *wmap; + for (wmap = ar->widgetmaps.first; wmap; wmap = wmap->next) { + if (wmap->type == wmaptype) { + wmWidgetGroup *wgroup = MEM_callocN(sizeof(wmWidgetGroup), "widgetgroup"); + wgroup->type = wgrouptype; + + /* just add here, drawing will occur on next update */ + BLI_addtail(&wmap->widgetgroups, wgroup); + wm_widgetmap_set_highlighted_widget(wmap, NULL, NULL, 0); + ED_region_tag_redraw(ar); + } + } + } + } + } + } + } + + return wgrouptype; +} + +wmWidget *WM_widget_new(void (*draw)(struct wmWidget *customdata, const struct bContext *C), + void (*render_3d_intersection)(const struct bContext *C, struct wmWidget *customdata, int selectionbase), + int (*intersect)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget), + int (*handler)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget)) +{ + wmWidget *widget; + + widget = MEM_callocN(sizeof(wmWidget), "widget"); + + widget->draw = draw; + widget->handler = handler; + widget->intersect = intersect; + widget->render_3d_intersection = render_3d_intersection; + + return widget; +} + +void WM_widget_property(struct wmWidget *widget, int slot, struct PointerRNA *ptr, const char *propname) +{ + if (slot < 0 || slot >= widget->max_prop) { + fprintf(stderr, "invalid index %d when binding property for widget type %s\n", slot, widget->idname); + return; + } + + /* if widget evokes an operator we cannot use it for property manipulation */ + widget->opname = NULL; + widget->ptr[slot] = *ptr; + widget->props[slot] = RNA_struct_find_property(ptr, propname); + + if (widget->bind_to_prop) + widget->bind_to_prop(widget, slot); +} + +PointerRNA *WM_widget_operator(struct wmWidget *widget, const char *opname) +{ + wmOperatorType *ot = WM_operatortype_find(opname, 0); + + if (ot) { + widget->opname = opname; + + WM_operator_properties_create_ptr(&widget->opptr, ot); + + return &widget->opptr; + } + else { + fprintf(stderr, "Error binding operator to widget: operator %s not found!\n", opname); + } + + return NULL; +} + + +static void wm_widget_delete(ListBase *widgetlist, wmWidget *widget) +{ + if (widget->opptr.data) { + WM_operator_properties_free(&widget->opptr); + } + + MEM_freeN(widget->props); + MEM_freeN(widget->ptr); + + BLI_freelinkN(widgetlist, widget); +} + + +static void widget_calculate_scale(wmWidget *widget, const bContext *C) +{ + float scale = 1.0f; + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (rv3d && !(U.tw_flag & V3D_3D_WIDGETS) && (widget->flag & WM_WIDGET_SCALE_3D)) { + if (widget->get_final_position) { + float position[3]; + + widget->get_final_position(widget, position); + scale = ED_view3d_pixel_size(rv3d, position) * U.tw_size; + } + else { + scale = ED_view3d_pixel_size(rv3d, widget->origin) * U.tw_size; + } + } + + widget->scale = scale * widget->user_scale; +} + +static bool widgets_compare(wmWidget *widget, wmWidget *widget2) +{ + int i; + + if (widget->max_prop != widget2->max_prop) + return false; + + for (i = 0; i < widget->max_prop; i++) { + if (widget->props[i] != widget2->props[i] || widget->ptr[i].data != widget2->ptr[i].data) + return false; + } + + return true; +} + +void WM_widgets_update(const bContext *C, wmWidgetMap *wmap) +{ + wmWidget *widget; + + if (!wmap) + return; + + widget = wmap->active_widget; + + if (widget) { + widget_calculate_scale(widget, C); + } + else if (wmap->widgetgroups.first) { + wmWidgetGroup *wgroup; + + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) { + if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type)) + { + wmWidget *highlighted = NULL; + + /* first delete and recreate the widgets */ + for (widget = wgroup->widgets.first; widget;) { + wmWidget *widget_next = widget->next; + + /* do not delete the highlighted widget, instead keep it to compare with the new one */ + if (widget->flag & WM_WIDGET_HIGHLIGHT) { + highlighted = widget; + BLI_remlink(&wgroup->widgets, widget); + widget->next = widget->prev = NULL; + } + else { + wm_widget_delete(&wgroup->widgets, widget); + } + widget = widget_next; + } + + if (wgroup->type->draw) { + wgroup->type->draw(C, wgroup); + } + + if (highlighted) { + for (widget = wgroup->widgets.first; widget; widget = widget->next) { + if (widgets_compare(widget, highlighted)) + { + widget->flag |= WM_WIDGET_HIGHLIGHT; + wmap->highlighted_widget = widget; + widget->highlighted_part = highlighted->highlighted_part; + wm_widget_delete(&wgroup->widgets, highlighted); + highlighted = NULL; + break; + } + } + } + + /* if we don't find a highlighted widget, delete the old one here */ + if (highlighted) { + MEM_freeN(highlighted); + highlighted = NULL; + wmap->highlighted_widget = NULL; + } + + for (widget = wgroup->widgets.first; widget; widget = widget->next) { + widget_calculate_scale(widget, C); + } + } + } + } +} + +void WM_widgets_draw(const bContext *C, wmWidgetMap *wmap, bool in_scene) +{ + wmWidget *widget; + bool use_lighting; + + if (!wmap) + return; + + use_lighting = (U.tw_flag & V3D_SHADED_WIDGETS) != 0; + + if (use_lighting) { + float lightpos[4] = {0.0, 0.0, 1.0, 0.0}; + float diffuse[4] = {1.0, 1.0, 1.0, 0.0}; + + glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + glPushMatrix(); + glLoadIdentity(); + glLightfv(GL_LIGHT0, GL_POSITION, lightpos); + glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); + glPopMatrix(); + } + + widget = wmap->active_widget; + + if (widget && in_scene == ((widget->flag & WM_WIDGET_SCENE_DEPTH)!= 0)) { + /* notice that we don't update the widgetgroup, widget is now on its own, it should have all + * relevant data to update itself */ + widget->draw(widget, C); + } + else if (wmap->widgetgroups.first) { + wmWidgetGroup *wgroup; + + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) { + if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type)) + { + for (widget = wgroup->widgets.first; widget; widget = widget->next) { + if ((!(widget->flag & WM_WIDGET_DRAW_HOVER) || (widget->flag & WM_WIDGET_HIGHLIGHT)) && + ((widget->flag & WM_WIDGET_SCENE_DEPTH) != 0) == in_scene) + { + widget->draw(widget, C); + } + } + } + } + } + + if (use_lighting) + glPopAttrib(); +} + +void WM_event_add_area_widgetmap_handlers(ARegion *ar) +{ + wmWidgetMap *wmap; + wmEventHandler *handler; + + for (wmap = ar->widgetmaps.first; wmap; wmap = wmap->next) { + handler = MEM_callocN(sizeof(wmEventHandler), "widget handler"); + + handler->widgetmap = wmap; + BLI_addtail(&ar->handlers, handler); + } +} + +void WM_modal_handler_attach_widgetgroup(bContext *C, wmEventHandler *handler, wmWidgetGroupType *wgrouptype, wmOperator *op) +{ + /* maybe overly careful, but widgetgrouptype could come from a failed creation */ + if (!wgrouptype) { + return; + } + + /* now instantiate the widgetmap */ + wgrouptype->op = op; + + if (handler->op_region && handler->op_region->widgetmaps.first) { + wmWidgetMap *wmap; + for (wmap = handler->op_region->widgetmaps.first; wmap; wmap = wmap->next) { + wmWidgetMapType *wmaptype = wmap->type; + + if (wmaptype->spaceid == wgrouptype->spaceid && wmaptype->regionid == wgrouptype->regionid) { + handler->widgetmap = wmap; + } + } + } + + WM_event_add_mousemove(C); +} + +bool wm_widget_register(struct wmWidgetGroup *wgroup, wmWidget *widget) +{ + widget->user_scale = 1.0f; + + /* create at least one property for interaction */ + if (widget->max_prop == 0) { + widget->max_prop = 1; + } + + widget->props = MEM_callocN(sizeof(PropertyRNA *) * widget->max_prop, "widget->props"); + widget->ptr = MEM_callocN(sizeof(PointerRNA) * widget->max_prop, "widget->ptr"); + + BLI_addtail(&wgroup->widgets, widget); + return true; +} + +void WM_widget_set_origin(struct wmWidget *widget, float origin[3]) +{ + copy_v3_v3(widget->origin, origin); +} + +void WM_widget_set_3d_scale(struct wmWidget *widget, bool scale) +{ + if (scale) { + widget->flag |= WM_WIDGET_SCALE_3D; + } + else { + widget->flag &= ~WM_WIDGET_SCALE_3D; + } +} + + +void WM_widget_set_draw_on_hover_only(struct wmWidget *widget, bool draw) +{ + if (draw) { + widget->flag |= WM_WIDGET_DRAW_HOVER; + } + else { + widget->flag &= ~WM_WIDGET_DRAW_HOVER; + } +} + +void WM_widget_set_scene_depth(struct wmWidget *widget, bool scene) +{ + if (scene) { + widget->flag |= WM_WIDGET_SCENE_DEPTH; + } + else { + widget->flag &= ~WM_WIDGET_SCENE_DEPTH; + } +} + + +void WM_widget_set_scale(struct wmWidget *widget, float scale) +{ + widget->user_scale = scale; +} + + +wmWidgetMapType *WM_widgetmaptype_find(const char *idname, int spaceid, int regionid, bool is_3d, bool create) +{ + wmWidgetMapType *wmaptype; + + for (wmaptype = widgetmaptypes.first; wmaptype; wmaptype = wmaptype->next) { + if (wmaptype->spaceid == spaceid && wmaptype->regionid == regionid && wmaptype->is_3d == is_3d + && strcmp(wmaptype->idname, idname) == 0) { + return wmaptype; + } + } + + if (!create) return NULL; + + wmaptype = MEM_callocN(sizeof(wmWidgetMapType), "widgettype list"); + wmaptype->spaceid = spaceid; + wmaptype->regionid = regionid; + wmaptype->is_3d = is_3d; + BLI_strncpy(wmaptype->idname, idname, 64); + BLI_addhead(&widgetmaptypes, wmaptype); + + return wmaptype; +} + +void WM_widgetmaptypes_free(void) +{ + wmWidgetMapType *wmaptype; + + for (wmaptype = widgetmaptypes.first; wmaptype; wmaptype = wmaptype->next) { + BLI_freelistN(&wmaptype->widgetgrouptypes); + } + BLI_freelistN(&widgetmaptypes); + + fix_linking_widget_lib(); +} + +bool wm_widgetmap_is_3d(struct wmWidgetMap *wmap) +{ + return wmap->type->is_3d; +} + +static void widget_find_active_3D_loop(bContext *C, ListBase *visible_widgets) +{ + int selectionbase = 0; + LinkData *link; + wmWidget *widget; + + for (link = visible_widgets->first; link; link = link->next) { + widget = link->data; + /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected widget part id */ + widget->render_3d_intersection(C, widget, selectionbase << 8); + + selectionbase++; + } +} + +static int wm_widget_find_highlighted_3D_intern (ListBase *visible_widgets, bContext *C, const struct wmEvent *event, float hotspot) +{ + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + View3D *v3d = sa->spacedata.first; + RegionView3D *rv3d = ar->regiondata; + rctf rect, selrect; + GLuint buffer[64]; // max 4 items per select, so large enuf + short hits; + const bool do_passes = GPU_select_query_check_active(); + + extern void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect); + + rect.xmin = event->mval[0] - hotspot; + rect.xmax = event->mval[0] + hotspot; + rect.ymin = event->mval[1] - hotspot; + rect.ymax = event->mval[1] + hotspot; + + selrect = rect; + + view3d_winmatrix_set(ar, v3d, &rect); + mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat); + + if (do_passes) + GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_NEAREST_FIRST_PASS, 0); + else + GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_ALL, 0); + /* do the drawing */ + widget_find_active_3D_loop(C, visible_widgets); + + hits = GPU_select_end(); + + if (do_passes) { + GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_NEAREST_SECOND_PASS, hits); + widget_find_active_3D_loop(C, visible_widgets); + GPU_select_end(); + } + + view3d_winmatrix_set(ar, v3d, NULL); + mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat); + + if (hits == 1) { + return buffer[3]; + + } + /* find the widget the value belongs to */ + else if (hits > 1) { + GLuint val, dep, mindep = 0, minval = -1; + int a; + + /* we compare the hits in buffer, but value centers highest */ + /* we also store the rotation hits separate (because of arcs) and return hits on other widgets if there are */ + + for (a = 0; a < hits; a++) { + dep = buffer[4 * a + 1]; + val = buffer[4 * a + 3]; + + if (minval == -1 || dep < mindep) { + mindep = dep; + minval = val; + } + } + + return minval; + } + return -1; +} + +static void wm_prepare_visible_widgets_3D(struct wmWidgetMap *wmap, ListBase *visible_widgets, bContext *C) +{ + wmWidget *widget; + wmWidgetGroup *wgroup; + + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) { + if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type)) { + for (widget = wgroup->widgets.first; widget; widget = widget->next) { + if (widget->render_3d_intersection) { + BLI_addhead(visible_widgets, BLI_genericNodeN(widget)); + } + } + } + } +} + +wmWidget *wm_widget_find_highlighted_3D(struct wmWidgetMap *wmap, bContext *C, const struct wmEvent *event, unsigned char *part) +{ + int ret; + wmWidget *result = NULL; + + ListBase visible_widgets = {0}; + + wm_prepare_visible_widgets_3D(wmap, &visible_widgets, C); + + *part = 0; + /* set up view matrices */ + view3d_operator_needs_opengl(C); + + ret = wm_widget_find_highlighted_3D_intern(&visible_widgets, C, event, 0.5f * (float)U.tw_hotspot); + + if (ret != -1) { + LinkData *link; + int retsec; + retsec = wm_widget_find_highlighted_3D_intern(&visible_widgets, C, event, 0.2f * (float)U.tw_hotspot); + + if (retsec != -1) + ret = retsec; + + link = BLI_findlink(&visible_widgets, ret >> 8); + *part = ret & 255; + result = link->data; + } + + BLI_freelistN(&visible_widgets); + + return result; +} + +wmWidget *wm_widget_find_highlighted(struct wmWidgetMap *wmap, bContext *C, const struct wmEvent *event, unsigned char *part) +{ + wmWidget *widget; + wmWidgetGroup *wgroup; + + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) { + if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type)) { + for (widget = wgroup->widgets.first; widget; widget = widget->next) { + if (widget->intersect) { + if ((*part = widget->intersect(C, event, widget))) + return widget; + } + } + } + } + + return NULL; +} + +bool WM_widgetmap_cursor_set(wmWidgetMap *wmap, wmWindow *win) +{ + for (; wmap; wmap = wmap->next) { + wmWidget *widget = wmap->highlighted_widget; + if (widget && widget->get_cursor) { + WM_cursor_set(win, widget->get_cursor(widget)); + return true; + } + } + + return false; +} + +void wm_widgetmap_set_highlighted_widget(struct wmWidgetMap *wmap, struct bContext *C, struct wmWidget *widget, unsigned char part) +{ + if ((widget != wmap->highlighted_widget) || (widget && part != widget->highlighted_part)) { + if (wmap->highlighted_widget) { + wmap->highlighted_widget->flag &= ~WM_WIDGET_HIGHLIGHT; + wmap->highlighted_widget->highlighted_part = 0; + } + + wmap->highlighted_widget = widget; + + if (widget) { + widget->flag |= WM_WIDGET_HIGHLIGHT; + widget->highlighted_part = part; + + if (C && widget->get_cursor) { + wmWindow *win = CTX_wm_window(C); + WM_cursor_set(win, widget->get_cursor(widget)); + } + } + 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); + } + } +} + +struct wmWidget *wm_widgetmap_get_highlighted_widget(struct wmWidgetMap *wmap) +{ + return wmap->highlighted_widget; +} + +void wm_widgetmap_set_active_widget(struct wmWidgetMap *wmap, struct bContext *C, struct wmEvent *event, struct wmWidget *widget, bool call_op) +{ + if (widget) { + if (call_op) { + wmOperatorType *ot; + const char *opname = (widget->opname) ? widget->opname : "WM_OT_widget_tweak"; + + ot = WM_operatortype_find(opname, 0); + + if (ot) { + /* first activate the widget itself */ + if (widget->invoke && widget->handler) { + widget->flag |= WM_WIDGET_ACTIVE; + widget->invoke(C, event, widget); + wmap->active_widget = widget; + } + + /* if operator runs modal, we will need to activate the current widgetmap on the operator handler, so it can + * process events first, then pass them on to the operator */ + if (WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &widget->opptr) == OPERATOR_RUNNING_MODAL) { + /* check if operator added a a modal event handler */ + wmEventHandler *handler = CTX_wm_window(C)->modalhandlers.first; + + if (handler && handler->op && handler->op->type == ot) { + handler->widgetmap = wmap; + } + } + + /* we failed to hook the widget to the operator handler or operator was cancelled, return */ + if (!wmap->active_widget) { + widget->flag &= ~WM_WIDGET_ACTIVE; + /* first activate the widget itself */ + if (widget->interaction_data) { + MEM_freeN(widget->interaction_data); + widget->interaction_data = NULL; + } + } + return; + } + else { + printf("Widget error: operator not found"); + wmap->active_widget = NULL; + return; + } + } + else { + if (widget->invoke && widget->handler) { + widget->flag |= WM_WIDGET_ACTIVE; + widget->invoke(C, event, widget); + wmap->active_widget = widget; + } + } + } + else { + widget = wmap->active_widget; + + /* deactivate, widget but first take care of some stuff */ + if (widget) { + widget->flag &= ~WM_WIDGET_ACTIVE; + /* first activate the widget itself */ + if (widget->interaction_data) { + MEM_freeN(widget->interaction_data); + widget->interaction_data = NULL; + } + } + wmap->active_widget = NULL; + + if (C) { + ARegion *ar = CTX_wm_region(C); + ED_region_tag_redraw(ar); + WM_event_add_mousemove(C); + } + } +} + +struct wmWidget *wm_widgetmap_get_active_widget(struct wmWidgetMap *wmap) +{ + return wmap->active_widget; +} + + +struct wmWidgetMap *WM_widgetmap_from_type(const char *idname, int spaceid, int regionid, bool is_3d) +{ + wmWidgetMapType *wmaptype = WM_widgetmaptype_find(idname, spaceid, regionid, is_3d, true); + wmWidgetGroupType *wgrouptype = wmaptype->widgetgrouptypes.first; + wmWidgetMap *wmap; + + wmap = MEM_callocN(sizeof(wmWidgetMap), "WidgetMap"); + wmap->type = wmaptype; + + /* create all widgetgroups for this widgetmap. We may create an empty one too in anticipation of widgets from operators etc */ + for (; wgrouptype; wgrouptype = wgrouptype->next) { + wmWidgetGroup *wgroup = MEM_callocN(sizeof(wmWidgetGroup), "widgetgroup"); + wgroup->type = wgrouptype; + BLI_addtail(&wmap->widgetgroups, wgroup); + } + + return wmap; +} + +void WM_widgetmap_delete(struct wmWidgetMap *wmap) +{ + wmWidgetGroup *wgroup; + + if (!wmap) + return; + + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) { + wmWidget *widget; + + for (widget = wgroup->widgets.first; widget;) { + wmWidget *widget_next = widget->next; + wm_widget_delete(&wgroup->widgets, widget); + widget = widget_next; + } + } + BLI_freelistN(&wmap->widgetgroups); + + MEM_freeN(wmap); +} + +static void wm_widgetgroup_free(bContext *C, wmWidgetMap *wmap, wmWidgetGroup *wgroup) +{ + wmWidget *widget; + + for (widget = wgroup->widgets.first; widget;) { + wmWidget *widget_next = widget->next; + if (widget->flag & WM_WIDGET_HIGHLIGHT) { + wm_widgetmap_set_highlighted_widget(wmap, C, NULL, 0); + } + if (widget->flag & WM_WIDGET_ACTIVE) { + wm_widgetmap_set_active_widget(wmap, C, NULL, NULL, false); + } + wm_widget_delete(&wgroup->widgets, widget); + widget = widget_next; + } + +#ifdef WITH_PYTHON + if (wgroup->py_instance) { + /* do this first in case there are any __del__ functions or + * similar that use properties */ + BPY_DECREF_RNA_INVALIDATE(wgroup->py_instance); + } +#endif + + if (wgroup->reports && (wgroup->reports->flag & RPT_FREE)) { + BKE_reports_clear(wgroup->reports); + MEM_freeN(wgroup->reports); + } + + BLI_remlink(&wmap->widgetgroups, wgroup); + MEM_freeN(wgroup); +} + +void WM_widgetgrouptype_unregister(bContext *C, Main *bmain, wmWidgetGroupType *wgrouptype) +{ + bScreen *sc; + wmWidgetMapType *wmaptype; + + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + + for (sl = sa->spacedata.first; sl; sl = sl->next) { + ARegion *ar; + ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase; + + for (ar = lb->first; ar; ar = ar->next) { + wmWidgetMap *wmap; + for (wmap = ar->widgetmaps.first; wmap; wmap = wmap->next) { + wmWidgetGroup *wgroup, *wgroup_tmp; + for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup_tmp) { + wgroup_tmp = wgroup->next; + if (wgroup->type == wgrouptype) { + wm_widgetgroup_free(C, wmap, wgroup); + ED_region_tag_redraw(ar); + } + } + } + } + } + } + } + + wmaptype = WM_widgetmaptype_find(wgrouptype->mapidname, wgrouptype->spaceid, wgrouptype->regionid, wgrouptype->is_3d, false); + BLI_remlink(&wmaptype->widgetgrouptypes, wgrouptype); + wgrouptype->prev = wgrouptype->next = NULL; + MEM_freeN(wgrouptype); +} + |