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
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/intern/image.c12
-rw-r--r--source/blender/blenlib/BLI_math_vector.h1
-rw-r--r--source/blender/blenlib/intern/math_vector.c13
-rw-r--r--source/blender/blenlib/intern/scanfill.c13
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_dissolve.c168
-rw-r--r--source/blender/editors/animation/drivers.c3
-rw-r--r--source/blender/editors/curve/editcurve_paint.c30
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c4
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/interface/interface_handlers.c4
-rw-r--r--source/blender/editors/interface/interface_regions.c22
-rw-r--r--source/blender/editors/interface/interface_widgets.c9
-rw-r--r--source/blender/editors/screen/area.c10
-rw-r--r--source/blender/editors/screen/screen_edit.c14
-rw-r--r--source/blender/editors/space_action/action_edit.c24
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c57
-rw-r--r--source/blender/editors/space_graph/graph_edit.c23
-rw-r--r--source/blender/editors/space_image/image_ops.c2
-rw-r--r--source/blender/editors/space_nla/nla_edit.c24
-rw-r--r--source/blender/editors/space_nla/nla_intern.h1
-rw-r--r--source/blender/editors/space_nla/nla_ops.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c24
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_ops.c2
-rw-r--r--source/blender/editors/space_text/space_text.c10
-rw-r--r--source/blender/editors/space_time/time_ops.c28
-rw-r--r--source/blender/editors/space_view3d/drawobject.c250
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c10
-rw-r--r--source/blender/python/intern/bpy_rna_anim.c15
-rw-r--r--source/blender/windowmanager/CMakeLists.txt1
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c2
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c1
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c18
-rw-r--r--source/blender/windowmanager/intern/wm_files.c833
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c497
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c1027
-rw-r--r--source/blender/windowmanager/wm_cursors.h2
-rw-r--r--source/blender/windowmanager/wm_files.h28
38 files changed, 1824 insertions, 1362 deletions
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 63cc57d0a21..cdb3d1afc29 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -4494,13 +4494,17 @@ void BKE_image_get_size(Image *image, ImageUser *iuser, int *width, int *height)
ImBuf *ibuf = NULL;
void *lock;
- ibuf = BKE_image_acquire_ibuf(image, iuser, &lock);
+ if (image != NULL) {
+ ibuf = BKE_image_acquire_ibuf(image, iuser, &lock);
+ }
if (ibuf && ibuf->x > 0 && ibuf->y > 0) {
*width = ibuf->x;
*height = ibuf->y;
}
- else if (image->type == IMA_TYPE_R_RESULT && iuser != NULL && iuser->scene != NULL) {
+ else if (image != NULL && image->type == IMA_TYPE_R_RESULT &&
+ iuser != NULL && iuser->scene != NULL)
+ {
Scene *scene = iuser->scene;
*width = (scene->r.xsch * scene->r.size) / 100;
*height = (scene->r.ysch * scene->r.size) / 100;
@@ -4514,7 +4518,9 @@ void BKE_image_get_size(Image *image, ImageUser *iuser, int *width, int *height)
*height = IMG_SIZE_FALLBACK;
}
- BKE_image_release_ibuf(image, ibuf, lock);
+ if (image != NULL) {
+ BKE_image_release_ibuf(image, ibuf, lock);
+ }
}
void BKE_image_get_size_fl(Image *image, ImageUser *iuser, float size[2])
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index c51446d6cc8..c44fcf47fdb 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -272,6 +272,7 @@ float angle_normalized_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED
float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT;
float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) ATTR_WARN_UNUSED_RESULT;
float cos_v3v3v3(const float p1[3], const float p2[3], const float p3[3]) ATTR_WARN_UNUSED_RESULT;
+float cos_v2v2v2(const float p1[2], const float p2[2], const float p3[2]) ATTR_WARN_UNUSED_RESULT;
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT;
float angle_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT;
float angle_signed_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT;
diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c
index 8d33e04241a..72a3da265c7 100644
--- a/source/blender/blenlib/intern/math_vector.c
+++ b/source/blender/blenlib/intern/math_vector.c
@@ -397,6 +397,19 @@ float angle_v2v2v2(const float v1[2], const float v2[2], const float v3[2])
return angle_normalized_v2v2(vec1, vec2);
}
+/* Quicker than full angle computation */
+float cos_v2v2v2(const float p1[2], const float p2[2], const float p3[2])
+{
+ float vec1[2], vec2[2];
+
+ sub_v2_v2v2(vec1, p2, p1);
+ sub_v2_v2v2(vec2, p2, p3);
+ normalize_v2(vec1);
+ normalize_v2(vec2);
+
+ return dot_v2v2(vec1, vec2);
+}
+
/* Return the shortest angle in radians between the 2 vectors */
float angle_v2v2(const float v1[2], const float v2[2])
{
diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c
index 8a96daeeb91..e913499ba2b 100644
--- a/source/blender/blenlib/intern/scanfill.c
+++ b/source/blender/blenlib/intern/scanfill.c
@@ -602,7 +602,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl
else {
/* test rest of vertices */
ScanFillVertLink *best_sc = NULL;
- float best_angle = 3.14f;
+ float angle_best_cos = -1.0f;
float miny;
bool firsttime = false;
@@ -633,21 +633,18 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl
best_sc = sc1;
}
else {
- float angle;
-
/* prevent angle calc for the simple cases only 1 vertex is found */
if (firsttime == false) {
- best_angle = angle_v2v2v2(v2->xy, v1->xy, best_sc->vert->xy);
+ angle_best_cos = cos_v2v2v2(v2->xy, v1->xy, best_sc->vert->xy);
firsttime = true;
}
- angle = angle_v2v2v2(v2->xy, v1->xy, sc1->vert->xy);
- if (angle < best_angle) {
+ const float angle_test_cos = cos_v2v2v2(v2->xy, v1->xy, sc1->vert->xy);
+ if (angle_test_cos > angle_best_cos) {
best_sc = sc1;
- best_angle = angle;
+ angle_best_cos = angle_test_cos;
}
}
-
}
}
}
diff --git a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
index a1460cec7d1..5a7a2f3ee29 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
@@ -37,6 +37,9 @@
#include "bmesh.h"
#include "bmesh_decimate.h" /* own include */
+/* check that collapsing a vertex between 2 edges doesn't cause a degenerate face. */
+#define USE_DEGENERATE_CHECK
+
#define COST_INVALID FLT_MAX
@@ -88,9 +91,6 @@ static float bm_edge_calc_dissolve_error(
const BMEdge *e, const BMO_Delimit delimit,
const struct DelimitData *delimit_data)
{
- const bool is_contig = BM_edge_is_contiguous(e);
- float angle;
-
if (!BM_edge_is_manifold(e)) {
goto fail;
}
@@ -113,6 +113,8 @@ static float bm_edge_calc_dissolve_error(
goto fail;
}
+ const bool is_contig = BM_edge_is_contiguous(e);
+
if ((delimit & BMO_DELIM_NORMAL) &&
(is_contig == false))
{
@@ -125,18 +127,130 @@ static float bm_edge_calc_dissolve_error(
goto fail;
}
- angle = BM_edge_calc_face_angle(e);
- if (is_contig == false) {
- angle = (float)M_PI - angle;
+ float angle_cos_neg = dot_v3v3(e->l->f->no, e->l->radial_next->f->no);
+ if (is_contig) {
+ angle_cos_neg *= -1;
}
- return angle;
+ return angle_cos_neg;
fail:
return COST_INVALID;
}
+#ifdef USE_DEGENERATE_CHECK
+
+static void mul_v2_m3v3_center(float r[2], float m[3][3], const float a[3], const float center[3])
+{
+ BLI_assert(r != a);
+ BLI_assert(r != center);
+
+ float co[3];
+ sub_v3_v3v3(co, a, center);
+
+ r[0] = m[0][0] * co[0] + m[1][0] * co[1] + m[2][0] * co[2];
+ r[1] = m[0][1] * co[0] + m[1][1] * co[1] + m[2][1] * co[2];
+}
+
+static bool bm_loop_collapse_is_degenerate(BMLoop *l_ear)
+{
+ /* calculate relative to the centeral vertex for higher precision */
+ const float *center = l_ear->v->co;
+
+ float tri_2d[3][2];
+ float axis_mat[3][3];
+
+ axis_dominant_v3_to_m3(axis_mat, l_ear->f->no);
+
+ {
+ mul_v2_m3v3_center(tri_2d[0], axis_mat, l_ear->prev->v->co, center);
+#if 0
+ mul_v2_m3v3_center(tri_2d[1], axis_mat, l_ear->v->co, center);
+#else
+ zero_v2(tri_2d[1]);
+#endif
+ mul_v2_m3v3_center(tri_2d[2], axis_mat, l_ear->next->v->co, center);
+ }
+
+ /* check we're not flipping face corners before or after the ear */
+ {
+ float adjacent_2d[2];
+
+ if (!BM_vert_is_edge_pair(l_ear->prev->v)) {
+ mul_v2_m3v3_center(adjacent_2d, axis_mat, l_ear->prev->prev->v->co, center);
+ if (signum_i(cross_tri_v2(adjacent_2d, tri_2d[0], tri_2d[1])) !=
+ signum_i(cross_tri_v2(adjacent_2d, tri_2d[0], tri_2d[2])))
+ {
+ return true;
+ }
+ }
+
+ if (!BM_vert_is_edge_pair(l_ear->next->v)) {
+ mul_v2_m3v3_center(adjacent_2d, axis_mat, l_ear->next->next->v->co, center);
+ if (signum_i(cross_tri_v2(adjacent_2d, tri_2d[2], tri_2d[1])) !=
+ signum_i(cross_tri_v2(adjacent_2d, tri_2d[2], tri_2d[0])))
+ {
+ return true;
+ }
+ }
+ }
+
+ /* check no existing verts are inside the triangle */
+ {
+ /* triangle may be concave, if so - flip so we can use clockwise check */
+ if (cross_tri_v2(UNPACK3(tri_2d)) < 0.0f) {
+ swap_v2_v2(tri_2d[1], tri_2d[2]);
+ }
+
+ /* skip l_ear and adjacent verts */
+ BMLoop *l_iter, *l_first;
+
+ l_iter = l_ear->next->next;
+ l_first = l_ear->prev;
+ do {
+ float co_2d[2];
+ mul_v2_m3v3_center(co_2d, axis_mat, l_iter->v->co, center);
+ if (isect_point_tri_v2_cw(co_2d, tri_2d[0], tri_2d[1], tri_2d[2])) {
+ return true;
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+
+ return false;
+}
+
+static bool bm_vert_collapse_is_degenerate(BMVert *v)
+{
+ BMEdge *e_pair[2];
+ BMVert *v_pair[2];
+
+ if (BM_vert_edge_pair(v, &e_pair[0], &e_pair[1])) {
+ v_pair[0] = BM_edge_other_vert(e_pair[0], v);
+ v_pair[1] = BM_edge_other_vert(e_pair[1], v);
+
+ if (fabsf(cos_v3v3v3(v_pair[0]->co, v->co, v_pair[1]->co)) < (1.0f - FLT_EPSILON)) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = e_pair[1]->l;
+ do {
+ if (l_iter->f->len > 3) {
+ BMLoop *l_pivot = (l_iter->v == v ? l_iter : l_iter->next);
+ BLI_assert(v == l_pivot->v);
+ if (bm_loop_collapse_is_degenerate(l_pivot)) {
+ return true;
+ }
+ }
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+#endif /* USE_DEGENERATE_CHECK */
+
+
void BM_mesh_decimate_dissolve_ex(
BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries,
BMO_Delimit delimit,
@@ -144,6 +258,7 @@ void BM_mesh_decimate_dissolve_ex(
BMEdge **einput_arr, const int einput_len,
const short oflag_out)
{
+ const float angle_limit_cos_neg = -cosf(angle_limit);
struct DelimitData delimit_data = {0};
const int eheap_table_len = do_dissolve_boundaries ? einput_len : max_ii(einput_len, vinput_len);
void *_heap_table = MEM_mallocN(sizeof(HeapNode *) * eheap_table_len, __func__);
@@ -193,7 +308,7 @@ void BM_mesh_decimate_dissolve_ex(
}
while ((BLI_heap_is_empty(eheap) == false) &&
- (BLI_heap_node_value((enode_top = BLI_heap_top(eheap))) < angle_limit))
+ (BLI_heap_node_value((enode_top = BLI_heap_top(eheap))) < angle_limit_cos_neg))
{
BMFace *f_new = NULL;
BMEdge *e;
@@ -328,7 +443,14 @@ void BM_mesh_decimate_dissolve_ex(
v = BLI_heap_node_ptr(vnode_top);
i = BM_elem_index_get(v);
- if (BM_vert_is_edge_pair(v)) {
+ if (
+#ifdef USE_DEGENERATE_CHECK
+ !bm_vert_collapse_is_degenerate(v)
+#else
+ BM_vert_is_edge_pair(v)
+#endif
+ )
+ {
e_new = BM_vert_collapse_edge(bm, v->e, v, true, true); /* join edges */
if (e_new) {
@@ -343,7 +465,6 @@ void BM_mesh_decimate_dissolve_ex(
do {
BM_face_normal_update(l_iter->f);
} while ((l_iter = l_iter->radial_next) != l_first);
-
}
/* re-calculate costs */
@@ -355,6 +476,33 @@ void BM_mesh_decimate_dissolve_ex(
vheap_table[j] = BLI_heap_insert(vheap, cost, v_iter);
}
}
+
+#ifdef USE_DEGENERATE_CHECK
+ /* dissolving a vertex may mean vertices we previously weren't able to dissolve
+ * can now be re-evaluated. */
+ if (e_new->l) {
+ BMLoop *l_first, *l_iter;
+ l_iter = l_first = e_new->l;
+ do {
+ /* skip vertices part of this edge, evaluated above */
+ BMLoop *l_cycle_first, *l_cycle_iter;
+ l_cycle_iter = l_iter->next->next;
+ l_cycle_first = l_iter->prev;
+ do {
+ const int j = BM_elem_index_get(l_cycle_iter->v);
+ if (j != -1 && vheap_table[j] &&
+ (BLI_heap_node_value(vheap_table[j]) == COST_INVALID))
+ {
+ const float cost = bm_vert_edge_face_angle(l_cycle_iter->v);
+ BLI_heap_remove(vheap, vheap_table[j]);
+ vheap_table[j] = BLI_heap_insert(vheap, cost, l_cycle_iter->v);
+ }
+ } while ((l_cycle_iter = l_cycle_iter->next) != l_cycle_first);
+
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+#endif /* USE_DEGENERATE_CHECK */
+
}
}
diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c
index c7c945b492b..f92967ef5ff 100644
--- a/source/blender/editors/animation/drivers.c
+++ b/source/blender/editors/animation/drivers.c
@@ -748,7 +748,8 @@ EnumPropertyItem prop_driver_create_mapping_types[] = {
"Drive all components of this property using the target picked"},
{CREATEDRIVER_MAPPING_1_1, "DIRECT", 0, "Single from Target",
"Drive this component of this property using the target picked"},
- {CREATEDRIVER_MAPPING_N_N, "MATCH", 0, "Match Indices",
+
+ {CREATEDRIVER_MAPPING_N_N, "MATCH", ICON_COLOR, "Match Indices",
"Create drivers for each pair of corresponding elements"},
{CREATEDRIVER_MAPPING_NONE_ALL, "NONE_ALL", ICON_HAND, "Manually Create Later",
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 4af123c0f51..5c74a3e43c2 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -185,6 +185,10 @@ struct CurveDrawData {
/* use 'rv3d->depths', note that this will become 'damaged' while drawing, but thats OK. */
bool use_depth;
+
+ /* offset projection by this value */
+ bool use_offset;
+ float offset[3]; /* worldspace */
} project;
/* cursor sampling */
@@ -287,6 +291,12 @@ static bool stroke_elem_project(
}
}
+ if (is_location_world_set) {
+ if (cdd->project.use_offset) {
+ add_v3_v3(r_location_world, cdd->project.offset);
+ }
+ }
+
return is_location_world_set;
}
@@ -582,6 +592,26 @@ static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
normalize_v3_v3(cdd->project.plane, normal);
cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, cdd->prev.location_world_valid);
+
+ /* Special case for when we only have offset applied on the first-hit,
+ * the remaining stroke must be offset too. */
+ if (cdd->radius.offset != 0.0f) {
+ const float mval_fl[2] = {UNPACK2(event->mval)};
+
+ float location_no_offset[3];
+
+ if (stroke_elem_project(
+ cdd, event->mval, mval_fl, 0.0f, 0.0f,
+ location_no_offset))
+ {
+ sub_v3_v3v3(cdd->project.offset, cdd->prev.location_world_valid, location_no_offset);
+ if (!is_zero_v3(cdd->project.offset)) {
+ cdd->project.use_offset = true;
+ }
+ }
+ }
+ /* end special case */
+
}
cdd->init_event_type = event->type;
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 64cb0b5bb6a..f54da91af71 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -569,6 +569,10 @@ bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure)
/* add the point itself */
madd_v3_v3fl(sco, &pt->x, average_fac);
+ if (affect_pressure) {
+ pressure += pt->pressure * average_fac;
+ }
+
/* n-steps before/after current point */
// XXX: review how the endpoints are treated by this algorithm
// XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 85370de0013..6a558d1c185 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -67,6 +67,7 @@ void ED_region_panels(
const bool vertical);
void ED_region_header_init(struct ARegion *ar);
void ED_region_header(const struct bContext *C, struct ARegion *ar);
+void ED_region_cursor_set(struct wmWindow *win, struct ScrArea *sa, struct ARegion *ar);
void ED_region_toggle_hidden(struct bContext *C, struct ARegion *ar);
void ED_region_info_draw(struct ARegion *ar, const char *text, float fill_color[4], const bool full_redraw);
void ED_region_image_metadata_draw(int x, int y, struct ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy);
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index d59d29363e6..6c961179c6f 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -6537,7 +6537,6 @@ static void menu_add_shortcut_cancel(struct bContext *C, void *arg1)
static void popup_change_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
{
uiBut *but = (uiBut *)arg1;
- UI_but_tooltip_timer_remove(C, but);
UI_popup_block_invoke(C, menu_change_shortcut, but);
}
@@ -6559,7 +6558,6 @@ static void remove_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
{
uiBut *but = (uiBut *)arg1;
- UI_but_tooltip_timer_remove(C, but);
UI_popup_block_ex(C, menu_add_shortcut, NULL, menu_add_shortcut_cancel, but);
}
@@ -6608,8 +6606,6 @@ static bool ui_but_menu(bContext *C, uiBut *but)
return false;
}
- UI_but_tooltip_timer_remove(C, but);
-
/* highly unlikely getting the label ever fails */
UI_but_string_info_get(C, but, &label, NULL);
diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c
index abd395afbe0..b3972bebd96 100644
--- a/source/blender/editors/interface/interface_regions.c
+++ b/source/blender/editors/interface/interface_regions.c
@@ -1788,10 +1788,20 @@ void ui_popup_block_scrolltest(uiBlock *block)
static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle)
{
- ui_region_temp_remove(C, CTX_wm_screen(C), handle->region);
+ wmWindow *win = CTX_wm_window(C);
+ bScreen *sc = CTX_wm_screen(C);
+
+ ui_region_temp_remove(C, sc, handle->region);
+
+ /* reset to region cursor (only if there's not another menu open) */
+ if (BLI_listbase_is_empty(&sc->regionbase)) {
+ ED_region_cursor_set(win, CTX_wm_area(C), CTX_wm_region(C));
+ /* in case cursor needs to be changed again */
+ WM_event_add_mousemove(C);
+ }
if (handle->scrolltimer)
- WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer);
+ WM_event_remove_timer(CTX_wm_manager(C), win, handle->scrolltimer);
}
/**
@@ -1965,11 +1975,19 @@ uiPopupBlockHandle *ui_popup_block_create(
void *arg)
{
wmWindow *window = CTX_wm_window(C);
+ uiBut *activebut = UI_context_active_but_get(C);
static ARegionType type;
ARegion *ar;
uiBlock *block;
uiPopupBlockHandle *handle;
+ /* disable tooltips from buttons below */
+ if (activebut) {
+ UI_but_tooltip_timer_remove(C, activebut);
+ }
+ /* standard cursor by default */
+ WM_cursor_set(window, CURSOR_STD);
+
/* create handle */
handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 841b76c676b..d1461f1acec 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -3042,6 +3042,15 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat
wcol->shaded = 0;
wcol->alpha_check = (wcol->inner[3] < 255);
+
+ if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
+ /* Now we reduce alpha of the inner color (i.e. the color shown)
+ * so that this setting can look greyed out, while retaining
+ * the checkboard (for transparent values). This is needed
+ * here as the effects of ui_widget_color_disabled() are overwritten.
+ */
+ wcol->inner[3] /= 2;
+ }
widgetbase_draw(&wtb, wcol);
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 4ede89e1620..3511480821a 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1507,6 +1507,16 @@ void ED_region_init(bContext *C, ARegion *ar)
region_update_rect(ar);
}
+void ED_region_cursor_set(wmWindow *win, ScrArea *sa, ARegion *ar)
+{
+ if (ar && sa && ar->type && ar->type->cursor) {
+ ar->type->cursor(win, sa, ar);
+ }
+ else {
+ WM_cursor_set(win, CURSOR_STD);
+ }
+}
+
/* for quick toggle, can skip fades */
void region_toggle_hidden(bContext *C, ARegion *ar, const bool do_fade)
{
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index cb07ce756a6..23c6aa37a83 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -1064,17 +1064,11 @@ bScreen *ED_screen_duplicate(wmWindow *win, bScreen *sc)
/* screen sets cursor based on swinid */
static void region_cursor_set(wmWindow *win, int swinid, int swin_changed)
{
- ScrArea *sa = win->screen->areabase.first;
-
- for (; sa; sa = sa->next) {
- ARegion *ar = sa->regionbase.first;
- for (; ar; ar = ar->next) {
+ for (ScrArea *sa = win->screen->areabase.first; sa; sa = sa->next) {
+ for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->swinid == swinid) {
if (swin_changed || (ar->type && ar->type->event_cursor)) {
- if (ar->type && ar->type->cursor)
- ar->type->cursor(win, sa, ar);
- else
- WM_cursor_set(win, CURSOR_STD);
+ ED_region_cursor_set(win, sa, ar);
}
return;
}
@@ -1146,7 +1140,7 @@ void ED_screen_draw(wmWindow *win)
/* blended join arrow */
if (sa1 && sa2) {
int dir = area_getorientation(sa1, sa2);
- int dira;
+ int dira = -1;
if (dir != -1) {
switch (dir) {
case 0: /* W */
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index 9cc138b2cc4..b69547b0506 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -431,13 +431,7 @@ static int actkeys_viewsel_exec(bContext *C, wmOperator *UNUSED(op))
return actkeys_viewall(C, true);
}
-static int actkeys_view_frame_exec(bContext *C, wmOperator *op)
-{
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
- ANIM_center_frame(C, smooth_viewtx);
-
- return OPERATOR_FINISHED;
-}
+/* ......... */
void ACTION_OT_view_all(wmOperatorType *ot)
{
@@ -469,17 +463,27 @@ void ACTION_OT_view_selected(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/* ****************** View-All Operator ****************** */
+
+static int actkeys_view_frame_exec(bContext *C, wmOperator *op)
+{
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+ ANIM_center_frame(C, smooth_viewtx);
+
+ return OPERATOR_FINISHED;
+}
+
void ACTION_OT_view_frame(wmOperatorType *ot)
{
/* identifiers */
ot->name = "View Frame";
ot->idname = "ACTION_OT_view_frame";
ot->description = "Reset viewable area to show range around current frame";
-
+
/* api callbacks */
ot->exec = actkeys_view_frame_exec;
- ot->poll = ED_operator_action_active; /* XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier... */
-
+ ot->poll = ED_operator_action_active;
+
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index 5846a439b3c..a9ab1502e16 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -607,6 +607,7 @@ static void graph_panel_driverVar__singleProp(uiLayout *layout, ID *id, DriverVa
}
/* settings for 'rotation difference' driver variable type */
+/* FIXME: 1) Must be same armature for both dtars, 2) Alignment issues... */
static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar *dvar)
{
DriverTarget *dtar = &dvar->targets[0];
@@ -623,7 +624,7 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar *
/* Bone 1 */
col = uiLayoutColumn(layout, true);
uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
- uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Bone 1:"));
+ uiItemR(col, &dtar_ptr, "id", 0, IFACE_("Bone 1"), ICON_NONE);
if (dtar->id && GS(dtar->id->name) == ID_OB && ob1->pose) {
PointerRNA tar_ptr;
@@ -634,7 +635,7 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar *
col = uiLayoutColumn(layout, true);
uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
- uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Bone 2:"));
+ uiItemR(col, &dtar2_ptr, "id", 0, IFACE_("Bone 2"), ICON_NONE);
if (dtar2->id && GS(dtar2->id->name) == ID_OB && ob2->pose) {
PointerRNA tar_ptr;
@@ -661,13 +662,13 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar *
/* Bone 1 */
col = uiLayoutColumn(layout, true);
uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
- uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone 1:"));
-
+ uiItemR(col, &dtar_ptr, "id", 0, IFACE_("Object 1"), ICON_NONE);
+
if (dtar->id && GS(dtar->id->name) == ID_OB && ob1->pose) {
PointerRNA tar_ptr;
RNA_pointer_create(dtar->id, &RNA_Pose, ob1->pose, &tar_ptr);
- uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
+ uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA);
}
uiLayoutSetRedAlert(col, false); /* we can clear it again now - it's only needed when creating the ID/Bone fields */
@@ -675,13 +676,13 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar *
col = uiLayoutColumn(layout, true);
uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
- uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Ob/Bone 2:"));
-
+ uiItemR(col, &dtar2_ptr, "id", 0, IFACE_("Object 2"), ICON_NONE);
+
if (dtar2->id && GS(dtar2->id->name) == ID_OB && ob2->pose) {
PointerRNA tar_ptr;
RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr);
- uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
+ uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA);
}
uiLayoutSetRedAlert(col, false); /* we can clear it again now - it's only needed when creating the ID/Bone fields */
@@ -702,13 +703,13 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar
/* properties */
col = uiLayoutColumn(layout, true);
uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */
- uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone:"));
-
+ uiItemR(col, &dtar_ptr, "id", 0, IFACE_("Object"), ICON_NONE);
+
if (dtar->id && GS(dtar->id->name) == ID_OB && ob->pose) {
PointerRNA tar_ptr;
RNA_pointer_create(dtar->id, &RNA_Pose, ob->pose, &tar_ptr);
- uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
+ uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA);
}
sub = uiLayoutColumn(layout, true);
@@ -850,21 +851,34 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
PointerRNA dvar_ptr;
uiLayout *box, *row;
+ uiLayout *subrow, *sub;
/* sub-layout column for this variable's settings */
col = uiLayoutColumn(pa->layout, true);
- /* header panel */
+ /* 1) header panel */
box = uiLayoutBox(col);
- /* first row context info for driver */
RNA_pointer_create(ale->id, &RNA_DriverVariable, dvar, &dvar_ptr);
row = uiLayoutRow(box, false);
block = uiLayoutGetBlock(row);
- /* variable name */
- uiItemR(row, &dvar_ptr, "name", 0, "", ICON_NONE);
- /* invalid name? */
+ /* 1.1) variable type and name */
+ subrow = uiLayoutRow(row, true);
+
+ /* 1.1.1) variable type */
+ sub = uiLayoutRow(subrow, true); /* HACK: special group just for the enum, otherwise we */
+ uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT); /* we get ugly layout with text included too... */
+
+ uiItemR(sub, &dvar_ptr, "type", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
+
+ /* 1.1.2) variable name */
+ sub = uiLayoutRow(subrow, true); /* HACK: special group to counteract the effects of the previous */
+ uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_EXPAND); /* enum, which now pushes everything too far right */
+
+ uiItemR(sub, &dvar_ptr, "name", 0, "", ICON_NONE);
+
+ /* 1.2) invalid name? */
UI_block_emboss_set(block, UI_EMBOSS_NONE);
if (dvar->flag & DVAR_FLAG_INVALID_NAME) {
@@ -873,17 +887,14 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
UI_but_func_set(but, driver_dvar_invalid_name_query_cb, dvar, NULL); // XXX: reports?
}
- /* remove button */
+ /* 1.3) remove button */
but = uiDefIconBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_X, 290, 0, UI_UNIT_X, UI_UNIT_Y,
NULL, 0.0, 0.0, 0.0, 0.0, IFACE_("Delete target variable"));
UI_but_func_set(but, driver_delete_var_cb, driver, dvar);
UI_block_emboss_set(block, UI_EMBOSS);
- /* variable type */
- row = uiLayoutRow(box, false);
- uiItemR(row, &dvar_ptr, "type", 0, "", ICON_NONE);
-
- /* variable type settings */
+
+ /* 2) variable type settings */
box = uiLayoutBox(col);
/* controls to draw depends on the type of variable */
switch (dvar->type) {
@@ -901,7 +912,7 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
break;
}
- /* value of variable */
+ /* 3) value of variable */
if (driver->flag & DRIVER_FLAG_SHOWDEBUG) {
char valBuf[32];
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index 19325962adc..e1cd1da3a25 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -265,13 +265,7 @@ static int graphkeys_view_selected_exec(bContext *C, wmOperator *op)
return graphkeys_viewall(C, true, include_handles, smooth_viewtx);
}
-static int graphkeys_view_frame_exec(bContext *C, wmOperator *op)
-{
- const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
- ANIM_center_frame(C, smooth_viewtx);
- return OPERATOR_FINISHED;
-}
-
+/* ......... */
void GRAPH_OT_view_all(wmOperatorType *ot)
{
@@ -311,17 +305,26 @@ void GRAPH_OT_view_selected(wmOperatorType *ot)
"Include handles of keyframes when calculating extents");
}
+/* ********************** View Frame Operator ****************************** */
+
+static int graphkeys_view_frame_exec(bContext *C, wmOperator *op)
+{
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+ ANIM_center_frame(C, smooth_viewtx);
+ return OPERATOR_FINISHED;
+}
+
void GRAPH_OT_view_frame(wmOperatorType *ot)
{
/* identifiers */
ot->name = "View Frame";
ot->idname = "GRAPH_OT_view_frame";
ot->description = "Reset viewable area to show range around current frame";
-
+
/* api callbacks */
ot->exec = graphkeys_view_frame_exec;
- ot->poll = ED_operator_graphedit_active; /* XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier... */
-
+ ot->poll = ED_operator_graphedit_active;
+
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index eb74922a256..80dbfa140f6 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -3554,7 +3554,7 @@ static int clear_render_border_exec(bContext *C, wmOperator *UNUSED(op))
void IMAGE_OT_clear_render_border(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Render Border";
+ ot->name = "Clear Render Border";
ot->description = "Clear the boundaries of the border render and disable border render";
ot->idname = "IMAGE_OT_clear_render_border";
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index 02814e385c0..baf87f3fee5 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -542,6 +542,30 @@ void NLA_OT_view_selected(wmOperatorType *ot)
}
/* *********************************************** */
+
+static int nlaedit_viewframe_exec(bContext *C, wmOperator *op)
+{
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+ ANIM_center_frame(C, smooth_viewtx);
+ return OPERATOR_FINISHED;
+}
+
+void NLA_OT_view_frame(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "View Frame";
+ ot->idname = "NLA_OT_view_frame";
+ ot->description = "Reset viewable area to show range around current frame";
+
+ /* api callbacks */
+ ot->exec = nlaedit_viewframe_exec;
+ ot->poll = ED_operator_nla_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* *********************************************** */
/* NLA Editing Operations (Constructive/Destructive) */
/* ******************** Add Action-Clip Operator ***************************** */
diff --git a/source/blender/editors/space_nla/nla_intern.h b/source/blender/editors/space_nla/nla_intern.h
index 344580c0d15..806fbe90ff2 100644
--- a/source/blender/editors/space_nla/nla_intern.h
+++ b/source/blender/editors/space_nla/nla_intern.h
@@ -94,6 +94,7 @@ void NLA_OT_previewrange_set(wmOperatorType *ot);
void NLA_OT_view_all(wmOperatorType *ot);
void NLA_OT_view_selected(wmOperatorType *ot);
+void NLA_OT_view_frame(wmOperatorType *ot);
void NLA_OT_actionclip_add(wmOperatorType *ot);
void NLA_OT_transition_add(wmOperatorType *ot);
diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c
index 98da10470f8..386950ead3a 100644
--- a/source/blender/editors/space_nla/nla_ops.c
+++ b/source/blender/editors/space_nla/nla_ops.c
@@ -130,6 +130,7 @@ void nla_operatortypes(void)
/* view */
WM_operatortype_append(NLA_OT_view_all);
WM_operatortype_append(NLA_OT_view_selected);
+ WM_operatortype_append(NLA_OT_view_frame);
WM_operatortype_append(NLA_OT_previewrange_set);
@@ -243,6 +244,7 @@ static void nla_keymap_main(wmKeyConfig *keyconf, wmKeyMap *keymap)
WM_keymap_add_item(keymap, "NLA_OT_view_all", HOMEKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "NLA_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "NLA_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "NLA_OT_view_frame", PAD0, KM_PRESS, 0, 0);
/* editing ------------------------------------------------ */
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index df3e508ae0e..e3cdedf042b 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -61,6 +61,7 @@
/* for menu/popup icons etc etc*/
+#include "ED_anim_api.h"
#include "ED_numinput.h"
#include "ED_screen.h"
#include "ED_transform.h"
@@ -2697,6 +2698,29 @@ void SEQUENCER_OT_view_all(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER;
}
+static int sequencer_view_frame_exec(bContext *C, wmOperator *op)
+{
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+ ANIM_center_frame(C, smooth_viewtx);
+
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_view_frame(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "View Frame";
+ ot->idname = "SEQUENCER_OT_view_frame";
+ ot->description = "Reset viewable area to show range around current frame";
+
+ /* api callbacks */
+ ot->exec = sequencer_view_frame_exec;
+ ot->poll = ED_operator_sequencer_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
/* view_all operator */
static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op))
{
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 3e228fd0b31..730cc117287 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -121,6 +121,7 @@ void SEQUENCER_OT_rendersize(struct wmOperatorType *ot);
void SEQUENCER_OT_view_toggle(struct wmOperatorType *ot);
void SEQUENCER_OT_view_all(struct wmOperatorType *ot);
void SEQUENCER_OT_view_selected(struct wmOperatorType *ot);
+void SEQUENCER_OT_view_frame(struct wmOperatorType *ot);
void SEQUENCER_OT_view_zoom_ratio(struct wmOperatorType *ot);
void SEQUENCER_OT_view_ghost_border(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c
index 3d08e0c5ed8..655e029cfdd 100644
--- a/source/blender/editors/space_sequencer/sequencer_ops.c
+++ b/source/blender/editors/space_sequencer/sequencer_ops.c
@@ -84,6 +84,7 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_view_all);
WM_operatortype_append(SEQUENCER_OT_view_selected);
+ WM_operatortype_append(SEQUENCER_OT_view_frame);
WM_operatortype_append(SEQUENCER_OT_view_all_preview);
WM_operatortype_append(SEQUENCER_OT_view_toggle);
WM_operatortype_append(SEQUENCER_OT_view_zoom_ratio);
@@ -202,6 +203,7 @@ void sequencer_keymap(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", HOMEKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "SEQUENCER_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "SEQUENCER_OT_view_frame", PAD0, KM_PRESS, 0, 0);
kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_strip_jump", PAGEUPKEY, KM_PRESS, 0, 0);
RNA_boolean_set(kmi->ptr, "next", true);
diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c
index 87270bace9a..0a6a9a81e63 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -79,7 +79,15 @@ static SpaceLink *text_new(const bContext *UNUSED(C))
BLI_addtail(&stext->regionbase, ar);
ar->regiontype = RGN_TYPE_HEADER;
ar->alignment = RGN_ALIGN_BOTTOM;
-
+
+ /* properties region */
+ ar = MEM_callocN(sizeof(ARegion), "properties region for text");
+
+ BLI_addtail(&stext->regionbase, ar);
+ ar->regiontype = RGN_TYPE_UI;
+ ar->alignment = RGN_ALIGN_LEFT;
+ ar->flag = RGN_FLAG_HIDDEN;
+
/* main region */
ar = MEM_callocN(sizeof(ARegion), "main region for text");
diff --git a/source/blender/editors/space_time/time_ops.c b/source/blender/editors/space_time/time_ops.c
index e2e861fda38..a7f549b65ae 100644
--- a/source/blender/editors/space_time/time_ops.c
+++ b/source/blender/editors/space_time/time_ops.c
@@ -39,6 +39,7 @@
#include "BKE_context.h"
+#include "ED_anim_api.h"
#include "ED_screen.h"
#include "WM_api.h"
@@ -176,6 +177,31 @@ static void TIME_OT_view_all(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/* ************************ View Frame Operator *******************************/
+
+static int time_view_frame_exec(bContext *C, wmOperator *op)
+{
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+ ANIM_center_frame(C, smooth_viewtx);
+
+ return OPERATOR_FINISHED;
+}
+
+static void TIME_OT_view_frame(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "View Frame";
+ ot->idname = "TIME_OT_view_frame";
+ ot->description = "Reset viewable area to show range around current frame";
+
+ /* api callbacks */
+ ot->exec = time_view_frame_exec;
+ ot->poll = ED_operator_timeline_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
/* ************************** registration **********************************/
void time_operatortypes(void)
@@ -183,6 +209,7 @@ void time_operatortypes(void)
WM_operatortype_append(TIME_OT_start_frame_set);
WM_operatortype_append(TIME_OT_end_frame_set);
WM_operatortype_append(TIME_OT_view_all);
+ WM_operatortype_append(TIME_OT_view_frame);
}
void time_keymap(wmKeyConfig *keyconf)
@@ -193,5 +220,6 @@ void time_keymap(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "TIME_OT_end_frame_set", EKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "TIME_OT_view_all", HOMEKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "TIME_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "TIME_OT_view_frame", PAD0, KM_PRESS, 0, 0);
}
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index e4b67e29935..0aa62c4467e 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -3676,29 +3676,10 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d,
{
RegionView3D *rv3d = ar->regiondata;
Mesh *me = ob->data;
- BMFace *efa_act = BM_mesh_active_face_get(em->bm, false, true); /* annoying but active faces is stored differently */
- BMEdge *eed_act = NULL;
- BMVert *eve_act = NULL;
- bool use_occlude_wire = (v3d->flag2 & V3D_OCCLUDE_WIRE) && (dt > OB_WIRE);
+ const bool use_occlude_wire = (dt > OB_WIRE) && (v3d->flag2 & V3D_OCCLUDE_WIRE);
+ bool use_depth_offset = false;
glLineWidth(1);
-
- if (em->bm->selected.last) {
- BMEditSelection *ese = em->bm->selected.last;
- /* face is handled above */
-#if 0
- if (ese->type == BM_FACE) {
- efa_act = (BMFace *)ese->data;
- }
- else
-#endif
- if (ese->htype == BM_EDGE) {
- eed_act = (BMEdge *)ese->ele;
- }
- else if (ese->htype == BM_VERT) {
- eve_act = (BMVert *)ese->ele;
- }
- }
BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE | BM_FACE);
@@ -3708,6 +3689,7 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d,
ED_view3d_polygon_offset(rv3d, 1.0);
glDepthMask(0);
+ use_depth_offset = true;
}
else {
glEnable(GL_DEPTH_TEST);
@@ -3754,6 +3736,7 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d,
ED_view3d_polygon_offset(rv3d, 1.0);
glDepthMask(0);
+ use_depth_offset = true;
}
else {
if (cageDM != finalDM) {
@@ -3762,145 +3745,172 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d,
}
}
- if ((me->drawflag & ME_DRAWFACES) && (use_occlude_wire == false)) { /* transp faces */
- unsigned char col1[4], col2[4], col3[4];
+ if ((dt > OB_WIRE) && (v3d->flag2 & V3D_RENDER_SHADOW)) {
+ /* pass */
+ }
+ else {
+ /* annoying but active faces is stored differently */
+ BMFace *efa_act = BM_mesh_active_face_get(em->bm, false, true);
+ BMEdge *eed_act = NULL;
+ BMVert *eve_act = NULL;
+
+ if (em->bm->selected.last) {
+ BMEditSelection *ese = em->bm->selected.last;
+ /* face is handled above */
+#if 0
+ if (ese->type == BM_FACE) {
+ efa_act = (BMFace *)ese->data;
+ }
+ else
+#endif
+ if (ese->htype == BM_EDGE) {
+ eed_act = (BMEdge *)ese->ele;
+ }
+ else if (ese->htype == BM_VERT) {
+ eve_act = (BMVert *)ese->ele;
+ }
+ }
+
+ if ((me->drawflag & ME_DRAWFACES) && (use_occlude_wire == false)) { /* transp faces */
+ unsigned char col1[4], col2[4], col3[4];
#ifdef WITH_FREESTYLE
- unsigned char col4[4];
+ unsigned char col4[4];
#endif
- UI_GetThemeColor4ubv(TH_FACE, col1);
- UI_GetThemeColor4ubv(TH_FACE_SELECT, col2);
- UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3);
+ UI_GetThemeColor4ubv(TH_FACE, col1);
+ UI_GetThemeColor4ubv(TH_FACE_SELECT, col2);
+ UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3);
#ifdef WITH_FREESTYLE
- UI_GetThemeColor4ubv(TH_FREESTYLE_FACE_MARK, col4);
+ UI_GetThemeColor4ubv(TH_FREESTYLE_FACE_MARK, col4);
#endif
- glEnable(GL_BLEND);
- glDepthMask(0); /* disable write in zbuffer, needed for nice transp */
+ glEnable(GL_BLEND);
+ glDepthMask(0); /* disable write in zbuffer, needed for nice transp */
- /* don't draw unselected faces, only selected, this is MUCH nicer when texturing */
- if (check_object_draw_texture(scene, v3d, dt))
- col1[3] = 0;
+ /* don't draw unselected faces, only selected, this is MUCH nicer when texturing */
+ if (check_object_draw_texture(scene, v3d, dt))
+ col1[3] = 0;
#ifdef WITH_FREESTYLE
- if (!(me->drawflag & ME_DRAW_FREESTYLE_FACE) || !CustomData_has_layer(&em->bm->pdata, CD_FREESTYLE_FACE))
- col4[3] = 0;
+ if (!(me->drawflag & ME_DRAW_FREESTYLE_FACE) || !CustomData_has_layer(&em->bm->pdata, CD_FREESTYLE_FACE))
+ col4[3] = 0;
- draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act);
+ draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act);
#else
- draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act);
+ draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act);
#endif
- glDisable(GL_BLEND);
- glDepthMask(1); /* restore write in zbuffer */
- }
- else if (efa_act) {
- /* even if draw faces is off it would be nice to draw the stipple face
- * Make all other faces zero alpha except for the active */
- unsigned char col1[4], col2[4], col3[4];
+ glDisable(GL_BLEND);
+ glDepthMask(1); /* restore write in zbuffer */
+ }
+ else if (efa_act) {
+ /* even if draw faces is off it would be nice to draw the stipple face
+ * Make all other faces zero alpha except for the active */
+ unsigned char col1[4], col2[4], col3[4];
#ifdef WITH_FREESTYLE
- unsigned char col4[4];
- col4[3] = 0; /* don't draw */
+ unsigned char col4[4];
+ col4[3] = 0; /* don't draw */
#endif
- col1[3] = col2[3] = 0; /* don't draw */
+ col1[3] = col2[3] = 0; /* don't draw */
- UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3);
+ UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3);
- glEnable(GL_BLEND);
- glDepthMask(0); /* disable write in zbuffer, needed for nice transp */
+ glEnable(GL_BLEND);
+ glDepthMask(0); /* disable write in zbuffer, needed for nice transp */
#ifdef WITH_FREESTYLE
- draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act);
+ draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act);
#else
- draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act);
+ draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act);
#endif
- glDisable(GL_BLEND);
- glDepthMask(1); /* restore write in zbuffer */
- }
+ glDisable(GL_BLEND);
+ glDepthMask(1); /* restore write in zbuffer */
+ }
- /* here starts all fancy draw-extra over */
- if ((me->drawflag & ME_DRAWEDGES) == 0 && check_object_draw_texture(scene, v3d, dt)) {
- /* we are drawing textures and 'ME_DRAWEDGES' is disabled, don't draw any edges */
-
- /* only draw selected edges otherwise there is no way of telling if a face is selected */
- draw_em_fancy_edges(em, scene, v3d, me, cageDM, 1, eed_act);
-
- }
- else {
- if (me->drawflag & ME_DRAWSEAMS) {
- UI_ThemeColor(TH_EDGE_SEAM);
- glLineWidth(2);
+ /* here starts all fancy draw-extra over */
+ if ((me->drawflag & ME_DRAWEDGES) == 0 && check_object_draw_texture(scene, v3d, dt)) {
+ /* we are drawing textures and 'ME_DRAWEDGES' is disabled, don't draw any edges */
- draw_dm_edges_seams(em, cageDM);
+ /* only draw selected edges otherwise there is no way of telling if a face is selected */
+ draw_em_fancy_edges(em, scene, v3d, me, cageDM, 1, eed_act);
- glColor3ub(0, 0, 0);
}
-
- if (me->drawflag & ME_DRAWSHARP) {
- UI_ThemeColor(TH_EDGE_SHARP);
- glLineWidth(2);
+ else {
+ if (me->drawflag & ME_DRAWSEAMS) {
+ UI_ThemeColor(TH_EDGE_SEAM);
+ glLineWidth(2);
- draw_dm_edges_sharp(em, cageDM);
+ draw_dm_edges_seams(em, cageDM);
- glColor3ub(0, 0, 0);
- }
+ glColor3ub(0, 0, 0);
+ }
+
+ if (me->drawflag & ME_DRAWSHARP) {
+ UI_ThemeColor(TH_EDGE_SHARP);
+ glLineWidth(2);
+
+ draw_dm_edges_sharp(em, cageDM);
+
+ glColor3ub(0, 0, 0);
+ }
#ifdef WITH_FREESTYLE
- if (me->drawflag & ME_DRAW_FREESTYLE_EDGE && CustomData_has_layer(&em->bm->edata, CD_FREESTYLE_EDGE)) {
- UI_ThemeColor(TH_FREESTYLE_EDGE_MARK);
- glLineWidth(2);
-
- draw_dm_edges_freestyle(em, cageDM);
-
- glColor3ub(0, 0, 0);
- }
-#endif
-
- if (me->drawflag & ME_DRAWCREASES) {
- draw_dm_creases(em, cageDM);
- }
- if (me->drawflag & ME_DRAWBWEIGHTS) {
- draw_dm_bweights(em, scene, cageDM);
- }
+ if (me->drawflag & ME_DRAW_FREESTYLE_EDGE && CustomData_has_layer(&em->bm->edata, CD_FREESTYLE_EDGE)) {
+ UI_ThemeColor(TH_FREESTYLE_EDGE_MARK);
+ glLineWidth(2);
- glLineWidth(1);
- draw_em_fancy_edges(em, scene, v3d, me, cageDM, 0, eed_act);
- }
+ draw_dm_edges_freestyle(em, cageDM);
- {
- draw_em_fancy_verts(scene, v3d, ob, em, cageDM, eve_act, rv3d);
+ glColor3ub(0, 0, 0);
+ }
+#endif
- if (me->drawflag & ME_DRAWNORMALS) {
- UI_ThemeColor(TH_NORMAL);
- draw_dm_face_normals(em, scene, ob, cageDM);
- }
- if (me->drawflag & ME_DRAW_VNORMALS) {
- UI_ThemeColor(TH_VNORMAL);
- draw_dm_vert_normals(em, scene, ob, cageDM);
- }
- if (me->drawflag & ME_DRAW_LNORMALS) {
- UI_ThemeColor(TH_LNORMAL);
- draw_dm_loop_normals(em, scene, ob, cageDM);
- }
+ if (me->drawflag & ME_DRAWCREASES) {
+ draw_dm_creases(em, cageDM);
+ }
+ if (me->drawflag & ME_DRAWBWEIGHTS) {
+ draw_dm_bweights(em, scene, cageDM);
+ }
- if ((me->drawflag & (ME_DRAWEXTRA_EDGELEN |
- ME_DRAWEXTRA_FACEAREA |
- ME_DRAWEXTRA_FACEANG |
- ME_DRAWEXTRA_EDGEANG)) &&
- !(v3d->flag2 & V3D_RENDER_OVERRIDE))
- {
- draw_em_measure_stats(ar, v3d, ob, em, &scene->unit);
+ glLineWidth(1);
+ draw_em_fancy_edges(em, scene, v3d, me, cageDM, 0, eed_act);
}
- if ((G.debug & G_DEBUG) && (me->drawflag & ME_DRAWEXTRA_INDICES) &&
- !(v3d->flag2 & V3D_RENDER_OVERRIDE))
{
- draw_em_indices(em);
+ draw_em_fancy_verts(scene, v3d, ob, em, cageDM, eve_act, rv3d);
+
+ if (me->drawflag & ME_DRAWNORMALS) {
+ UI_ThemeColor(TH_NORMAL);
+ draw_dm_face_normals(em, scene, ob, cageDM);
+ }
+ if (me->drawflag & ME_DRAW_VNORMALS) {
+ UI_ThemeColor(TH_VNORMAL);
+ draw_dm_vert_normals(em, scene, ob, cageDM);
+ }
+ if (me->drawflag & ME_DRAW_LNORMALS) {
+ UI_ThemeColor(TH_LNORMAL);
+ draw_dm_loop_normals(em, scene, ob, cageDM);
+ }
+
+ if ((me->drawflag & (ME_DRAWEXTRA_EDGELEN |
+ ME_DRAWEXTRA_FACEAREA |
+ ME_DRAWEXTRA_FACEANG |
+ ME_DRAWEXTRA_EDGEANG)) &&
+ !(v3d->flag2 & V3D_RENDER_OVERRIDE))
+ {
+ draw_em_measure_stats(ar, v3d, ob, em, &scene->unit);
+ }
+
+ if ((G.debug & G_DEBUG) && (me->drawflag & ME_DRAWEXTRA_INDICES) &&
+ !(v3d->flag2 & V3D_RENDER_OVERRIDE))
+ {
+ draw_em_indices(em);
+ }
}
}
- if (dt > OB_WIRE) {
+ if (use_depth_offset) {
glDepthMask(1);
ED_view3d_polygon_offset(rv3d, 0.0);
GPU_object_material_unbind();
diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index 32008640930..b428ab30784 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -61,7 +61,7 @@ EnumPropertyItem rna_enum_fmodifier_type_items[] = {
{FMODIFIER_TYPE_NOISE, "NOISE", 0, "Noise",
"Add pseudo-random noise on top of F-Curves"},
/*{FMODIFIER_TYPE_FILTER, "FILTER", 0, "Filter", ""},*/ /* FIXME: not implemented yet! */
- {FMODIFIER_TYPE_PYTHON, "PYTHON", 0, "Python", ""},
+ /*{FMODIFIER_TYPE_PYTHON, "PYTHON", 0, "Python", ""},*/ /* FIXME: not implemented yet! */
{FMODIFIER_TYPE_LIMITS, "LIMITS", 0, "Limits",
"Restrict maximum and minimum values of F-Curve"},
{FMODIFIER_TYPE_STEPPED, "STEPPED", 0, "Stepped Interpolation",
@@ -1467,11 +1467,11 @@ static void rna_def_drivervar(BlenderRNA *brna)
PropertyRNA *prop;
static EnumPropertyItem prop_type_items[] = {
- {DVAR_TYPE_SINGLE_PROP, "SINGLE_PROP", 0, "Single Property", "Use the value from some RNA property (Default)"},
- {DVAR_TYPE_TRANSFORM_CHAN, "TRANSFORMS", 0, "Transform Channel",
+ {DVAR_TYPE_SINGLE_PROP, "SINGLE_PROP", ICON_RNA, "Single Property", "Use the value from some RNA property (Default)"},
+ {DVAR_TYPE_TRANSFORM_CHAN, "TRANSFORMS", ICON_MANIPUL, "Transform Channel",
"Final transformation value of object or bone"},
- {DVAR_TYPE_ROT_DIFF, "ROTATION_DIFF", 0, "Rotational Difference", "Use the angle between two bones"},
- {DVAR_TYPE_LOC_DIFF, "LOC_DIFF", 0, "Distance", "Distance between two bones or objects"},
+ {DVAR_TYPE_ROT_DIFF, "ROTATION_DIFF", ICON_PARTICLE_TIP, "Rotational Difference", "Use the angle between two bones"}, /* XXX: Icon... */
+ {DVAR_TYPE_LOC_DIFF, "LOC_DIFF", ICON_FULLSCREEN_ENTER, "Distance", "Distance between two bones or objects"}, /* XXX: Icon... */
{0, NULL, 0, NULL, NULL}
};
diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c
index e74a5185ab0..2ec73c3c9b7 100644
--- a/source/blender/python/intern/bpy_rna_anim.c
+++ b/source/blender/python/intern/bpy_rna_anim.c
@@ -188,18 +188,19 @@ char pyrna_struct_keyframe_insert_doc[] =
"\n"
" :arg data_path: path to the property to key, analogous to the fcurve's data path.\n"
" :type data_path: string\n"
-" :arg index: array index of the property to key. Defaults to -1 which will key all indices or a single channel "
- "if the property is not an array.\n"
+" :arg index: array index of the property to key.\n"
+" Defaults to -1 which will key all indices or a single channel if the property is not an array.\n"
" :type index: int\n"
" :arg frame: The frame on which the keyframe is inserted, defaulting to the current frame.\n"
" :type frame: float\n"
" :arg group: The name of the group the F-Curve should be added to if it doesn't exist yet.\n"
" :type group: str\n"
-" :arg options: Some optional flags:\n"
-" 'NEEDED': Only insert keyframes where they're needed in the relevant F-Curves.\n"
-" 'VISUAL': Insert keyframes based on 'visual transforms'.\n"
-" 'XYZ_TO_RGB': Color for newly added transformation F-Curves (Location, Rotation, Scale) "
- "and also Color is based on the transform axis.\n"
+" :arg options: Optional flags:\n"
+"\n"
+" - ``INSERTKEY_NEEDED`` Only insert keyframes where they're needed in the relevant F-Curves.\n"
+" - ``INSERTKEY_VISUAL`` Insert keyframes based on 'visual transforms'.\n"
+" - ``INSERTKEY_XYZ_TO_RGB`` Color for newly added transformation F-Curves (Location, Rotation, Scale)\n"
+" and also Color is based on the transform axis.\n"
" :type flag: set\n"
" :return: Success of keyframe insertion.\n"
" :rtype: boolean\n"
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index af8a7cca8dd..01188cb7f65 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -58,6 +58,7 @@ set(SRC
intern/wm_draw.c
intern/wm_event_system.c
intern/wm_files.c
+ intern/wm_files_link.c
intern/wm_gesture.c
intern/wm_init_exit.c
intern/wm_jobs.c
diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c
index d9466cbd035..d4c3928bd6c 100644
--- a/source/blender/windowmanager/intern/wm_cursors.c
+++ b/source/blender/windowmanager/intern/wm_cursors.c
@@ -253,7 +253,7 @@ static void wm_cursor_warp_relative(wmWindow *win, int x, int y)
}
/* give it a modal keymap one day? */
-int wm_cursor_arrow_move(wmWindow *win, wmEvent *event)
+bool wm_cursor_arrow_move(wmWindow *win, const wmEvent *event)
{
if (win && event->val == KM_PRESS) {
if (event->type == UPARROWKEY) {
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index f8a879d0485..8f15d94f538 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -43,7 +43,6 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
-#include "BLI_math_base.h"
#include "BIF_gl.h"
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 24d4144ec17..e5b98a1b735 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -410,15 +410,15 @@ void wm_event_do_notifiers(bContext *C)
CTX_wm_window_set(C, NULL);
}
-static int wm_event_always_pass(wmEvent *event)
+static int wm_event_always_pass(const wmEvent *event)
{
/* some events we always pass on, to ensure proper communication */
- return ISTIMER(event->type) || (event->type == WINDEACTIVATE) || (event->type == EVT_BUT_OPEN);
+ return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
}
/* ********************* ui handler ******************* */
-static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event, int always_pass)
+static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, const wmEvent *event, int always_pass)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
@@ -1526,7 +1526,7 @@ int WM_userdef_event_map(int kmitype)
}
-static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
+static int wm_eventmatch(const wmEvent *winevent, wmKeyMapItem *kmi)
{
int kmitype = WM_userdef_event_map(kmi->type);
@@ -1921,7 +1921,7 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand
return action;
}
-static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
+static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, const wmEvent *event)
{
int action = WM_HANDLER_CONTINUE;
@@ -1933,7 +1933,7 @@ static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHa
return wm_handler_fileselect_do(C, handlers, handler, event->val);
}
-static bool handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
+static bool handler_boundbox_test(wmEventHandler *handler, const wmEvent *event)
{
if (handler->bbwin) {
if (handler->bblocal) {
@@ -2245,7 +2245,7 @@ static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
/* called on mousemove, check updates for paintcursors */
/* context was set on active area and region */
-static void wm_paintcursor_test(bContext *C, wmEvent *event)
+static void wm_paintcursor_test(bContext *C, const wmEvent *event)
{
wmWindowManager *wm = CTX_wm_manager(C);
@@ -2312,7 +2312,7 @@ static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *even
}
/* filter out all events of the pie that spawned the last pie unless it's a release event */
-static bool wm_event_pie_filter(wmWindow *win, wmEvent *event)
+static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event)
{
if (win->lock_pie_event && win->lock_pie_event == event->type) {
if (event->val == KM_RELEASE) {
@@ -3124,7 +3124,7 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi
return NULL;
}
-static bool wm_event_is_double_click(wmEvent *event, wmEvent *event_state)
+static bool wm_event_is_double_click(wmEvent *event, const wmEvent *event_state)
{
if ((event->type == event_state->prevtype) &&
(event_state->prevval == KM_RELEASE) &&
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index ce722c732e6..498a3f5bdda 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -26,7 +26,7 @@
/** \file blender/windowmanager/intern/wm_files.c
* \ingroup wm
*
- * User level access for blend file read/write, file-history and userprefs.
+ * User level access for blend file read/write, file-history and userprefs (including relevant operators).
*/
@@ -62,6 +62,7 @@
#include "BLT_translation.h"
+#include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */
#include "DNA_object_types.h"
#include "DNA_space_types.h"
#include "DNA_userdef_types.h"
@@ -88,6 +89,7 @@
#include "BLO_writefile.h"
#include "RNA_access.h"
+#include "RNA_define.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -103,6 +105,7 @@
#include "GHOST_Path-api.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "UI_view2d.h"
#include "GPU_draw.h"
@@ -749,44 +752,6 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c
return true;
}
-int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
-{
- ED_file_read_bookmarks();
- wm_history_file_read();
- return OPERATOR_FINISHED;
-}
-
-int wm_homefile_read_exec(bContext *C, wmOperator *op)
-{
- const bool from_memory = (STREQ(op->type->idname, "WM_OT_read_factory_settings"));
- char filepath_buf[FILE_MAX];
- const char *filepath = NULL;
-
- if (!from_memory) {
- PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath");
-
- /* This can be used when loading of a start-up file should only change
- * the scene content but keep the blender UI as it is. */
- wm_open_init_load_ui(op, true);
- BKE_BIT_TEST_SET(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI);
-
- if (RNA_property_is_set(op->ptr, prop)) {
- RNA_property_string_get(op->ptr, prop, filepath_buf);
- filepath = filepath_buf;
- if (BLI_access(filepath, R_OK)) {
- BKE_reportf(op->reports, RPT_ERROR, "Can't read alternative start-up file: '%s'", filepath);
- return OPERATOR_CANCELLED;
- }
- }
- }
- else {
- /* always load UI for factory settings (prefs will re-init) */
- G.fileflags &= ~G_FILE_NO_UI;
- }
-
- return wm_homefile_read(C, op->reports, from_memory, filepath) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
-}
-
/** \name WM History File API
* \{ */
@@ -1005,7 +970,7 @@ bool write_crash_blend(void)
/**
* \see #wm_homefile_write_exec wraps #BLO_write_file in a similar way.
*/
-int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports)
+static int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports)
{
Library *li;
int len;
@@ -1117,69 +1082,6 @@ int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *
return ret;
}
-/**
- * \see #wm_file_write wraps #BLO_write_file in a similar way.
- */
-int wm_homefile_write_exec(bContext *C, wmOperator *op)
-{
- wmWindowManager *wm = CTX_wm_manager(C);
- wmWindow *win = CTX_wm_window(C);
- char filepath[FILE_MAX];
- int fileflags;
-
- BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE);
-
- /* check current window and close it if temp */
- if (win && win->screen->temp)
- wm_window_close(C, wm, win);
-
- /* update keymaps in user preferences */
- WM_keyconfig_update(wm);
-
- BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_STARTUP_FILE);
- printf("trying to save homefile at %s ", filepath);
-
- ED_editors_flush_edits(C, false);
-
- /* force save as regular blend file */
- fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY);
-
- if (BLO_write_file(CTX_data_main(C), filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) {
- printf("fail\n");
- return OPERATOR_CANCELLED;
- }
-
- printf("ok\n");
-
- G.save_over = 0;
-
- BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_POST);
-
- return OPERATOR_FINISHED;
-}
-
-/* Only save the prefs block. operator entry */
-int wm_userpref_write_exec(bContext *C, wmOperator *op)
-{
- wmWindowManager *wm = CTX_wm_manager(C);
- char filepath[FILE_MAX];
-
- /* update keymaps in user preferences */
- WM_keyconfig_update(wm);
-
- BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_USERPREF_FILE);
- printf("trying to save userpref at %s ", filepath);
-
- if (BKE_write_file_userdef(filepath, op->reports) == 0) {
- printf("fail\n");
- return OPERATOR_CANCELLED;
- }
-
- printf("ok\n");
-
- return OPERATOR_FINISHED;
-}
-
/************************ autosave ****************************/
void wm_autosave_location(char *filepath)
@@ -1341,3 +1243,728 @@ void WM_file_tag_modified(const bContext *C)
WM_event_add_notifier(C, NC_WM | ND_DATACHANGED, NULL);
}
}
+
+/** \name Preferences/startup save & load.
+ *
+ * \{ */
+
+/**
+ * \see #wm_file_write wraps #BLO_write_file in a similar way.
+ */
+static int wm_homefile_write_exec(bContext *C, wmOperator *op)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
+ char filepath[FILE_MAX];
+ int fileflags;
+
+ BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE);
+
+ /* check current window and close it if temp */
+ if (win && win->screen->temp)
+ wm_window_close(C, wm, win);
+
+ /* update keymaps in user preferences */
+ WM_keyconfig_update(wm);
+
+ BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_STARTUP_FILE);
+ printf("trying to save homefile at %s ", filepath);
+
+ ED_editors_flush_edits(C, false);
+
+ /* force save as regular blend file */
+ fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY);
+
+ if (BLO_write_file(CTX_data_main(C), filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) {
+ printf("fail\n");
+ return OPERATOR_CANCELLED;
+ }
+
+ printf("ok\n");
+
+ G.save_over = 0;
+
+ BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_POST);
+
+ return OPERATOR_FINISHED;
+}
+
+void WM_OT_save_homefile(wmOperatorType *ot)
+{
+ ot->name = "Save Startup File";
+ ot->idname = "WM_OT_save_homefile";
+ ot->description = "Make the current file the default .blend file, includes preferences";
+
+ ot->invoke = WM_operator_confirm;
+ ot->exec = wm_homefile_write_exec;
+}
+
+static int wm_userpref_autoexec_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ bPathCompare *path_cmp = MEM_callocN(sizeof(bPathCompare), "bPathCompare");
+ BLI_addtail(&U.autoexec_paths, path_cmp);
+ return OPERATOR_FINISHED;
+}
+
+void WM_OT_userpref_autoexec_path_add(wmOperatorType *ot)
+{
+ ot->name = "Add Autoexec Path";
+ ot->idname = "WM_OT_userpref_autoexec_path_add";
+ ot->description = "Add path to exclude from autoexecution";
+
+ ot->exec = wm_userpref_autoexec_add_exec;
+
+ ot->flag = OPTYPE_INTERNAL;
+}
+
+static int wm_userpref_autoexec_remove_exec(bContext *UNUSED(C), wmOperator *op)
+{
+ const int index = RNA_int_get(op->ptr, "index");
+ bPathCompare *path_cmp = BLI_findlink(&U.autoexec_paths, index);
+ if (path_cmp) {
+ BLI_freelinkN(&U.autoexec_paths, path_cmp);
+ }
+ return OPERATOR_FINISHED;
+}
+
+void WM_OT_userpref_autoexec_path_remove(wmOperatorType *ot)
+{
+ ot->name = "Remove Autoexec Path";
+ ot->idname = "WM_OT_userpref_autoexec_path_remove";
+ ot->description = "Remove path to exclude from autoexecution";
+
+ ot->exec = wm_userpref_autoexec_remove_exec;
+
+ ot->flag = OPTYPE_INTERNAL;
+
+ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
+}
+
+/* Only save the prefs block. operator entry */
+static int wm_userpref_write_exec(bContext *C, wmOperator *op)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ char filepath[FILE_MAX];
+
+ /* update keymaps in user preferences */
+ WM_keyconfig_update(wm);
+
+ BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_USERPREF_FILE);
+ printf("trying to save userpref at %s ", filepath);
+
+ if (BKE_write_file_userdef(filepath, op->reports) == 0) {
+ printf("fail\n");
+ return OPERATOR_CANCELLED;
+ }
+
+ printf("ok\n");
+
+ return OPERATOR_FINISHED;
+}
+
+void WM_OT_save_userpref(wmOperatorType *ot)
+{
+ ot->name = "Save User Settings";
+ ot->idname = "WM_OT_save_userpref";
+ ot->description = "Save user preferences separately, overrides startup file preferences";
+
+ ot->invoke = WM_operator_confirm;
+ ot->exec = wm_userpref_write_exec;
+}
+
+static int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ ED_file_read_bookmarks();
+ wm_history_file_read();
+ return OPERATOR_FINISHED;
+}
+
+void WM_OT_read_history(wmOperatorType *ot)
+{
+ ot->name = "Reload History File";
+ ot->idname = "WM_OT_read_history";
+ ot->description = "Reloads history and bookmarks";
+
+ ot->invoke = WM_operator_confirm;
+ ot->exec = wm_history_file_read_exec;
+
+ /* this operator is only used for loading settings from a previous blender install */
+ ot->flag = OPTYPE_INTERNAL;
+}
+
+static int wm_homefile_read_exec(bContext *C, wmOperator *op)
+{
+ const bool from_memory = (STREQ(op->type->idname, "WM_OT_read_factory_settings"));
+ char filepath_buf[FILE_MAX];
+ const char *filepath = NULL;
+
+ if (!from_memory) {
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath");
+
+ /* This can be used when loading of a start-up file should only change
+ * the scene content but keep the blender UI as it is. */
+ wm_open_init_load_ui(op, true);
+ BKE_BIT_TEST_SET(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI);
+
+ if (RNA_property_is_set(op->ptr, prop)) {
+ RNA_property_string_get(op->ptr, prop, filepath_buf);
+ filepath = filepath_buf;
+ if (BLI_access(filepath, R_OK)) {
+ BKE_reportf(op->reports, RPT_ERROR, "Can't read alternative start-up file: '%s'", filepath);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ }
+ else {
+ /* always load UI for factory settings (prefs will re-init) */
+ G.fileflags &= ~G_FILE_NO_UI;
+ }
+
+ return wm_homefile_read(C, op->reports, from_memory, filepath) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+}
+
+void WM_OT_read_homefile(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+ ot->name = "Reload Start-Up File";
+ ot->idname = "WM_OT_read_homefile";
+ ot->description = "Open the default file (doesn't save the current file)";
+
+ ot->invoke = WM_operator_confirm;
+ ot->exec = wm_homefile_read_exec;
+
+ prop = RNA_def_string_file_path(ot->srna, "filepath", NULL,
+ FILE_MAX, "File Path",
+ "Path to an alternative start-up file");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ /* So scripts can use an alternative start-up file without the UI */
+ prop = RNA_def_boolean(ot->srna, "load_ui", true, "Load UI",
+ "Load user interface setup from the .blend file");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
+ /* omit poll to run in background mode */
+}
+
+void WM_OT_read_factory_settings(wmOperatorType *ot)
+{
+ ot->name = "Load Factory Settings";
+ ot->idname = "WM_OT_read_factory_settings";
+ ot->description = "Load default file and user preferences";
+
+ ot->invoke = WM_operator_confirm;
+ ot->exec = wm_homefile_read_exec;
+ /* omit poll to run in background mode */
+}
+
+/** \} */
+
+/** \name Open main .blend file.
+ *
+ * \{ */
+
+/**
+ * Wrap #WM_file_read, shared by file reading operators.
+ */
+static bool wm_file_read_opwrap(bContext *C, const char *filepath, ReportList *reports,
+ const bool autoexec_init)
+{
+ bool success;
+
+ /* XXX wm in context is not set correctly after WM_file_read -> crash */
+ /* do it before for now, but is this correct with multiple windows? */
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+
+ if (autoexec_init) {
+ WM_file_autoexec_init(filepath);
+ }
+
+ success = WM_file_read(C, filepath, reports);
+
+ return success;
+}
+
+/* currently fits in a pointer */
+struct FileRuntime {
+ bool is_untrusted;
+};
+
+static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ const char *openname = G.main->name;
+
+ if (CTX_wm_window(C) == NULL) {
+ /* in rare cases this could happen, when trying to invoke in background
+ * mode on load for example. Don't use poll for this because exec()
+ * can still run without a window */
+ BKE_report(op->reports, RPT_ERROR, "Context window not set");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* if possible, get the name of the most recently used .blend file */
+ if (G.recent_files.first) {
+ struct RecentFile *recent = G.recent_files.first;
+ openname = recent->filepath;
+ }
+
+ RNA_string_set(op->ptr, "filepath", openname);
+ wm_open_init_load_ui(op, true);
+ wm_open_init_use_scripts(op, true);
+ op->customdata = NULL;
+
+ WM_event_add_fileselect(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int wm_open_mainfile_exec(bContext *C, wmOperator *op)
+{
+ char filepath[FILE_MAX];
+ bool success;
+
+ RNA_string_get(op->ptr, "filepath", filepath);
+
+ /* re-use last loaded setting so we can reload a file without changing */
+ wm_open_init_load_ui(op, false);
+ wm_open_init_use_scripts(op, false);
+
+ if (RNA_boolean_get(op->ptr, "load_ui"))
+ G.fileflags &= ~G_FILE_NO_UI;
+ else
+ G.fileflags |= G_FILE_NO_UI;
+
+ if (RNA_boolean_get(op->ptr, "use_scripts"))
+ G.f |= G_SCRIPT_AUTOEXEC;
+ else
+ G.f &= ~G_SCRIPT_AUTOEXEC;
+
+ success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_SCRIPT_AUTOEXEC));
+
+ /* for file open also popup for warnings, not only errors */
+ BKE_report_print_level_set(op->reports, RPT_WARNING);
+
+ if (success) {
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op)
+{
+ struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata;
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
+ bool is_untrusted = false;
+ char path[FILE_MAX];
+ char *lslash;
+
+ RNA_string_get(op->ptr, "filepath", path);
+
+ /* get the dir */
+ lslash = (char *)BLI_last_slash(path);
+ if (lslash) *(lslash + 1) = '\0';
+
+ if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
+ if (BKE_autoexec_match(path) == true) {
+ RNA_property_boolean_set(op->ptr, prop, false);
+ is_untrusted = true;
+ }
+ }
+
+ if (file_info) {
+ file_info->is_untrusted = is_untrusted;
+ }
+
+ return is_untrusted;
+}
+
+static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata;
+ uiLayout *layout = op->layout;
+ uiLayout *col = op->layout;
+ const char *autoexec_text;
+
+ uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE);
+
+ col = uiLayoutColumn(layout, false);
+ if (file_info->is_untrusted) {
+ autoexec_text = IFACE_("Trusted Source [Untrusted Path]");
+ uiLayoutSetActive(col, false);
+ uiLayoutSetEnabled(col, false);
+ }
+ else {
+ autoexec_text = IFACE_("Trusted Source");
+ }
+
+ uiItemR(col, op->ptr, "use_scripts", 0, autoexec_text, ICON_NONE);
+}
+
+void WM_OT_open_mainfile(wmOperatorType *ot)
+{
+ ot->name = "Open Blender File";
+ ot->idname = "WM_OT_open_mainfile";
+ ot->description = "Open a Blender file";
+
+ ot->invoke = wm_open_mainfile_invoke;
+ ot->exec = wm_open_mainfile_exec;
+ ot->check = wm_open_mainfile_check;
+ ot->ui = wm_open_mainfile_ui;
+ /* omit window poll so this can work in background mode */
+
+ WM_operator_properties_filesel(
+ ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE,
+ WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
+
+ RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file");
+ RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source",
+ "Allow .blend file to execute scripts automatically, default available from system preferences");
+}
+
+/** \} */
+
+/** \name Reload (revert) main .blend file.
+ *
+ * \{ */
+
+static int wm_revert_mainfile_exec(bContext *C, wmOperator *op)
+{
+ bool success;
+
+ wm_open_init_use_scripts(op, false);
+
+ if (RNA_boolean_get(op->ptr, "use_scripts"))
+ G.f |= G_SCRIPT_AUTOEXEC;
+ else
+ G.f &= ~G_SCRIPT_AUTOEXEC;
+
+ success = wm_file_read_opwrap(C, G.main->name, op->reports, !(G.f & G_SCRIPT_AUTOEXEC));
+
+ if (success) {
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+static int wm_revert_mainfile_poll(bContext *UNUSED(C))
+{
+ return G.relbase_valid;
+}
+
+void WM_OT_revert_mainfile(wmOperatorType *ot)
+{
+ ot->name = "Revert";
+ ot->idname = "WM_OT_revert_mainfile";
+ ot->description = "Reload the saved file";
+ ot->invoke = WM_operator_confirm;
+
+ RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source",
+ "Allow .blend file to execute scripts automatically, default available from system preferences");
+
+ ot->exec = wm_revert_mainfile_exec;
+ ot->poll = wm_revert_mainfile_poll;
+}
+
+/** \} */
+
+/** \name Recover last session & auto-save.
+ *
+ * \{ */
+
+void WM_recover_last_session(bContext *C, ReportList *reports)
+{
+ char filepath[FILE_MAX];
+
+ BLI_make_file_string("/", filepath, BKE_tempdir_base(), BLENDER_QUIT_FILE);
+ /* if reports==NULL, it's called directly without operator, we add a quick check here */
+ if (reports || BLI_exists(filepath)) {
+ G.fileflags |= G_FILE_RECOVER;
+
+ wm_file_read_opwrap(C, filepath, reports, true);
+
+ G.fileflags &= ~G_FILE_RECOVER;
+
+ /* XXX bad global... fixme */
+ if (G.main->name[0])
+ G.file_loaded = 1; /* prevents splash to show */
+ else {
+ G.relbase_valid = 0;
+ G.save_over = 0; /* start with save preference untitled.blend */
+ }
+
+ }
+}
+
+static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
+{
+ WM_recover_last_session(C, op->reports);
+ return OPERATOR_FINISHED;
+}
+
+void WM_OT_recover_last_session(wmOperatorType *ot)
+{
+ ot->name = "Recover Last Session";
+ ot->idname = "WM_OT_recover_last_session";
+ ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")";
+ ot->invoke = WM_operator_confirm;
+
+ ot->exec = wm_recover_last_session_exec;
+}
+
+static int wm_recover_auto_save_exec(bContext *C, wmOperator *op)
+{
+ char filepath[FILE_MAX];
+ bool success;
+
+ RNA_string_get(op->ptr, "filepath", filepath);
+
+ G.fileflags |= G_FILE_RECOVER;
+
+ success = wm_file_read_opwrap(C, filepath, op->reports, true);
+
+ G.fileflags &= ~G_FILE_RECOVER;
+
+ if (success) {
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ char filename[FILE_MAX];
+
+ wm_autosave_location(filename);
+ RNA_string_set(op->ptr, "filepath", filename);
+ WM_event_add_fileselect(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void WM_OT_recover_auto_save(wmOperatorType *ot)
+{
+ ot->name = "Recover Auto Save";
+ ot->idname = "WM_OT_recover_auto_save";
+ ot->description = "Open an automatically saved file to recover it";
+
+ ot->exec = wm_recover_auto_save_exec;
+ ot->invoke = wm_recover_auto_save_invoke;
+
+ WM_operator_properties_filesel(
+ ot, FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE,
+ WM_FILESEL_FILEPATH, FILE_LONGDISPLAY, FILE_SORT_TIME);
+}
+
+/** \} */
+
+/** \name Save main .blend file.
+ *
+ * \{ */
+
+static void wm_filepath_default(char *filepath)
+{
+ if (G.save_over == false) {
+ BLI_ensure_filename(filepath, FILE_MAX, "untitled.blend");
+ }
+}
+
+static void save_set_compress(wmOperator *op)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_struct_find_property(op->ptr, "compress");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ if (G.save_over) { /* keep flag for existing file */
+ RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0);
+ }
+ else { /* use userdef for new file */
+ RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0);
+ }
+ }
+}
+
+static void save_set_filepath(wmOperator *op)
+{
+ PropertyRNA *prop;
+ char name[FILE_MAX];
+
+ prop = RNA_struct_find_property(op->ptr, "filepath");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ /* if not saved before, get the name of the most recently used .blend file */
+ if (G.main->name[0] == 0 && G.recent_files.first) {
+ struct RecentFile *recent = G.recent_files.first;
+ BLI_strncpy(name, recent->filepath, FILE_MAX);
+ }
+ else {
+ BLI_strncpy(name, G.main->name, FILE_MAX);
+ }
+
+ wm_filepath_default(name);
+ RNA_property_string_set(op->ptr, prop, name);
+ }
+}
+
+static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+
+ save_set_compress(op);
+ save_set_filepath(op);
+
+ WM_event_add_fileselect(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* function used for WM_OT_save_mainfile too */
+static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
+{
+ char path[FILE_MAX];
+ int fileflags;
+
+ save_set_compress(op);
+
+ if (RNA_struct_property_is_set(op->ptr, "filepath")) {
+ RNA_string_get(op->ptr, "filepath", path);
+ }
+ else {
+ BLI_strncpy(path, G.main->name, FILE_MAX);
+ wm_filepath_default(path);
+ }
+
+ fileflags = G.fileflags & ~G_FILE_USERPREFS;
+
+ /* set compression flag */
+ BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "compress"),
+ G_FILE_COMPRESS);
+ BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "relative_remap"),
+ G_FILE_RELATIVE_REMAP);
+ BKE_BIT_TEST_SET(fileflags,
+ (RNA_struct_property_is_set(op->ptr, "copy") &&
+ RNA_boolean_get(op->ptr, "copy")),
+ G_FILE_SAVE_COPY);
+
+#ifdef USE_BMESH_SAVE_AS_COMPAT
+ BKE_BIT_TEST_SET(fileflags,
+ (RNA_struct_find_property(op->ptr, "use_mesh_compat") &&
+ RNA_boolean_get(op->ptr, "use_mesh_compat")),
+ G_FILE_MESH_COMPAT);
+#else
+# error "don't remove by accident"
+#endif
+
+ if (wm_file_write(C, path, fileflags, op->reports) != 0)
+ return OPERATOR_CANCELLED;
+
+ WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+/* function used for WM_OT_save_mainfile too */
+static bool blend_save_check(bContext *UNUSED(C), wmOperator *op)
+{
+ char filepath[FILE_MAX];
+ RNA_string_get(op->ptr, "filepath", filepath);
+ if (!BLO_has_bfile_extension(filepath)) {
+ /* some users would prefer BLI_replace_extension(),
+ * we keep getting nitpicking bug reports about this - campbell */
+ BLI_ensure_extension(filepath, FILE_MAX, ".blend");
+ RNA_string_set(op->ptr, "filepath", filepath);
+ return true;
+ }
+ return false;
+}
+
+void WM_OT_save_as_mainfile(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ ot->name = "Save As Blender File";
+ ot->idname = "WM_OT_save_as_mainfile";
+ ot->description = "Save the current file in the desired location";
+
+ ot->invoke = wm_save_as_mainfile_invoke;
+ ot->exec = wm_save_as_mainfile_exec;
+ ot->check = blend_save_check;
+ /* omit window poll so this can work in background mode */
+
+ WM_operator_properties_filesel(
+ ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE,
+ WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
+ RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
+ RNA_def_boolean(ot->srna, "relative_remap", true, "Remap Relative",
+ "Remap relative paths when saving in a different directory");
+ prop = RNA_def_boolean(ot->srna, "copy", false, "Save Copy",
+ "Save a copy of the actual working state but does not make saved file active");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+#ifdef USE_BMESH_SAVE_AS_COMPAT
+ RNA_def_boolean(ot->srna, "use_mesh_compat", false, "Legacy Mesh Format",
+ "Save using legacy mesh format (no ngons) - WARNING: only saves tris and quads, other ngons will "
+ "be lost (no implicit triangulation)");
+#endif
+}
+
+static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ int ret;
+
+ /* cancel if no active window */
+ if (CTX_wm_window(C) == NULL)
+ return OPERATOR_CANCELLED;
+
+ save_set_compress(op);
+ save_set_filepath(op);
+
+ /* if we're saving for the first time and prefer relative paths - any existing paths will be absolute,
+ * enable the option to remap paths to avoid confusion [#37240] */
+ if ((G.relbase_valid == false) && (U.flag & USER_RELPATHS)) {
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ RNA_property_boolean_set(op->ptr, prop, true);
+ }
+ }
+
+ if (G.save_over) {
+ char path[FILE_MAX];
+
+ RNA_string_get(op->ptr, "filepath", path);
+ if (BLI_exists(path)) {
+ ret = WM_operator_confirm_message_ex(C, op, IFACE_("Save Over?"), ICON_QUESTION, path);
+ }
+ else {
+ ret = wm_save_as_mainfile_exec(C, op);
+ }
+ }
+ else {
+ WM_event_add_fileselect(C, op);
+ ret = OPERATOR_RUNNING_MODAL;
+ }
+
+ return ret;
+}
+
+void WM_OT_save_mainfile(wmOperatorType *ot)
+{
+ ot->name = "Save Blender File";
+ ot->idname = "WM_OT_save_mainfile";
+ ot->description = "Save the current Blender file";
+
+ ot->invoke = wm_save_mainfile_invoke;
+ ot->exec = wm_save_as_mainfile_exec;
+ ot->check = blend_save_check;
+ /* omit window poll so this can work in background mode */
+
+ WM_operator_properties_filesel(
+ ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE,
+ WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
+ RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
+ RNA_def_boolean(ot->srna, "relative_remap", false, "Remap Relative",
+ "Remap relative paths when saving in a different directory");
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
new file mode 100644
index 00000000000..5041eebd126
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -0,0 +1,497 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2007 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/intern/wm_files_link.c
+ * \ingroup wm
+ *
+ * Functions for dealing with append/link operators and helpers.
+ */
+
+
+#include <float.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+#include "DNA_screen_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_windowmanager_types.h"
+
+
+
+#include "BLI_blenlib.h"
+#include "BLI_bitmap.h"
+#include "BLI_linklist.h"
+#include "BLI_math.h"
+#include "BLI_memarena.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+
+#include "BLO_readfile.h"
+
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_library.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+
+#include "BKE_idcode.h"
+
+
+#include "IMB_colormanagement.h"
+
+#include "ED_screen.h"
+
+#include "GPU_material.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "wm_files.h"
+
+/* **************** link/append *************** */
+
+static int wm_link_append_poll(bContext *C)
+{
+ if (WM_operator_winactive(C)) {
+ /* linking changes active object which is pretty useful in general,
+ * but which totally confuses edit mode (i.e. it becoming not so obvious
+ * to leave from edit mode and invalid tools in toolbar might be displayed)
+ * so disable link/append when in edit mode (sergey) */
+ if (CTX_data_edit_object(C))
+ return 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ if (RNA_struct_property_is_set(op->ptr, "filepath")) {
+ return WM_operator_call_notest(C, op);
+ }
+ else {
+ /* XXX TODO solve where to get last linked library from */
+ if (G.lib[0] != '\0') {
+ RNA_string_set(op->ptr, "filepath", G.lib);
+ }
+ else if (G.relbase_valid) {
+ char path[FILE_MAX];
+ BLI_strncpy(path, G.main->name, sizeof(G.main->name));
+ BLI_parent_dir(path);
+ RNA_string_set(op->ptr, "filepath", path);
+ }
+ WM_event_add_fileselect(C, op);
+ return OPERATOR_RUNNING_MODAL;
+ }
+}
+
+static short wm_link_append_flag(wmOperator *op)
+{
+ PropertyRNA *prop;
+ short flag = 0;
+
+ if (RNA_boolean_get(op->ptr, "autoselect"))
+ flag |= FILE_AUTOSELECT;
+ if (RNA_boolean_get(op->ptr, "active_layer"))
+ flag |= FILE_ACTIVELAY;
+ if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop))
+ flag |= FILE_RELPATH;
+ if (RNA_boolean_get(op->ptr, "link"))
+ flag |= FILE_LINK;
+ if (RNA_boolean_get(op->ptr, "instance_groups"))
+ flag |= FILE_GROUP_INSTANCE;
+
+ return flag;
+}
+
+typedef struct WMLinkAppendDataItem {
+ char *name;
+ BLI_bitmap *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */
+ short idcode;
+
+ ID *new_id;
+ void *customdata;
+} WMLinkAppendDataItem;
+
+typedef struct WMLinkAppendData {
+ LinkNodePair libraries;
+ LinkNodePair items;
+ int num_libraries;
+ int num_items;
+ short flag;
+
+ /* Internal 'private' data */
+ MemArena *memarena;
+} WMLinkAppendData;
+
+static WMLinkAppendData *wm_link_append_data_new(const int flag)
+{
+ MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+ WMLinkAppendData *lapp_data = BLI_memarena_calloc(ma, sizeof(*lapp_data));
+
+ lapp_data->flag = flag;
+ lapp_data->memarena = ma;
+
+ return lapp_data;
+}
+
+static void wm_link_append_data_free(WMLinkAppendData *lapp_data)
+{
+ BLI_memarena_free(lapp_data->memarena);
+}
+
+/* WARNING! *Never* call wm_link_append_data_library_add() after having added some items! */
+
+static void wm_link_append_data_library_add(WMLinkAppendData *lapp_data, const char *libname)
+{
+ size_t len = strlen(libname) + 1;
+ char *libpath = BLI_memarena_alloc(lapp_data->memarena, len);
+
+ BLI_strncpy(libpath, libname, len);
+ BLI_linklist_append_arena(&lapp_data->libraries, libpath, lapp_data->memarena);
+ lapp_data->num_libraries++;
+}
+
+static WMLinkAppendDataItem *wm_link_append_data_item_add(
+ WMLinkAppendData *lapp_data, const char *idname, const short idcode, void *customdata)
+{
+ WMLinkAppendDataItem *item = BLI_memarena_alloc(lapp_data->memarena, sizeof(*item));
+ size_t len = strlen(idname) + 1;
+
+ item->name = BLI_memarena_alloc(lapp_data->memarena, len);
+ BLI_strncpy(item->name, idname, len);
+ item->idcode = idcode;
+ item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries);
+
+ item->new_id = NULL;
+ item->customdata = customdata;
+
+ BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena);
+ lapp_data->num_items++;
+
+ return item;
+}
+
+static void wm_link_do(
+ WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d)
+{
+ Main *mainl;
+ BlendHandle *bh;
+ Library *lib;
+
+ const int flag = lapp_data->flag;
+
+ LinkNode *liblink, *itemlink;
+ int lib_idx, item_idx;
+
+ BLI_assert(lapp_data->num_items && lapp_data->num_libraries);
+
+ for (lib_idx = 0, liblink = lapp_data->libraries.list; liblink; lib_idx++, liblink = liblink->next) {
+ char *libname = liblink->link;
+
+ bh = BLO_blendhandle_from_file(libname, reports);
+
+ if (bh == NULL) {
+ /* Unlikely since we just browsed it, but possible
+ * Error reports will have been made by BLO_blendhandle_from_file() */
+ continue;
+ }
+
+ /* here appending/linking starts */
+ mainl = BLO_library_link_begin(bmain, &bh, libname);
+ lib = mainl->curlib;
+ BLI_assert(lib);
+ UNUSED_VARS_NDEBUG(lib);
+
+ if (mainl->versionfile < 250) {
+ BKE_reportf(reports, RPT_WARNING,
+ "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will "
+ "be done! You may want to re-save your lib file with current Blender",
+ mainl->versionfile, mainl->subversionfile);
+ }
+
+ /* For each lib file, we try to link all items belonging to that lib,
+ * and tag those successful to not try to load them again with the other libs. */
+ for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+ ID *new_id;
+
+ if (!BLI_BITMAP_TEST(item->libraries, lib_idx)) {
+ continue;
+ }
+
+ new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d);
+ if (new_id) {
+ /* If the link is sucessful, clear item's libs 'todo' flags.
+ * This avoids trying to link same item with other libraries to come. */
+ BLI_BITMAP_SET_ALL(item->libraries, false, lapp_data->num_libraries);
+ item->new_id = new_id;
+ }
+ }
+
+ BLO_library_link_end(mainl, &bh, flag, scene, v3d);
+ BLO_blendhandle_close(bh);
+ }
+}
+
+static int wm_link_append_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ PropertyRNA *prop;
+ WMLinkAppendData *lapp_data;
+ char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX];
+ char *group, *name;
+ int totfiles = 0;
+ short flag;
+
+ RNA_string_get(op->ptr, "filename", relname);
+ RNA_string_get(op->ptr, "directory", root);
+
+ BLI_join_dirfile(path, sizeof(path), root, relname);
+
+ /* test if we have a valid data */
+ if (!BLO_library_path_explode(path, libname, &group, &name)) {
+ BKE_reportf(op->reports, RPT_ERROR, "'%s': not a library", path);
+ return OPERATOR_CANCELLED;
+ }
+ else if (!group) {
+ BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path);
+ return OPERATOR_CANCELLED;
+ }
+ else if (BLI_path_cmp(bmain->name, libname) == 0) {
+ BKE_reportf(op->reports, RPT_ERROR, "'%s': cannot use current file as library", path);
+ return OPERATOR_CANCELLED;
+ }
+
+ /* check if something is indicated for append/link */
+ prop = RNA_struct_find_property(op->ptr, "files");
+ if (prop) {
+ totfiles = RNA_property_collection_length(op->ptr, prop);
+ if (totfiles == 0) {
+ if (!name) {
+ BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ }
+ else if (!name) {
+ BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path);
+ return OPERATOR_CANCELLED;
+ }
+
+ flag = wm_link_append_flag(op);
+
+ /* sanity checks for flag */
+ if (scene && scene->id.lib) {
+ BKE_reportf(op->reports, RPT_WARNING,
+ "Scene '%s' is linked, instantiation of objects & groups is disabled", scene->id.name + 2);
+ flag &= ~FILE_GROUP_INSTANCE;
+ scene = NULL;
+ }
+
+ /* from here down, no error returns */
+
+ if (scene && RNA_boolean_get(op->ptr, "autoselect")) {
+ BKE_scene_base_deselect_all(scene);
+ }
+
+ /* tag everything, all untagged data can be made local
+ * its also generally useful to know what is new
+ *
+ * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */
+ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
+
+ /* We define our working data...
+ * Note that here, each item 'uses' one library, and only one. */
+ lapp_data = wm_link_append_data_new(flag);
+ if (totfiles != 0) {
+ GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__);
+ int lib_idx = 0;
+
+ RNA_BEGIN (op->ptr, itemptr, "files")
+ {
+ RNA_string_get(&itemptr, "name", relname);
+
+ BLI_join_dirfile(path, sizeof(path), root, relname);
+
+ if (BLO_library_path_explode(path, libname, &group, &name)) {
+ if (!group || !name) {
+ continue;
+ }
+
+ if (!BLI_ghash_haskey(libraries, libname)) {
+ BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx));
+ lib_idx++;
+ wm_link_append_data_library_add(lapp_data, libname);
+ }
+ }
+ }
+ RNA_END;
+
+ RNA_BEGIN (op->ptr, itemptr, "files")
+ {
+ RNA_string_get(&itemptr, "name", relname);
+
+ BLI_join_dirfile(path, sizeof(path), root, relname);
+
+ if (BLO_library_path_explode(path, libname, &group, &name)) {
+ WMLinkAppendDataItem *item;
+ if (!group || !name) {
+ printf("skipping %s\n", path);
+ continue;
+ }
+
+ lib_idx = GET_INT_FROM_POINTER(BLI_ghash_lookup(libraries, libname));
+
+ item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL);
+ BLI_BITMAP_ENABLE(item->libraries, lib_idx);
+ }
+ }
+ RNA_END;
+
+ BLI_ghash_free(libraries, MEM_freeN, NULL);
+ }
+ else {
+ WMLinkAppendDataItem *item;
+
+ wm_link_append_data_library_add(lapp_data, libname);
+ item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL);
+ BLI_BITMAP_ENABLE(item->libraries, 0);
+ }
+
+ /* XXX We'd need re-entrant locking on Main for this to work... */
+ /* BKE_main_lock(bmain); */
+
+ wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C));
+
+ /* BKE_main_unlock(bmain); */
+
+ wm_link_append_data_free(lapp_data);
+
+ /* mark all library linked objects to be updated */
+ BKE_main_lib_objects_recalc_all(bmain);
+ IMB_colormanagement_check_file_config(bmain);
+
+ /* append, rather than linking */
+ if ((flag & FILE_LINK) == 0) {
+ bool set_fake = RNA_boolean_get(op->ptr, "set_fake");
+ BKE_library_make_local(bmain, NULL, true, set_fake);
+ }
+
+ /* important we unset, otherwise these object wont
+ * link into other scenes from this blend file */
+ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
+
+ /* recreate dependency graph to include new objects */
+ DAG_scene_relations_rebuild(bmain, scene);
+
+ /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */
+ GPU_materials_free();
+
+ /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */
+ BLI_strncpy(G.lib, root, FILE_MAX);
+
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link)
+{
+ PropertyRNA *prop;
+
+ /* better not save _any_ settings for this operator */
+ /* properties */
+ prop = RNA_def_boolean(ot->srna, "link", is_link,
+ "Link", "Link the objects or datablocks rather than appending");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+ prop = RNA_def_boolean(ot->srna, "autoselect", true,
+ "Select", "Select new objects");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "active_layer", true,
+ "Active Layer", "Put new objects on the active layer");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "instance_groups", is_link,
+ "Instance Groups", "Create Dupli-Group instances for each group");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
+void WM_OT_link(wmOperatorType *ot)
+{
+ ot->name = "Link from Library";
+ ot->idname = "WM_OT_link";
+ ot->description = "Link from a Library .blend file";
+
+ ot->invoke = wm_link_append_invoke;
+ ot->exec = wm_link_append_exec;
+ ot->poll = wm_link_append_poll;
+
+ ot->flag |= OPTYPE_UNDO;
+
+ WM_operator_properties_filesel(
+ ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE,
+ WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES,
+ FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
+
+ wm_link_append_properties_common(ot, true);
+}
+
+void WM_OT_append(wmOperatorType *ot)
+{
+ ot->name = "Append from Library";
+ ot->idname = "WM_OT_append";
+ ot->description = "Append from a Library .blend file";
+
+ ot->invoke = wm_link_append_invoke;
+ ot->exec = wm_link_append_exec;
+ ot->poll = wm_link_append_poll;
+
+ ot->flag |= OPTYPE_UNDO;
+
+ WM_operator_properties_filesel(
+ ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE,
+ WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES,
+ FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
+
+ wm_link_append_properties_common(ot, false);
+ RNA_def_boolean(ot->srna, "set_fake", false, "Fake User", "Set Fake User for appended items (except Objects and Groups)");
+}
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index b5ad027148a..8a15237078a 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -52,26 +52,21 @@
#include "DNA_scene_types.h"
#include "DNA_userdef_types.h"
#include "DNA_windowmanager_types.h"
-#include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */
#include "BLT_translation.h"
#include "PIL_time.h"
#include "BLI_blenlib.h"
-#include "BLI_bitmap.h"
#include "BLI_dial.h"
#include "BLI_dynstr.h" /*for WM_operator_pystring */
-#include "BLI_linklist.h"
#include "BLI_math.h"
-#include "BLI_memarena.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
#include "BLO_readfile.h"
#include "BKE_appdir.h"
-#include "BKE_autoexec.h"
#include "BKE_blender.h"
#include "BKE_brush.h"
#include "BKE_context.h"
@@ -88,14 +83,12 @@
#include "BKE_scene.h"
#include "BKE_screen.h" /* BKE_ST_MAXNAME */
#include "BKE_unit.h"
-#include "BKE_utildefines.h"
#include "BKE_idcode.h"
#include "BIF_glutil.h" /* for paint cursor */
#include "BLF_api.h"
-#include "IMB_colormanagement.h"
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
@@ -105,7 +98,6 @@
#include "ED_view3d.h"
#include "GPU_basic_shader.h"
-#include "GPU_material.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -2073,1025 +2065,6 @@ static void WM_OT_window_duplicate(wmOperatorType *ot)
ot->poll = wm_operator_winactive_normal;
}
-static void WM_OT_save_homefile(wmOperatorType *ot)
-{
- ot->name = "Save Startup File";
- ot->idname = "WM_OT_save_homefile";
- ot->description = "Make the current file the default .blend file, includes preferences";
-
- ot->invoke = WM_operator_confirm;
- ot->exec = wm_homefile_write_exec;
-}
-
-static int wm_userpref_autoexec_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
-{
- bPathCompare *path_cmp = MEM_callocN(sizeof(bPathCompare), "bPathCompare");
- BLI_addtail(&U.autoexec_paths, path_cmp);
- return OPERATOR_FINISHED;
-}
-
-static void WM_OT_userpref_autoexec_path_add(wmOperatorType *ot)
-{
- ot->name = "Add Autoexec Path";
- ot->idname = "WM_OT_userpref_autoexec_path_add";
- ot->description = "Add path to exclude from autoexecution";
-
- ot->exec = wm_userpref_autoexec_add_exec;
-
- ot->flag = OPTYPE_INTERNAL;
-}
-
-static int wm_userpref_autoexec_remove_exec(bContext *UNUSED(C), wmOperator *op)
-{
- const int index = RNA_int_get(op->ptr, "index");
- bPathCompare *path_cmp = BLI_findlink(&U.autoexec_paths, index);
- if (path_cmp) {
- BLI_freelinkN(&U.autoexec_paths, path_cmp);
- }
- return OPERATOR_FINISHED;
-}
-
-static void WM_OT_userpref_autoexec_path_remove(wmOperatorType *ot)
-{
- ot->name = "Remove Autoexec Path";
- ot->idname = "WM_OT_userpref_autoexec_path_remove";
- ot->description = "Remove path to exclude from autoexecution";
-
- ot->exec = wm_userpref_autoexec_remove_exec;
-
- ot->flag = OPTYPE_INTERNAL;
-
- RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
-}
-
-static void WM_OT_save_userpref(wmOperatorType *ot)
-{
- ot->name = "Save User Settings";
- ot->idname = "WM_OT_save_userpref";
- ot->description = "Save user preferences separately, overrides startup file preferences";
-
- ot->invoke = WM_operator_confirm;
- ot->exec = wm_userpref_write_exec;
-}
-
-static void WM_OT_read_history(wmOperatorType *ot)
-{
- ot->name = "Reload History File";
- ot->idname = "WM_OT_read_history";
- ot->description = "Reloads history and bookmarks";
-
- ot->invoke = WM_operator_confirm;
- ot->exec = wm_history_file_read_exec;
-
- /* this operator is only used for loading settings from a previous blender install */
- ot->flag = OPTYPE_INTERNAL;
-}
-
-static void WM_OT_read_homefile(wmOperatorType *ot)
-{
- PropertyRNA *prop;
- ot->name = "Reload Start-Up File";
- ot->idname = "WM_OT_read_homefile";
- ot->description = "Open the default file (doesn't save the current file)";
-
- ot->invoke = WM_operator_confirm;
- ot->exec = wm_homefile_read_exec;
-
- prop = RNA_def_string_file_path(ot->srna, "filepath", NULL,
- FILE_MAX, "File Path",
- "Path to an alternative start-up file");
- RNA_def_property_flag(prop, PROP_HIDDEN);
-
- /* So scripts can use an alternative start-up file without the UI */
- prop = RNA_def_boolean(ot->srna, "load_ui", true, "Load UI",
- "Load user interface setup from the .blend file");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
-
- /* omit poll to run in background mode */
-}
-
-static void WM_OT_read_factory_settings(wmOperatorType *ot)
-{
- ot->name = "Load Factory Settings";
- ot->idname = "WM_OT_read_factory_settings";
- ot->description = "Load default file and user preferences";
-
- ot->invoke = WM_operator_confirm;
- ot->exec = wm_homefile_read_exec;
- /* omit poll to run in background mode */
-}
-
-/* *************** open file **************** */
-
-/**
- * Wrap #WM_file_read, shared by file reading operators.
- */
-static bool wm_file_read_opwrap(bContext *C, const char *filepath, ReportList *reports,
- const bool autoexec_init)
-{
- bool success;
-
- /* XXX wm in context is not set correctly after WM_file_read -> crash */
- /* do it before for now, but is this correct with multiple windows? */
- WM_event_add_notifier(C, NC_WINDOW, NULL);
-
- if (autoexec_init) {
- WM_file_autoexec_init(filepath);
- }
-
- success = WM_file_read(C, filepath, reports);
-
- return success;
-}
-
-/* currently fits in a pointer */
-struct FileRuntime {
- bool is_untrusted;
-};
-
-static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
-{
- const char *openname = G.main->name;
-
- if (CTX_wm_window(C) == NULL) {
- /* in rare cases this could happen, when trying to invoke in background
- * mode on load for example. Don't use poll for this because exec()
- * can still run without a window */
- BKE_report(op->reports, RPT_ERROR, "Context window not set");
- return OPERATOR_CANCELLED;
- }
-
- /* if possible, get the name of the most recently used .blend file */
- if (G.recent_files.first) {
- struct RecentFile *recent = G.recent_files.first;
- openname = recent->filepath;
- }
-
- RNA_string_set(op->ptr, "filepath", openname);
- wm_open_init_load_ui(op, true);
- wm_open_init_use_scripts(op, true);
- op->customdata = NULL;
-
- WM_event_add_fileselect(C, op);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-static int wm_open_mainfile_exec(bContext *C, wmOperator *op)
-{
- char filepath[FILE_MAX];
- bool success;
-
- RNA_string_get(op->ptr, "filepath", filepath);
-
- /* re-use last loaded setting so we can reload a file without changing */
- wm_open_init_load_ui(op, false);
- wm_open_init_use_scripts(op, false);
-
- if (RNA_boolean_get(op->ptr, "load_ui"))
- G.fileflags &= ~G_FILE_NO_UI;
- else
- G.fileflags |= G_FILE_NO_UI;
-
- if (RNA_boolean_get(op->ptr, "use_scripts"))
- G.f |= G_SCRIPT_AUTOEXEC;
- else
- G.f &= ~G_SCRIPT_AUTOEXEC;
-
- success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_SCRIPT_AUTOEXEC));
-
- /* for file open also popup for warnings, not only errors */
- BKE_report_print_level_set(op->reports, RPT_WARNING);
-
- if (success) {
- return OPERATOR_FINISHED;
- }
- else {
- return OPERATOR_CANCELLED;
- }
-}
-
-static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op)
-{
- struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata;
- PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
- bool is_untrusted = false;
- char path[FILE_MAX];
- char *lslash;
-
- RNA_string_get(op->ptr, "filepath", path);
-
- /* get the dir */
- lslash = (char *)BLI_last_slash(path);
- if (lslash) *(lslash + 1) = '\0';
-
- if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
- if (BKE_autoexec_match(path) == true) {
- RNA_property_boolean_set(op->ptr, prop, false);
- is_untrusted = true;
- }
- }
-
- if (file_info) {
- file_info->is_untrusted = is_untrusted;
- }
-
- return is_untrusted;
-}
-
-static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op)
-{
- struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata;
- uiLayout *layout = op->layout;
- uiLayout *col = op->layout;
- const char *autoexec_text;
-
- uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE);
-
- col = uiLayoutColumn(layout, false);
- if (file_info->is_untrusted) {
- autoexec_text = IFACE_("Trusted Source [Untrusted Path]");
- uiLayoutSetActive(col, false);
- uiLayoutSetEnabled(col, false);
- }
- else {
- autoexec_text = IFACE_("Trusted Source");
- }
-
- uiItemR(col, op->ptr, "use_scripts", 0, autoexec_text, ICON_NONE);
-}
-
-static void WM_OT_open_mainfile(wmOperatorType *ot)
-{
- ot->name = "Open Blender File";
- ot->idname = "WM_OT_open_mainfile";
- ot->description = "Open a Blender file";
-
- ot->invoke = wm_open_mainfile_invoke;
- ot->exec = wm_open_mainfile_exec;
- ot->check = wm_open_mainfile_check;
- ot->ui = wm_open_mainfile_ui;
- /* omit window poll so this can work in background mode */
-
- WM_operator_properties_filesel(
- ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE,
- WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
-
- RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file");
- RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source",
- "Allow .blend file to execute scripts automatically, default available from system preferences");
-}
-
-
-/* *************** revert file **************** */
-
-static int wm_revert_mainfile_exec(bContext *C, wmOperator *op)
-{
- bool success;
-
- wm_open_init_use_scripts(op, false);
-
- if (RNA_boolean_get(op->ptr, "use_scripts"))
- G.f |= G_SCRIPT_AUTOEXEC;
- else
- G.f &= ~G_SCRIPT_AUTOEXEC;
-
- success = wm_file_read_opwrap(C, G.main->name, op->reports, !(G.f & G_SCRIPT_AUTOEXEC));
-
- if (success) {
- return OPERATOR_FINISHED;
- }
- else {
- return OPERATOR_CANCELLED;
- }
-}
-
-static int wm_revert_mainfile_poll(bContext *UNUSED(C))
-{
- return G.relbase_valid;
-}
-
-static void WM_OT_revert_mainfile(wmOperatorType *ot)
-{
- ot->name = "Revert";
- ot->idname = "WM_OT_revert_mainfile";
- ot->description = "Reload the saved file";
- ot->invoke = WM_operator_confirm;
-
- RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source",
- "Allow .blend file to execute scripts automatically, default available from system preferences");
-
- ot->exec = wm_revert_mainfile_exec;
- ot->poll = wm_revert_mainfile_poll;
-}
-
-/* **************** link/append *************** */
-
-static int wm_link_append_poll(bContext *C)
-{
- if (WM_operator_winactive(C)) {
- /* linking changes active object which is pretty useful in general,
- * but which totally confuses edit mode (i.e. it becoming not so obvious
- * to leave from edit mode and invalid tools in toolbar might be displayed)
- * so disable link/append when in edit mode (sergey) */
- if (CTX_data_edit_object(C))
- return 0;
-
- return 1;
- }
-
- return 0;
-}
-
-static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
-{
- if (RNA_struct_property_is_set(op->ptr, "filepath")) {
- return WM_operator_call_notest(C, op);
- }
- else {
- /* XXX TODO solve where to get last linked library from */
- if (G.lib[0] != '\0') {
- RNA_string_set(op->ptr, "filepath", G.lib);
- }
- else if (G.relbase_valid) {
- char path[FILE_MAX];
- BLI_strncpy(path, G.main->name, sizeof(G.main->name));
- BLI_parent_dir(path);
- RNA_string_set(op->ptr, "filepath", path);
- }
- WM_event_add_fileselect(C, op);
- return OPERATOR_RUNNING_MODAL;
- }
-}
-
-static short wm_link_append_flag(wmOperator *op)
-{
- PropertyRNA *prop;
- short flag = 0;
-
- if (RNA_boolean_get(op->ptr, "autoselect"))
- flag |= FILE_AUTOSELECT;
- if (RNA_boolean_get(op->ptr, "active_layer"))
- flag |= FILE_ACTIVELAY;
- if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop))
- flag |= FILE_RELPATH;
- if (RNA_boolean_get(op->ptr, "link"))
- flag |= FILE_LINK;
- if (RNA_boolean_get(op->ptr, "instance_groups"))
- flag |= FILE_GROUP_INSTANCE;
-
- return flag;
-}
-
-typedef struct WMLinkAppendDataItem {
- char *name;
- BLI_bitmap *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */
- short idcode;
-
- ID *new_id;
- void *customdata;
-} WMLinkAppendDataItem;
-
-typedef struct WMLinkAppendData {
- LinkNodePair libraries;
- LinkNodePair items;
- int num_libraries;
- int num_items;
- short flag;
-
- /* Internal 'private' data */
- MemArena *memarena;
-} WMLinkAppendData;
-
-static WMLinkAppendData *wm_link_append_data_new(const int flag)
-{
- MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
- WMLinkAppendData *lapp_data = BLI_memarena_calloc(ma, sizeof(*lapp_data));
-
- lapp_data->flag = flag;
- lapp_data->memarena = ma;
-
- return lapp_data;
-}
-
-static void wm_link_append_data_free(WMLinkAppendData *lapp_data)
-{
- BLI_memarena_free(lapp_data->memarena);
-}
-
-/* WARNING! *Never* call wm_link_append_data_library_add() after having added some items! */
-
-static void wm_link_append_data_library_add(WMLinkAppendData *lapp_data, const char *libname)
-{
- size_t len = strlen(libname) + 1;
- char *libpath = BLI_memarena_alloc(lapp_data->memarena, len);
-
- BLI_strncpy(libpath, libname, len);
- BLI_linklist_append_arena(&lapp_data->libraries, libpath, lapp_data->memarena);
- lapp_data->num_libraries++;
-}
-
-static WMLinkAppendDataItem *wm_link_append_data_item_add(
- WMLinkAppendData *lapp_data, const char *idname, const short idcode, void *customdata)
-{
- WMLinkAppendDataItem *item = BLI_memarena_alloc(lapp_data->memarena, sizeof(*item));
- size_t len = strlen(idname) + 1;
-
- item->name = BLI_memarena_alloc(lapp_data->memarena, len);
- BLI_strncpy(item->name, idname, len);
- item->idcode = idcode;
- item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries);
-
- item->new_id = NULL;
- item->customdata = customdata;
-
- BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena);
- lapp_data->num_items++;
-
- return item;
-}
-
-static void wm_link_do(
- WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d)
-{
- Main *mainl;
- BlendHandle *bh;
- Library *lib;
-
- const int flag = lapp_data->flag;
-
- LinkNode *liblink, *itemlink;
- int lib_idx, item_idx;
-
- BLI_assert(lapp_data->num_items && lapp_data->num_libraries);
-
- for (lib_idx = 0, liblink = lapp_data->libraries.list; liblink; lib_idx++, liblink = liblink->next) {
- char *libname = liblink->link;
-
- bh = BLO_blendhandle_from_file(libname, reports);
-
- if (bh == NULL) {
- /* Unlikely since we just browsed it, but possible
- * Error reports will have been made by BLO_blendhandle_from_file() */
- continue;
- }
-
- /* here appending/linking starts */
- mainl = BLO_library_link_begin(bmain, &bh, libname);
- lib = mainl->curlib;
- BLI_assert(lib);
- UNUSED_VARS_NDEBUG(lib);
-
- if (mainl->versionfile < 250) {
- BKE_reportf(reports, RPT_WARNING,
- "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will "
- "be done! You may want to re-save your lib file with current Blender",
- mainl->versionfile, mainl->subversionfile);
- }
-
- /* For each lib file, we try to link all items belonging to that lib,
- * and tag those successful to not try to load them again with the other libs. */
- for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) {
- WMLinkAppendDataItem *item = itemlink->link;
- ID *new_id;
-
- if (!BLI_BITMAP_TEST(item->libraries, lib_idx)) {
- continue;
- }
-
- new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d);
- if (new_id) {
- /* If the link is sucessful, clear item's libs 'todo' flags.
- * This avoids trying to link same item with other libraries to come. */
- BLI_BITMAP_SET_ALL(item->libraries, false, lapp_data->num_libraries);
- item->new_id = new_id;
- }
- }
-
- BLO_library_link_end(mainl, &bh, flag, scene, v3d);
- BLO_blendhandle_close(bh);
- }
-}
-
-static int wm_link_append_exec(bContext *C, wmOperator *op)
-{
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- PropertyRNA *prop;
- WMLinkAppendData *lapp_data;
- char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX];
- char *group, *name;
- int totfiles = 0;
- short flag;
-
- RNA_string_get(op->ptr, "filename", relname);
- RNA_string_get(op->ptr, "directory", root);
-
- BLI_join_dirfile(path, sizeof(path), root, relname);
-
- /* test if we have a valid data */
- if (!BLO_library_path_explode(path, libname, &group, &name)) {
- BKE_reportf(op->reports, RPT_ERROR, "'%s': not a library", path);
- return OPERATOR_CANCELLED;
- }
- else if (!group) {
- BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path);
- return OPERATOR_CANCELLED;
- }
- else if (BLI_path_cmp(bmain->name, libname) == 0) {
- BKE_reportf(op->reports, RPT_ERROR, "'%s': cannot use current file as library", path);
- return OPERATOR_CANCELLED;
- }
-
- /* check if something is indicated for append/link */
- prop = RNA_struct_find_property(op->ptr, "files");
- if (prop) {
- totfiles = RNA_property_collection_length(op->ptr, prop);
- if (totfiles == 0) {
- if (!name) {
- BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path);
- return OPERATOR_CANCELLED;
- }
- }
- }
- else if (!name) {
- BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path);
- return OPERATOR_CANCELLED;
- }
-
- flag = wm_link_append_flag(op);
-
- /* sanity checks for flag */
- if (scene && scene->id.lib) {
- BKE_reportf(op->reports, RPT_WARNING,
- "Scene '%s' is linked, instantiation of objects & groups is disabled", scene->id.name + 2);
- flag &= ~FILE_GROUP_INSTANCE;
- scene = NULL;
- }
-
- /* from here down, no error returns */
-
- if (scene && RNA_boolean_get(op->ptr, "autoselect")) {
- BKE_scene_base_deselect_all(scene);
- }
-
- /* tag everything, all untagged data can be made local
- * its also generally useful to know what is new
- *
- * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */
- BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
-
- /* We define our working data...
- * Note that here, each item 'uses' one library, and only one. */
- lapp_data = wm_link_append_data_new(flag);
- if (totfiles != 0) {
- GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__);
- int lib_idx = 0;
-
- RNA_BEGIN (op->ptr, itemptr, "files")
- {
- RNA_string_get(&itemptr, "name", relname);
-
- BLI_join_dirfile(path, sizeof(path), root, relname);
-
- if (BLO_library_path_explode(path, libname, &group, &name)) {
- if (!group || !name) {
- continue;
- }
-
- if (!BLI_ghash_haskey(libraries, libname)) {
- BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx));
- lib_idx++;
- wm_link_append_data_library_add(lapp_data, libname);
- }
- }
- }
- RNA_END;
-
- RNA_BEGIN (op->ptr, itemptr, "files")
- {
- RNA_string_get(&itemptr, "name", relname);
-
- BLI_join_dirfile(path, sizeof(path), root, relname);
-
- if (BLO_library_path_explode(path, libname, &group, &name)) {
- WMLinkAppendDataItem *item;
- if (!group || !name) {
- printf("skipping %s\n", path);
- continue;
- }
-
- lib_idx = GET_INT_FROM_POINTER(BLI_ghash_lookup(libraries, libname));
-
- item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL);
- BLI_BITMAP_ENABLE(item->libraries, lib_idx);
- }
- }
- RNA_END;
-
- BLI_ghash_free(libraries, MEM_freeN, NULL);
- }
- else {
- WMLinkAppendDataItem *item;
-
- wm_link_append_data_library_add(lapp_data, libname);
- item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL);
- BLI_BITMAP_ENABLE(item->libraries, 0);
- }
-
- /* XXX We'd need re-entrant locking on Main for this to work... */
- /* BKE_main_lock(bmain); */
-
- wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C));
-
- /* BKE_main_unlock(bmain); */
-
- wm_link_append_data_free(lapp_data);
-
- /* mark all library linked objects to be updated */
- BKE_main_lib_objects_recalc_all(bmain);
- IMB_colormanagement_check_file_config(bmain);
-
- /* append, rather than linking */
- if ((flag & FILE_LINK) == 0) {
- bool set_fake = RNA_boolean_get(op->ptr, "set_fake");
- BKE_library_make_local(bmain, NULL, true, set_fake);
- }
-
- /* important we unset, otherwise these object wont
- * link into other scenes from this blend file */
- BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
-
- /* recreate dependency graph to include new objects */
- DAG_scene_relations_rebuild(bmain, scene);
-
- /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */
- GPU_materials_free();
-
- /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */
- BLI_strncpy(G.lib, root, FILE_MAX);
-
- WM_event_add_notifier(C, NC_WINDOW, NULL);
-
- return OPERATOR_FINISHED;
-}
-
-static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link)
-{
- PropertyRNA *prop;
-
- /* better not save _any_ settings for this operator */
- /* properties */
- prop = RNA_def_boolean(ot->srna, "link", is_link,
- "Link", "Link the objects or datablocks rather than appending");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
- prop = RNA_def_boolean(ot->srna, "autoselect", true,
- "Select", "Select new objects");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "active_layer", true,
- "Active Layer", "Put new objects on the active layer");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "instance_groups", is_link,
- "Instance Groups", "Create Dupli-Group instances for each group");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
-}
-
-static void WM_OT_link(wmOperatorType *ot)
-{
- ot->name = "Link from Library";
- ot->idname = "WM_OT_link";
- ot->description = "Link from a Library .blend file";
-
- ot->invoke = wm_link_append_invoke;
- ot->exec = wm_link_append_exec;
- ot->poll = wm_link_append_poll;
-
- ot->flag |= OPTYPE_UNDO;
-
- WM_operator_properties_filesel(
- ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE,
- WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES,
- FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
-
- wm_link_append_properties_common(ot, true);
-}
-
-static void WM_OT_append(wmOperatorType *ot)
-{
- ot->name = "Append from Library";
- ot->idname = "WM_OT_append";
- ot->description = "Append from a Library .blend file";
-
- ot->invoke = wm_link_append_invoke;
- ot->exec = wm_link_append_exec;
- ot->poll = wm_link_append_poll;
-
- ot->flag |= OPTYPE_UNDO;
-
- WM_operator_properties_filesel(
- ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE,
- WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES,
- FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
-
- wm_link_append_properties_common(ot, false);
- RNA_def_boolean(ot->srna, "set_fake", false, "Fake User", "Set Fake User for appended items (except Objects and Groups)");
-}
-
-/* *************** recover last session **************** */
-
-void WM_recover_last_session(bContext *C, ReportList *reports)
-{
- char filepath[FILE_MAX];
-
- BLI_make_file_string("/", filepath, BKE_tempdir_base(), BLENDER_QUIT_FILE);
- /* if reports==NULL, it's called directly without operator, we add a quick check here */
- if (reports || BLI_exists(filepath)) {
- G.fileflags |= G_FILE_RECOVER;
-
- wm_file_read_opwrap(C, filepath, reports, true);
-
- G.fileflags &= ~G_FILE_RECOVER;
-
- /* XXX bad global... fixme */
- if (G.main->name[0])
- G.file_loaded = 1; /* prevents splash to show */
- else {
- G.relbase_valid = 0;
- G.save_over = 0; /* start with save preference untitled.blend */
- }
-
- }
-}
-
-static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
-{
- WM_recover_last_session(C, op->reports);
- return OPERATOR_FINISHED;
-}
-
-static void WM_OT_recover_last_session(wmOperatorType *ot)
-{
- ot->name = "Recover Last Session";
- ot->idname = "WM_OT_recover_last_session";
- ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")";
- ot->invoke = WM_operator_confirm;
-
- ot->exec = wm_recover_last_session_exec;
-}
-
-/* *************** recover auto save **************** */
-
-static int wm_recover_auto_save_exec(bContext *C, wmOperator *op)
-{
- char filepath[FILE_MAX];
- bool success;
-
- RNA_string_get(op->ptr, "filepath", filepath);
-
- G.fileflags |= G_FILE_RECOVER;
-
- success = wm_file_read_opwrap(C, filepath, op->reports, true);
-
- G.fileflags &= ~G_FILE_RECOVER;
-
- if (success) {
- return OPERATOR_FINISHED;
- }
- else {
- return OPERATOR_CANCELLED;
- }
-}
-
-static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
-{
- char filename[FILE_MAX];
-
- wm_autosave_location(filename);
- RNA_string_set(op->ptr, "filepath", filename);
- WM_event_add_fileselect(C, op);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-static void WM_OT_recover_auto_save(wmOperatorType *ot)
-{
- ot->name = "Recover Auto Save";
- ot->idname = "WM_OT_recover_auto_save";
- ot->description = "Open an automatically saved file to recover it";
-
- ot->exec = wm_recover_auto_save_exec;
- ot->invoke = wm_recover_auto_save_invoke;
-
- WM_operator_properties_filesel(
- ot, FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE,
- WM_FILESEL_FILEPATH, FILE_LONGDISPLAY, FILE_SORT_TIME);
-}
-
-/* *************** save file as **************** */
-
-static void wm_filepath_default(char *filepath)
-{
- if (G.save_over == false) {
- BLI_ensure_filename(filepath, FILE_MAX, "untitled.blend");
- }
-}
-
-static void save_set_compress(wmOperator *op)
-{
- PropertyRNA *prop;
-
- prop = RNA_struct_find_property(op->ptr, "compress");
- if (!RNA_property_is_set(op->ptr, prop)) {
- if (G.save_over) { /* keep flag for existing file */
- RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0);
- }
- else { /* use userdef for new file */
- RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0);
- }
- }
-}
-
-static void save_set_filepath(wmOperator *op)
-{
- PropertyRNA *prop;
- char name[FILE_MAX];
-
- prop = RNA_struct_find_property(op->ptr, "filepath");
- if (!RNA_property_is_set(op->ptr, prop)) {
- /* if not saved before, get the name of the most recently used .blend file */
- if (G.main->name[0] == 0 && G.recent_files.first) {
- struct RecentFile *recent = G.recent_files.first;
- BLI_strncpy(name, recent->filepath, FILE_MAX);
- }
- else {
- BLI_strncpy(name, G.main->name, FILE_MAX);
- }
-
- wm_filepath_default(name);
- RNA_property_string_set(op->ptr, prop, name);
- }
-}
-
-static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
-{
-
- save_set_compress(op);
- save_set_filepath(op);
-
- WM_event_add_fileselect(C, op);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-/* function used for WM_OT_save_mainfile too */
-static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
-{
- char path[FILE_MAX];
- int fileflags;
-
- save_set_compress(op);
-
- if (RNA_struct_property_is_set(op->ptr, "filepath")) {
- RNA_string_get(op->ptr, "filepath", path);
- }
- else {
- BLI_strncpy(path, G.main->name, FILE_MAX);
- wm_filepath_default(path);
- }
-
- fileflags = G.fileflags & ~G_FILE_USERPREFS;
-
- /* set compression flag */
- BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "compress"),
- G_FILE_COMPRESS);
- BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "relative_remap"),
- G_FILE_RELATIVE_REMAP);
- BKE_BIT_TEST_SET(fileflags,
- (RNA_struct_property_is_set(op->ptr, "copy") &&
- RNA_boolean_get(op->ptr, "copy")),
- G_FILE_SAVE_COPY);
-
-#ifdef USE_BMESH_SAVE_AS_COMPAT
- BKE_BIT_TEST_SET(fileflags,
- (RNA_struct_find_property(op->ptr, "use_mesh_compat") &&
- RNA_boolean_get(op->ptr, "use_mesh_compat")),
- G_FILE_MESH_COMPAT);
-#else
-# error "don't remove by accident"
-#endif
-
- if (wm_file_write(C, path, fileflags, op->reports) != 0)
- return OPERATOR_CANCELLED;
-
- WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL);
-
- return OPERATOR_FINISHED;
-}
-
-/* function used for WM_OT_save_mainfile too */
-static bool blend_save_check(bContext *UNUSED(C), wmOperator *op)
-{
- char filepath[FILE_MAX];
- RNA_string_get(op->ptr, "filepath", filepath);
- if (!BLO_has_bfile_extension(filepath)) {
- /* some users would prefer BLI_replace_extension(),
- * we keep getting nitpicking bug reports about this - campbell */
- BLI_ensure_extension(filepath, FILE_MAX, ".blend");
- RNA_string_set(op->ptr, "filepath", filepath);
- return true;
- }
- return false;
-}
-
-static void WM_OT_save_as_mainfile(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- ot->name = "Save As Blender File";
- ot->idname = "WM_OT_save_as_mainfile";
- ot->description = "Save the current file in the desired location";
-
- ot->invoke = wm_save_as_mainfile_invoke;
- ot->exec = wm_save_as_mainfile_exec;
- ot->check = blend_save_check;
- /* omit window poll so this can work in background mode */
-
- WM_operator_properties_filesel(
- ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE,
- WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
- RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
- RNA_def_boolean(ot->srna, "relative_remap", true, "Remap Relative",
- "Remap relative paths when saving in a different directory");
- prop = RNA_def_boolean(ot->srna, "copy", false, "Save Copy",
- "Save a copy of the actual working state but does not make saved file active");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
-#ifdef USE_BMESH_SAVE_AS_COMPAT
- RNA_def_boolean(ot->srna, "use_mesh_compat", false, "Legacy Mesh Format",
- "Save using legacy mesh format (no ngons) - WARNING: only saves tris and quads, other ngons will "
- "be lost (no implicit triangulation)");
-#endif
-}
-
-/* *************** save file directly ******** */
-
-static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
-{
- int ret;
-
- /* cancel if no active window */
- if (CTX_wm_window(C) == NULL)
- return OPERATOR_CANCELLED;
-
- save_set_compress(op);
- save_set_filepath(op);
-
- /* if we're saving for the first time and prefer relative paths - any existing paths will be absolute,
- * enable the option to remap paths to avoid confusion [#37240] */
- if ((G.relbase_valid == false) && (U.flag & USER_RELPATHS)) {
- PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap");
- if (!RNA_property_is_set(op->ptr, prop)) {
- RNA_property_boolean_set(op->ptr, prop, true);
- }
- }
-
- if (G.save_over) {
- char path[FILE_MAX];
-
- RNA_string_get(op->ptr, "filepath", path);
- if (BLI_exists(path)) {
- ret = WM_operator_confirm_message_ex(C, op, IFACE_("Save Over?"), ICON_QUESTION, path);
- }
- else {
- ret = wm_save_as_mainfile_exec(C, op);
- }
- }
- else {
- WM_event_add_fileselect(C, op);
- ret = OPERATOR_RUNNING_MODAL;
- }
-
- return ret;
-}
-
-static void WM_OT_save_mainfile(wmOperatorType *ot)
-{
- ot->name = "Save Blender File";
- ot->idname = "WM_OT_save_mainfile";
- ot->description = "Save the current Blender file";
-
- ot->invoke = wm_save_mainfile_invoke;
- ot->exec = wm_save_as_mainfile_exec;
- ot->check = blend_save_check;
- /* omit window poll so this can work in background mode */
-
- WM_operator_properties_filesel(
- ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE,
- WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
- RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
- RNA_def_boolean(ot->srna, "relative_remap", false, "Remap Relative",
- "Remap relative paths when saving in a different directory");
-}
-
static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
{
ot->name = "Toggle Window Fullscreen";
diff --git a/source/blender/windowmanager/wm_cursors.h b/source/blender/windowmanager/wm_cursors.h
index e99f80f53cd..c695a12f52c 100644
--- a/source/blender/windowmanager/wm_cursors.h
+++ b/source/blender/windowmanager/wm_cursors.h
@@ -111,7 +111,7 @@ enum {
struct wmWindow;
struct wmEvent;
-int wm_cursor_arrow_move(struct wmWindow *win, struct wmEvent *event);
+bool wm_cursor_arrow_move(struct wmWindow *win, const struct wmEvent *event);
#endif /* __WM_CURSORS_H__ */
diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h
index 4b35f662a99..2eae9cdb012 100644
--- a/source/blender/windowmanager/wm_files.h
+++ b/source/blender/windowmanager/wm_files.h
@@ -31,15 +31,33 @@
#ifndef __WM_FILES_H__
#define __WM_FILES_H__
+struct wmOperatorType;
+
+/* wm_files.c */
void wm_history_file_read(void);
-int wm_history_file_read_exec(bContext *C, wmOperator *op);
-int wm_file_write(struct bContext *C, const char *target, int fileflags, struct ReportList *reports);
-int wm_homefile_read_exec(struct bContext *C, struct wmOperator *op);
int wm_homefile_read(struct bContext *C, struct ReportList *reports, bool from_memory, const char *filepath);
-int wm_homefile_write_exec(struct bContext *C, struct wmOperator *op);
-int wm_userpref_write_exec(struct bContext *C, struct wmOperator *op);
void wm_file_read_report(bContext *C);
+void WM_OT_save_homefile(struct wmOperatorType *ot);
+void WM_OT_userpref_autoexec_path_add(struct wmOperatorType *ot);
+void WM_OT_userpref_autoexec_path_remove(struct wmOperatorType *ot);
+void WM_OT_save_userpref(struct wmOperatorType *ot);
+void WM_OT_read_history(struct wmOperatorType *ot);
+void WM_OT_read_homefile(struct wmOperatorType *ot);
+void WM_OT_read_factory_settings(struct wmOperatorType *ot);
+
+void WM_OT_open_mainfile(struct wmOperatorType *ot);
+
+void WM_OT_revert_mainfile(struct wmOperatorType *ot);
+void WM_OT_recover_last_session(struct wmOperatorType *ot);
+void WM_OT_recover_auto_save(struct wmOperatorType *ot);
+
+void WM_OT_save_as_mainfile(struct wmOperatorType *ot);
+void WM_OT_save_mainfile(struct wmOperatorType *ot);
+
+/* wm_files_link.c */
+void WM_OT_link(struct wmOperatorType *ot);
+void WM_OT_append(struct wmOperatorType *ot);
#endif /* __WM_FILES_H__ */