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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/windowmanager/intern')
-rw-r--r--source/blender/windowmanager/intern/wm.c2
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c106
-rw-r--r--source/blender/windowmanager/intern/wm_generic_widgets.c1109
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c5
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c39
-rw-r--r--source/blender/windowmanager/intern/wm_widgets.c909
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);
+}
+