From 6343d4e233e96acee76d68adc060498313bb8d6c Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Sat, 20 Dec 2008 08:24:24 +0000 Subject: 2.5 - Start of porting of Animation Editors * Added new 'Animation' submodule under Editors. This will be used to house all code + features that are used by many different Animation Editors (Action/Dopesheet and IPO) as well as other parts of Blender. * Added back some of the core code need by the Action/Dopesheet editor, which will also be used by IPO Editor. * Brought back file for keyframing management code (i.e. keyframing.c), but there's still quite a lot of missing stuff that I'll need to restore, so in the meantime, it's #if 0'd out. * Moved markers code to this new module (I'm not sure whether SVN will recognise this change, as TortoiseSVN doesn't seem to have any obvious copy/move commands) --- source/blender/editors/SConscript | 3 +- source/blender/editors/animation/Makefile | 54 + source/blender/editors/animation/SConscript | 9 + source/blender/editors/animation/anim_draw.c | 174 ++ source/blender/editors/animation/anim_filter.c | 208 ++ source/blender/editors/animation/anim_keyframing.c | 2286 ++++++++++++++++++++ source/blender/editors/animation/anim_markers.c | 869 ++++++++ source/blender/editors/include/ED_anim_api.h | 172 ++ source/blender/editors/include/ED_keyframing.h | 134 ++ source/blender/editors/space_action/SConscript | 2 +- source/blender/editors/space_action/space_action.c | 12 +- source/blender/editors/space_time/space_time.c | 3 +- source/blender/editors/space_time/time_ops.c | 10 +- source/blender/editors/util/ed_markers.c | 869 -------- 14 files changed, 3927 insertions(+), 878 deletions(-) create mode 100644 source/blender/editors/animation/Makefile create mode 100644 source/blender/editors/animation/SConscript create mode 100644 source/blender/editors/animation/anim_draw.c create mode 100644 source/blender/editors/animation/anim_filter.c create mode 100644 source/blender/editors/animation/anim_keyframing.c create mode 100644 source/blender/editors/animation/anim_markers.c create mode 100644 source/blender/editors/include/ED_anim_api.h create mode 100644 source/blender/editors/include/ED_keyframing.h delete mode 100644 source/blender/editors/util/ed_markers.c (limited to 'source') diff --git a/source/blender/editors/SConscript b/source/blender/editors/SConscript index 84afc5512a7..e890ac69d76 100644 --- a/source/blender/editors/SConscript +++ b/source/blender/editors/SConscript @@ -6,6 +6,7 @@ SConscript(['datafiles/SConscript', 'space_api/SConscript', 'util/SConscript', 'interface/SConscript', + 'animation/SConscript', 'mesh/SConscript', 'object/SConscript', 'space_buttons/SConscript', @@ -23,5 +24,5 @@ SConscript(['datafiles/SConscript', 'space_script/SConscript', 'space_text/SConscript', 'space_sequencer/SConscript', - 'transform/SConscript', + 'transform/SConscript', # XXX this should be moved near start of list, as many modules depend on this? 'screen/SConscript']) diff --git a/source/blender/editors/animation/Makefile b/source/blender/editors/animation/Makefile new file mode 100644 index 00000000000..19b62891b63 --- /dev/null +++ b/source/blender/editors/animation/Makefile @@ -0,0 +1,54 @@ +# +# $Id: Makefile 14 2002-10-13 15:57:19Z hans $ +# +# ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# The Original Code is Copyright (C) 2007 Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): none yet. +# +# ***** END GPL LICENSE BLOCK ***** +# +# Makes module object directory and bounces make to subdirectories. + +LIBNAME = ed_animation +DIR = $(OCGDIR)/blender/$(LIBNAME) + +include nan_compile.mk + +CFLAGS += $(LEVEL_1_C_WARNINGS) + +CPPFLAGS += -I$(NAN_GLEW)/include +CPPFLAGS += -I$(OPENGL_HEADERS) + +# not very neat.... +CPPFLAGS += -I../../windowmanager +CPPFLAGS += -I../../blenloader +CPPFLAGS += -I../../blenkernel +CPPFLAGS += -I../../blenlib +CPPFLAGS += -I../../makesdna +CPPFLAGS += -I../../makesrna +CPPFLAGS += -I../../imbuf +CPPFLAGS += -I../../python +CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include + +# own include + +CPPFLAGS += -I../include diff --git a/source/blender/editors/animation/SConscript b/source/blender/editors/animation/SConscript new file mode 100644 index 00000000000..b972cdf2256 --- /dev/null +++ b/source/blender/editors/animation/SConscript @@ -0,0 +1,9 @@ +#!/usr/bin/python +Import ('env') + +sources = env.Glob('*.c') + +incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../makesrna ../../imbuf' +incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' + +env.BlenderLib ( 'bf_editors_animation', sources, Split(incs), [], libtype=['core','intern'], priority=[35, 40] ) diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c new file mode 100644 index 00000000000..e2bc73847c0 --- /dev/null +++ b/source/blender/editors/animation/anim_draw.c @@ -0,0 +1,174 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include + +#include "DNA_action_types.h" +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_screen.h" +#include "BKE_utildefines.h" + +#include "ED_anim_api.h" +#include "ED_util.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +/* *************************************************** */ +/* CURRENT FRAME DRAWING */ + +/* Draw current frame number in a little green box beside the current frame indicator */ +static void draw_cfra_number (View2D *v2d, float cfra, short time) +{ + float xscale, yscale, yspace, ypixels, x; + short slen; + char str[32]; + + /* because the frame number text is subject to the same scaling as the contents of the view */ + UI_view2d_getscale(v2d, &xscale, &yscale); + glScalef(1.0/xscale, 1.0, 1.0); + + if (time) + sprintf(str, " %.2f", FRA2TIME(CFRA)); + else + sprintf(str, " %d", CFRA); + slen= UI_GetStringWidth(G.font, str, 0); + + /* get starting coordinates for drawing */ + x= cfra * xscale; + + /* draw green box around/behind text */ + UI_ThemeColor(TH_CFRAME); + UI_ThemeColorShadeAlpha(TH_CFRAME, 0, -100); + glRectf(x, 0, x+slen, 15); + + /* draw current frame number - black text */ + UI_ThemeColor(TH_TEXT); + ui_rasterpos_safe(x-5, 17, 1.0); + UI_DrawString(G.fonts, str, 0); + + /* restore view transform */ + glScalef(xscale, yscale, 1.0); +} + +/* General call for drawing current frame indicator in a */ +void ANIM_draw_cfra (const bContext *C, View2D *v2d, short flag) +{ + Scene *scene= CTX_data_scene(C); + float vec[2]; + + /* Draw a light green line to indicate current frame */ + vec[0]= (float)(scene->r.cfra * scene->r.framelen); + + UI_ThemeColor(TH_CFRAME); + glLineWidth(2.0); + + glBegin(GL_LINE_STRIP); + vec[1]= v2d->cur.ymin; + glVertex2fv(vec); + + vec[1]= v2d->cur.ymax; + glVertex2fv(vec); + glEnd(); + + /* Draw dark green line if slow-parenting/time-offset is enabled */ + if (flag & DRAWCFRA_SHOW_TIMEOFS) { + Object *ob= (scene->basact) ? (scene->basact->object) : 0; + if ((ob) && (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)!=0.0)) { + vec[0]-= give_timeoffset(ob); /* could avoid calling twice */ + + UI_ThemeColorShade(TH_CFRAME, -30); + + glBegin(GL_LINE_STRIP); + /*vec[1]= v2d->cur.ymax;*/ // this is set already. this line is only included + glVertex2fv(vec); + + vec[1]= v2d->cur.ymin; + glVertex2fv(vec); + glEnd(); + } + } + + glLineWidth(1.0); + + /* Draw current frame number in a little box */ + if (flag & DRAWCFRA_SHOW_NUMBOX) { + UI_view2d_view_orthoSpecial(C, v2d, 1); + draw_cfra_number(v2d, vec[0], (flag & DRAWCFRA_UNIT_SECONDS)); + } +} + +/* *************************************************** */ +/* PREVIEW RANGE 'CURTAINS' */ + +/* Draw preview range 'curtains' for highlighting where the animation data is */ +void ANIM_draw_previewrange (const bContext *C, View2D *v2d) +{ + Scene *scene= CTX_data_scene(C); + + /* only draw this if preview range is set */ + if (scene->r.psfra) { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glColor4f(0.0f, 0.0f, 0.0f, 0.4f); + + /* only draw two separate 'curtains' if there's no overlap between them */ + if (PSFRA < PEFRA) { + glRectf(v2d->cur.xmin, v2d->cur.ymin, PSFRA, v2d->cur.ymax); + glRectf(PEFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); + } + else { + glRectf(v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); + } + + glDisable(GL_BLEND); + } +} + +/* *************************************************** */ diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c new file mode 100644 index 00000000000..e82e61aa158 --- /dev/null +++ b/source/blender/editors/animation/anim_filter.c @@ -0,0 +1,208 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* This file defines the system for filtering data into a form suitable for + * use by the Animation Editors, thus acting as a means by which the Animation + * Editors maintain a level of abstraction from the data they actually manipulate. + * Thus, they only need to check on the type of the data they're manipulating, and + * NOT worry about various layers of context/hierarchy checks. + * + * While this is primarily used for the Action/Dopesheet Editor (and its accessory modes), + * the IPO Editor also uses this for it's channel list and for determining which curves + * are being edited. + * + * -- Joshua Leung, Dec 2008 + */ + +#include +#include + +#include "DNA_listbase.h" +#include "DNA_ID.h" +#include "DNA_action_types.h" +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_lattice_types.h" +#include "DNA_key_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_screen.h" +#include "BKE_utildefines.h" + +#include "ED_anim_api.h" +#include "ED_types.h" +#include "ED_util.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +/* ************************************************************ */ +/* Blender Context <-> Animation Context mapping */ + +/* ----------- Private Stuff - Action Editor ------------- */ + +/* Get shapekey data being edited (for Action Editor -> ShapeKey mode) */ +/* Note: there's a similar function in key.c (ob_get_key) */ +Key *actedit_get_shapekeys (const bContext *C, SpaceAction *saction) +{ + Scene *scene= CTX_data_scene(C); + Object *ob; + Key *key; + + ob = OBACT; // XXX er... + if (ob == NULL) + return NULL; + + /* pinning is not available in 'ShapeKey' mode... */ + //if (saction->pin) return NULL; + + /* shapekey data is stored with geometry data */ + switch (ob->type) { + case OB_MESH: + key= ((Mesh *)ob->data)->key; + break; + + case OB_LATTICE: + key= ((Lattice *)ob->data)->key; + break; + + case OB_CURVE: + case OB_SURF: + key= ((Curve *)ob->data)->key; + break; + + default: + return NULL; + } + + if (key) { + if (key->type == KEY_RELATIVE) + return key; + } + + return NULL; +} + +/* Get data being edited in Action Editor (depending on current 'mode') */ +static void *actedit_get_context (const bContext *C, SpaceAction *saction, short *datatype) +{ + Scene *scene= CTX_data_scene(C); + + /* sync settings with current view status, then return appropriate data */ + switch (saction->mode) { + case SACTCONT_ACTION: /* 'Action Editor' */ + /* if not pinned, sync with active object */ + if (saction->pin == 0) { + if (OBACT) + saction->action = OBACT->action; + else + saction->action= NULL; + } + + *datatype= ANIMCONT_ACTION; + return saction->action; + + case SACTCONT_SHAPEKEY: /* 'ShapeKey Editor' */ + *datatype= ANIMCONT_SHAPEKEY; + return actedit_get_shapekeys(C, saction); + + case SACTCONT_GPENCIL: /* Grease Pencil */ // XXX review how this mode is handled... + *datatype=ANIMCONT_GPENCIL; + return CTX_wm_screen(C); // FIXME: add that dopesheet type thing here! + break; + + case SACTCONT_DOPESHEET: /* DopeSheet */ + /* update scene-pointer (no need to check for pinning yet, as not implemented) */ + saction->ads.source= (ID *)scene; + + *datatype= ANIMCONT_DOPESHEET; + return &saction->ads; + + default: /* unhandled yet */ + *datatype= ANIMCONT_NONE; + return NULL; + } +} + +/* ----------- Private Stuff - IPO Editor ------------- */ + +/* ----------- Public API --------------- */ + +/* Obtain current anim-data context from Blender Context info */ +void *animdata_get_context (const bContext *C, short *datatype) +{ + ScrArea *sa= CTX_wm_area(C); + + /* set datatype to 'None' for convenience */ + if (datatype == NULL) return NULL; + *datatype= ANIMCONT_NONE; + if (sa == NULL) return NULL; /* highly unlikely to happen, but still! */ + + /* context depends on editor we are currently in */ + switch (sa->spacetype) { + case SPACE_ACTION: + { + SpaceAction *saction= (SpaceAction *)CTX_wm_space_data(C); + return actedit_get_context(C, saction, datatype); + } + break; + + case SPACE_IPO: + { + SpaceIpo *sipo= (SpaceIpo *)CTX_wm_space_data(C); + // ... + } + break; + } + + /* nothing appropriate */ + return NULL; +} + +/* ************************************************************ */ + +/* ************************************************************ */ diff --git a/source/blender/editors/animation/anim_keyframing.c b/source/blender/editors/animation/anim_keyframing.c new file mode 100644 index 00000000000..3e303a2d26c --- /dev/null +++ b/source/blender/editors/animation/anim_keyframing.c @@ -0,0 +1,2286 @@ +/** + * $Id: keyframing.c 17745 2008-12-08 09:16:09Z aligorith $ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender (with some old code) + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#if 0 // XXX reenable this file again later... + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "MEM_guardedalloc.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_dynstr.h" + +#include "DNA_listBase.h" +#include "DNA_ID.h" +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_constraint_types.h" +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_key_types.h" +#include "DNA_lamp_types.h" +#include "DNA_object_types.h" +#include "DNA_material_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_texture_types.h" +#include "DNA_userdef_types.h" +#include "DNA_vec_types.h" +#include "DNA_view3d_types.h" +#include "DNA_world_types.h" + +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BKE_blender.h" +#include "BKE_main.h" // XXX not needed old cruft? +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_constraint.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_ipo.h" +#include "BKE_key.h" +#include "BKE_object.h" +#include "BKE_material.h" + +#include "ED_keyframing.h" + +#if 0 // XXX resolve these old dependencies! +#include "BIF_butspace.h" +#include "BIF_editaction.h" +#include "BIF_editkey.h" +#include "BIF_interface.h" +#include "BIF_mywindow.h" +#include "BIF_poseobject.h" +#include "BIF_screen.h" +#include "BIF_space.h" +#include "BIF_toolbox.h" +#include "BIF_toets.h" + +#include "BSE_editipo.h" +#include "BSE_node.h" +#include "BSE_time.h" +#include "BSE_view.h" + +#include "blendef.h" + +#include "PIL_time.h" /* sleep */ +#include "mydevice.h" +#endif // XXX resolve these old dependencies! + + + +/* ************************************************** */ +/* LOCAL TYPES AND DEFINES */ + +/* ----------- Common KeyData Sources ------------ */ + +/* temporary struct to gather data combos to keyframe */ +typedef struct bCommonKeySrc { + struct bCommonKeySrc *next, *prev; + + /* general data/destination-source settings */ + ID *id; /* id-block this comes from */ + char *actname; /* name of action channel */ + char *constname; /* name of constraint channel */ + + /* general destination source settings */ + Ipo *ipo; /* ipo-block that id-block has (optional) */ + bAction *act; /* action-block that id-block has (optional) */ + + /* pose-level settings */ + bPoseChannel *pchan; /* pose channel */ + + /* buttons-window settings */ + int map; /* offset to apply to certain adrcodes */ +} bCommonKeySrc; + +/* -------------- Keying Sets ------------------- */ + +/* storage for iterator for looping over keyingset channels */ +typedef struct bKS_AdrcodeGetter { + struct bKeyingSet *ks; /* keyingset this applies to */ + struct bCommonKeySrc *cks; /* data to insert/delete keyframes... */ + + short index; /* index of current channel to resume from */ + short tot; /* index after which we start returning from some special collection */ +} bKS_AdrcodeGetter; + +/* flags to look out for in keyingset channels... */ +#define KAG_CHAN_EXTEND (-1) + + +/* keying set - a set of channels that will be keyframed together */ +// TODO: move this to a header to allow custom sets someday? +typedef struct bKeyingSet { + /* callback func to consider if keyingset should be included + * (by default, if this is undefined, item will be shown) + */ + short (*include_cb)(struct bKeyingSet *, const char *); + + char name[48]; /* name of keyingset */ + int blocktype; /* blocktype that all channels belong to */ // in future, this may be eliminated + short flag; /* flags to use when setting keyframes */ + + short chan_num; /* number of channels to insert keyframe in */ + short adrcodes[32]; /* adrcodes for channels to insert keys for (ideally would be variable-len, but limit of 32 will suffice) */ +} bKeyingSet; + +/* keying set context - an array of keying sets and the number of them */ +typedef struct bKeyingContext { + bKeyingSet *keyingsets; /* array containing the keyingsets of interest */ + bKeyingSet *lastused; /* item that was chosen last time*/ + int tot; /* number of keyingsets in */ +} bKeyingContext; + +/* ************************************************** */ +/* KEYFRAME INSERTION */ + +/* -------------- BezTriple Insertion -------------------- */ + +/* threshold for inserting keyframes - threshold here should be good enough for now, but should become userpref */ +#define BEZT_INSERT_THRESH 0.00001 + +/* Binary search algorithm for finding where to insert BezTriple. (for use by insert_bezt_icu) + * Returns the index to insert at (data already at that index will be offset if replace is 0) + */ +static int binarysearch_bezt_index (BezTriple array[], float frame, int arraylen, short *replace) +{ + int start=0, end=arraylen; + int loopbreaker= 0, maxloop= arraylen * 2; + + /* initialise replace-flag first */ + *replace= 0; + + /* sneaky optimisations (don't go through searching process if...): + * - keyframe to be added is to be added out of current bounds + * - keyframe to be added would replace one of the existing ones on bounds + */ + if ((arraylen <= 0) || (array == NULL)) { + printf("Warning: binarysearch_bezt_index() encountered invalid array \n"); + return 0; + } + else { + /* check whether to add before/after/on */ + float framenum; + + /* 'First' Keyframe (when only one keyframe, this case is used) */ + framenum= array[0].vec[1][0]; + if (IS_EQT(frame, framenum, BEZT_INSERT_THRESH)) { + *replace = 1; + return 0; + } + else if (frame < framenum) + return 0; + + /* 'Last' Keyframe */ + framenum= array[(arraylen-1)].vec[1][0]; + if (IS_EQT(frame, framenum, BEZT_INSERT_THRESH)) { + *replace= 1; + return (arraylen - 1); + } + else if (frame > framenum) + return arraylen; + } + + + /* most of the time, this loop is just to find where to put it + * 'loopbreaker' is just here to prevent infinite loops + */ + for (loopbreaker=0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) { + /* compute and get midpoint */ + int mid = (start + end) / 2; + float midfra= array[mid].vec[1][0]; + + /* check if exactly equal to midpoint */ + if (IS_EQT(frame, midfra, BEZT_INSERT_THRESH)) { + *replace = 1; + return mid; + } + + /* repeat in upper/lower half */ + if (frame > midfra) + start= mid + 1; + else if (frame < midfra) + end= mid - 1; + } + + /* print error if loop-limit exceeded */ + if (loopbreaker == (maxloop-1)) { + printf("Error: binarysearch_bezt_index() was taking too long \n"); + + // include debug info + printf("\tround = %d: start = %d, end = %d, arraylen = %d \n", loopbreaker, start, end, arraylen); + } + + /* not found, so return where to place it */ + return start; +} + +/* This function adds a given BezTriple to an IPO-Curve. It will allocate + * memory for the array if needed, and will insert the BezTriple into a + * suitable place in chronological order. + * + * NOTE: any recalculate of the IPO-Curve that needs to be done will need to + * be done by the caller. + */ +int insert_bezt_icu (IpoCurve *icu, BezTriple *bezt) +{ + BezTriple *newb; + int i= 0; + + if (icu->bezt) { + short replace = -1; + i = binarysearch_bezt_index(icu->bezt, bezt->vec[1][0], icu->totvert, &replace); + + if (replace) { + /* sanity check: 'i' may in rare cases exceed arraylen */ + // FIXME: do not overwrite handletype if just replacing...? + if ((i >= 0) && (i < icu->totvert)) + *(icu->bezt + i) = *bezt; + } + else { + /* add new */ + newb= MEM_callocN((icu->totvert+1)*sizeof(BezTriple), "beztriple"); + + /* add the beztriples that should occur before the beztriple to be pasted (originally in ei->icu) */ + if (i > 0) + memcpy(newb, icu->bezt, i*sizeof(BezTriple)); + + /* add beztriple to paste at index i */ + *(newb + i)= *bezt; + + /* add the beztriples that occur after the beztriple to be pasted (originally in icu) */ + if (i < icu->totvert) + memcpy(newb+i+1, icu->bezt+i, (icu->totvert-i)*sizeof(BezTriple)); + + /* replace (+ free) old with new */ + MEM_freeN(icu->bezt); + icu->bezt= newb; + + icu->totvert++; + } + } + else { + icu->bezt= MEM_callocN(sizeof(BezTriple), "beztriple"); + *(icu->bezt)= *bezt; + icu->totvert= 1; + } + + + /* we need to return the index, so that some tools which do post-processing can + * detect where we added the BezTriple in the array + */ + return i; +} + +/* This function is a wrapper for insert_bezt_icu, and should be used when + * adding a new keyframe to a curve, when the keyframe doesn't exist anywhere + * else yet. + * + * 'fast' - is only for the python API where importing BVH's would take an extreamly long time. + */ +void insert_vert_icu (IpoCurve *icu, float x, float y, short fast) +{ + BezTriple beztr; + int a, h1, h2; + + /* set all three points, for nicer start position */ + memset(&beztr, 0, sizeof(BezTriple)); + beztr.vec[0][0]= x; + beztr.vec[0][1]= y; + beztr.vec[1][0]= x; + beztr.vec[1][1]= y; + beztr.vec[2][0]= x; + beztr.vec[2][1]= y; + beztr.hide= IPO_BEZ; + beztr.f1= beztr.f2= beztr.f3= SELECT; + beztr.h1= beztr.h2= HD_AUTO; + + /* add temp beztriple to keyframes */ + a= insert_bezt_icu(icu, &beztr); + + /* what if 'a' is a negative index? + * for now, just exit to prevent any segfaults + */ + if (a < 0) return; + + /* don't recalculate handles if fast is set + * - this is a hack to make importers faster + * - we may calculate twice (see editipo_changed(), due to autohandle needing two calculations) + */ + if (!fast) calchandles_ipocurve(icu); + + /* set handletype and interpolation */ + if (icu->totvert > 2) { + BezTriple *bezt= (icu->bezt + a); + + /* set handles (autohandles by default) */ + h1= h2= HD_AUTO; + + if (a > 0) h1= (bezt-1)->h2; + if (a < icu->totvert-1) h2= (bezt+1)->h1; + + bezt->h1= h1; + bezt->h2= h2; + + /* set interpolation (if curve is using IPO_MIXED, then take from previous) */ + if (icu->ipo == IPO_MIXED) { + if (a > 0) bezt->ipo= (bezt-1)->ipo; + else if (a < icu->totvert-1) bezt->ipo= (bezt+1)->ipo; + } + else + bezt->ipo= icu->ipo; + + /* don't recalculate handles if fast is set + * - this is a hack to make importers faster + * - we may calculate twice (see editipo_changed(), due to autohandle needing two calculations) + */ + if (!fast) calchandles_ipocurve(icu); + } + else { + BezTriple *bezt= (icu->bezt + a); + + /* set interpolation directly from ipo-curve */ + bezt->ipo= icu->ipo; + } +} + +/* ------------------- Get Data ------------------------ */ + +/* Get pointer to use to get values from */ +// FIXME: this should not be possible with Data-API +static void *get_context_ipo_poin (ID *id, int blocktype, char *actname, char *constname, IpoCurve *icu, int *vartype) +{ + switch (blocktype) { + case ID_PO: /* posechannel */ + if (GS(id->name)==ID_OB) { + Object *ob= (Object *)id; + bPoseChannel *pchan= get_pose_channel(ob->pose, actname); + + if (pchan) { + if (ELEM3(icu->adrcode, AC_EUL_X, AC_EUL_Y, AC_EUL_Z)) + *vartype= IPO_FLOAT_DEGR; + else + *vartype= IPO_FLOAT; + return get_pchan_ipo_poin(pchan, icu->adrcode); + } + } + break; + + case ID_CO: /* constraint */ + if ((GS(id->name)==ID_OB) && (constname && constname[0])) { + Object *ob= (Object *)id; + bConstraint *con; + + /* assume that we only want the influence (as only used for Constraint Channels) */ + if ((ob->ipoflag & OB_ACTION_OB) && !strcmp(actname, "Object")) { + for (con= ob->constraints.first; con; con= con->next) { + if (strcmp(constname, con->name)==0) { + *vartype= IPO_FLOAT; + return &con->enforce; + } + } + } + else if (ob->pose) { + bPoseChannel *pchan= get_pose_channel(ob->pose, actname); + + if (pchan) { + for (con= pchan->constraints.first; con; con= con->next) { + if (strcmp(constname, con->name)==0) { + *vartype= IPO_FLOAT; + return &con->enforce; + } + } + } + } + } + break; + + case ID_OB: /* object */ + /* hack: layer channels for object need to be keyed WITHOUT localview flag... + * tsk... tsk... why must we just dump bitflags upon users :/ + */ + if ((GS(id->name)==ID_OB) && (icu->adrcode==OB_LAY)) { + Object *ob= (Object *)id; + static int layer = 0; + + /* init layer to be the object's layer var, then remove local view from it */ + layer = ob->lay; + layer &= 0xFFFFFF; + *vartype= IPO_INT_BIT; + + /* return pointer to this static var + * - assumes that this pointer won't be stored for use later, so may not be threadsafe + * if multiple keyframe calls are made, but that is unlikely to happen in the near future + */ + return (void *)(&layer); + } + /* no break here for other ob channel-types - as they can be done normally */ + + default: /* normal data-source */ + return get_ipo_poin(id, icu, vartype); + } + + /* not valid... */ + return NULL; +} + + +/* -------------- 'Smarter' Keyframing Functions -------------------- */ +/* return codes for new_key_needed */ +enum { + KEYNEEDED_DONTADD = 0, + KEYNEEDED_JUSTADD, + KEYNEEDED_DELPREV, + KEYNEEDED_DELNEXT +} eKeyNeededStatus; + +/* This helper function determines whether a new keyframe is needed */ +/* Cases where keyframes should not be added: + * 1. Keyframe to be added bewteen two keyframes with similar values + * 2. Keyframe to be added on frame where two keyframes are already situated + * 3. Keyframe lies at point that intersects the linear line between two keyframes + */ +static short new_key_needed (IpoCurve *icu, float cFrame, float nValue) +{ + BezTriple *bezt=NULL, *prev=NULL; + int totCount, i; + float valA = 0.0f, valB = 0.0f; + + /* safety checking */ + if (icu == NULL) return KEYNEEDED_JUSTADD; + totCount= icu->totvert; + if (totCount == 0) return KEYNEEDED_JUSTADD; + + /* loop through checking if any are the same */ + bezt= icu->bezt; + for (i=0; ivec[1][0]; + beztVal= bezt->vec[1][1]; + + if (prev) { + /* there is a keyframe before the one currently being examined */ + + /* get previous time+value */ + prevPosi= prev->vec[1][0]; + prevVal= prev->vec[1][1]; + + /* keyframe to be added at point where there are already two similar points? */ + if (IS_EQ(prevPosi, cFrame) && IS_EQ(beztPosi, cFrame) && IS_EQ(beztPosi, prevPosi)) { + return KEYNEEDED_DONTADD; + } + + /* keyframe between prev+current points ? */ + if ((prevPosi <= cFrame) && (cFrame <= beztPosi)) { + /* is the value of keyframe to be added the same as keyframes on either side ? */ + if (IS_EQ(prevVal, nValue) && IS_EQ(beztVal, nValue) && IS_EQ(prevVal, beztVal)) { + return KEYNEEDED_DONTADD; + } + else { + float realVal; + + /* get real value of curve at that point */ + realVal= eval_icu(icu, cFrame); + + /* compare whether it's the same as proposed */ + if (IS_EQ(realVal, nValue)) + return KEYNEEDED_DONTADD; + else + return KEYNEEDED_JUSTADD; + } + } + + /* new keyframe before prev beztriple? */ + if (cFrame < prevPosi) { + /* A new keyframe will be added. However, whether the previous beztriple + * stays around or not depends on whether the values of previous/current + * beztriples and new keyframe are the same. + */ + if (IS_EQ(prevVal, nValue) && IS_EQ(beztVal, nValue) && IS_EQ(prevVal, beztVal)) + return KEYNEEDED_DELNEXT; + else + return KEYNEEDED_JUSTADD; + } + } + else { + /* just add a keyframe if there's only one keyframe + * and the new one occurs before the exisiting one does. + */ + if ((cFrame < beztPosi) && (totCount==1)) + return KEYNEEDED_JUSTADD; + } + + /* continue. frame to do not yet passed (or other conditions not met) */ + if (i < (totCount-1)) { + prev= bezt; + bezt++; + } + else + break; + } + + /* Frame in which to add a new-keyframe occurs after all other keys + * -> If there are at least two existing keyframes, then if the values of the + * last two keyframes and the new-keyframe match, the last existing keyframe + * gets deleted as it is no longer required. + * -> Otherwise, a keyframe is just added. 1.0 is added so that fake-2nd-to-last + * keyframe is not equal to last keyframe. + */ + bezt= (icu->bezt + (icu->totvert - 1)); + valA= bezt->vec[1][1]; + + if (prev) + valB= prev->vec[1][1]; + else + valB= bezt->vec[1][1] + 1.0f; + + if (IS_EQ(valA, nValue) && IS_EQ(valA, valB)) + return KEYNEEDED_DELPREV; + else + return KEYNEEDED_JUSTADD; +} + +/* ------------------ 'Visual' Keyframing Functions ------------------ */ + +/* internal status codes for visualkey_can_use */ +enum { + VISUALKEY_NONE = 0, + VISUALKEY_LOC, + VISUALKEY_ROT +}; + +/* This helper function determines if visual-keyframing should be used when + * inserting keyframes for the given channel. As visual-keyframing only works + * on Object and Pose-Channel blocks, this should only get called for those + * blocktypes, when using "standard" keying but 'Visual Keying' option in Auto-Keying + * settings is on. + */ +static short visualkey_can_use (ID *id, int blocktype, char *actname, char *constname, int adrcode) +{ + Object *ob= NULL; + bConstraint *con= NULL; + short searchtype= VISUALKEY_NONE; + + /* validate data */ + if ((id == NULL) || (GS(id->name)!=ID_OB) || !(ELEM(blocktype, ID_OB, ID_PO))) + return 0; + + /* get first constraint and determine type of keyframe constraints to check for*/ + ob= (Object *)id; + + if (blocktype == ID_OB) { + con= ob->constraints.first; + + if (ELEM3(adrcode, OB_LOC_X, OB_LOC_Y, OB_LOC_Z)) + searchtype= VISUALKEY_LOC; + else if (ELEM3(adrcode, OB_ROT_X, OB_ROT_Y, OB_ROT_Z)) + searchtype= VISUALKEY_ROT; + } + else if (blocktype == ID_PO) { + bPoseChannel *pchan= get_pose_channel(ob->pose, actname); + con= pchan->constraints.first; + + if (ELEM3(adrcode, AC_LOC_X, AC_LOC_Y, AC_LOC_Z)) + searchtype= VISUALKEY_LOC; + else if (ELEM4(adrcode, AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z)) + searchtype= VISUALKEY_ROT; + } + + /* only search if a searchtype and initial constraint are available */ + if (searchtype && con) { + for (; con; con= con->next) { + /* only consider constraint if it is not disabled, and has influence */ + if (con->flag & CONSTRAINT_DISABLE) continue; + if (con->enforce == 0.0f) continue; + + /* some constraints may alter these transforms */ + switch (con->type) { + /* multi-transform constraints */ + case CONSTRAINT_TYPE_CHILDOF: + return 1; + case CONSTRAINT_TYPE_TRANSFORM: + return 1; + case CONSTRAINT_TYPE_FOLLOWPATH: + return 1; + case CONSTRAINT_TYPE_KINEMATIC: + return 1; + + /* single-transform constraits */ + case CONSTRAINT_TYPE_TRACKTO: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_ROTLIMIT: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_LOCLIMIT: + if (searchtype==VISUALKEY_LOC) return 1; + break; + case CONSTRAINT_TYPE_ROTLIKE: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_DISTLIMIT: + if (searchtype==VISUALKEY_LOC) return 1; + break; + case CONSTRAINT_TYPE_LOCLIKE: + if (searchtype==VISUALKEY_LOC) return 1; + break; + case CONSTRAINT_TYPE_LOCKTRACK: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_MINMAX: + if (searchtype==VISUALKEY_LOC) return 1; + break; + + default: + break; + } + } + } + + /* when some condition is met, this function returns, so here it can be 0 */ + return 0; +} + +/* This helper function extracts the value to use for visual-keyframing + * In the event that it is not possible to perform visual keying, try to fall-back + * to using the poin method. Assumes that all data it has been passed is valid. + */ +static float visualkey_get_value (ID *id, int blocktype, char *actname, char *constname, int adrcode, IpoCurve *icu) +{ + Object *ob; + void *poin = NULL; + int index, vartype; + + /* validate situtation */ + if ((id==NULL) || (GS(id->name)!=ID_OB) || (ELEM(blocktype, ID_OB, ID_PO)==0)) + return 0.0f; + + /* get object */ + ob= (Object *)id; + + /* only valid for objects or posechannels */ + if (blocktype == ID_OB) { + /* parented objects are not supported, as the effects of the parent + * are included in the matrix, which kindof beats the point + */ + if (ob->parent == NULL) { + /* only Location or Rotation keyframes are supported now */ + if (ELEM3(adrcode, OB_LOC_X, OB_LOC_Y, OB_LOC_Z)) { + /* assumes that OB_LOC_Z > OB_LOC_Y > OB_LOC_X */ + index= adrcode - OB_LOC_X; + + return ob->obmat[3][index]; + } + else if (ELEM3(adrcode, OB_ROT_X, OB_ROT_Y, OB_ROT_Z)) { + float eul[3]; + + /* assumes that OB_ROT_Z > OB_ROT_Y > OB_ROT_X */ + index= adrcode - OB_ROT_X; + + Mat4ToEul(ob->obmat, eul); + return eul[index]*(5.72958f); + } + } + } + else if (blocktype == ID_PO) { + bPoseChannel *pchan; + float tmat[4][4]; + + /* get data to use */ + pchan= get_pose_channel(ob->pose, actname); + + /* Although it is not strictly required for this particular space conversion, + * arg1 must not be null, as there is a null check for the other conversions to + * be safe. Therefore, the active object is passed here, and in many cases, this + * will be what owns the pose-channel that is getting this anyway. + */ + Mat4CpyMat4(tmat, pchan->pose_mat); + constraint_mat_convertspace(ob, pchan, tmat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL); + + /* Loc, Rot/Quat keyframes are supported... */ + if (ELEM3(adrcode, AC_LOC_X, AC_LOC_Y, AC_LOC_Z)) { + /* assumes that AC_LOC_Z > AC_LOC_Y > AC_LOC_X */ + index= adrcode - AC_LOC_X; + + /* only use for non-connected bones */ + if ((pchan->bone->parent) && !(pchan->bone->flag & BONE_CONNECTED)) + return tmat[3][index]; + else if (pchan->bone->parent == NULL) + return tmat[3][index]; + } + else if (ELEM4(adrcode, AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z)) { + float trimat[3][3], quat[4]; + + /* assumes that AC_QUAT_Z > AC_QUAT_Y > AC_QUAT_X > AC_QUAT_W */ + index= adrcode - AC_QUAT_W; + + Mat3CpyMat4(trimat, tmat); + Mat3ToQuat_is_ok(trimat, quat); + + return quat[index]; + } + } + + /* as the function hasn't returned yet, try reading from poin */ + poin= get_context_ipo_poin(id, blocktype, actname, constname, icu, &vartype); + if (poin) + return read_ipo_poin(poin, vartype); + else + return 0.0; +} + + +/* ------------------------- Insert Key API ------------------------- */ + +/* Main Keyframing API call: + * Use this when validation of necessary animation data isn't necessary as it + * already exists. It will insert a keyframe using the current value being keyframed. + * + * The flag argument is used for special settings that alter the behaviour of + * the keyframe insertion. These include the 'visual' keyframing modes, quick refresh, + * and extra keyframe filtering. + */ +short insertkey (ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag) +{ + IpoCurve *icu; + + /* get ipo-curve */ + //icu= verify_ipocurve(id, blocktype, actname, constname, NULL, adrcode, 1); // XXX this call needs to be in blenkernel + + /* only continue if we have an ipo-curve to add keyframe to */ + if (icu) { + float cfra = frame_to_float(CFRA); + float curval= 0.0f; + + /* apply special time tweaking */ + if (GS(id->name) == ID_OB) { + Object *ob= (Object *)id; + + /* apply NLA-scaling (if applicable) */ + if (actname && actname[0]) + cfra= get_action_frame(ob, cfra); + + /* ancient time-offset cruft */ + if ( (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)) ) { + /* actually frametofloat calc again! */ + cfra-= give_timeoffset(ob)*G.scene->r.framelen; + } + } + + /* obtain value to give keyframe */ + if ( (flag & INSERTKEY_MATRIX) && + (visualkey_can_use(id, blocktype, actname, constname, adrcode)) ) + { + /* visual-keying is only available for object and pchan datablocks, as + * it works by keyframing using a value extracted from the final matrix + * instead of using the kt system to extract a value. + */ + curval= visualkey_get_value(id, blocktype, actname, constname, adrcode, icu); + } + else { + void *poin; + int vartype; + + /* get pointer to data to read from */ + poin = get_context_ipo_poin(id, blocktype, actname, constname, icu, &vartype); + if (poin == NULL) { + printf("Insert Key: No pointer to variable obtained \n"); + return 0; + } + + /* use kt's read_poin function to extract value (kt->read_poin should + * exist in all cases, but it never hurts to check) + */ + curval= read_ipo_poin(poin, vartype); + } + + /* only insert keyframes where they are needed */ + if (flag & INSERTKEY_NEEDED) { + short insert_mode; + + /* check whether this curve really needs a new keyframe */ + insert_mode= new_key_needed(icu, cfra, curval); + + /* insert new keyframe at current frame */ + if (insert_mode) + insert_vert_icu(icu, cfra, curval, (flag & INSERTKEY_FAST)); + + /* delete keyframe immediately before/after newly added */ + switch (insert_mode) { + case KEYNEEDED_DELPREV: + delete_icu_key(icu, icu->totvert-2, 1); + break; + case KEYNEEDED_DELNEXT: + delete_icu_key(icu, 1, 1); + break; + } + + /* only return success if keyframe added */ + if (insert_mode) + return 1; + } + else { + /* just insert keyframe */ + insert_vert_icu(icu, cfra, curval, (flag & INSERTKEY_FAST)); + + /* return success */ + return 1; + } + } + + /* return failure */ + return 0; +} + + +/* ************************************************** */ +/* KEYFRAME DELETION */ + +/* Main Keyframing API call: + * Use this when validation of necessary animation data isn't necessary as it + * already exists. It will delete a keyframe at the current frame. + * + * The flag argument is used for special settings that alter the behaviour of + * the keyframe deletion. These include the quick refresh options. + */ +short deletekey (ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag) +{ + Ipo *ipo; + IpoCurve *icu; + + /* get ipo-curve + * Note: here is one of the places where we don't want new ipo + ipo-curve added! + * so 'add' var must be 0 + */ + // XXX funcs here need to be recoded in blenkernel... + //ipo= verify_ipo(id, blocktype, actname, constname, NULL, 0); + //icu= verify_ipocurve(id, blocktype, actname, constname, NULL, adrcode, 0); + + /* only continue if we have an ipo-curve to remove keyframes from */ + if (icu) { + float cfra = frame_to_float(CFRA); + short found = -1; + int i; + + /* apply special time tweaking */ + if (GS(id->name) == ID_OB) { + Object *ob= (Object *)id; + + /* apply NLA-scaling (if applicable) */ + if (actname && actname[0]) + cfra= get_action_frame(ob, cfra); + + /* ancient time-offset cruft */ + if ( (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)) ) { + /* actually frametofloat calc again! */ + cfra-= give_timeoffset(ob)*G.scene->r.framelen; + } + } + + /* try to find index of beztriple to get rid of */ + i = binarysearch_bezt_index(icu->bezt, cfra, icu->totvert, &found); + if (found) { + /* delete the key at the index (will sanity check + do recalc afterwards ) */ + delete_icu_key(icu, i, 1); + + /* Only delete curve too if there isn't an ipo-driver still hanging around on an empty curve */ + if (icu->totvert==0 && icu->driver==NULL) { + BLI_remlink(&ipo->curve, icu); + free_ipo_curve(icu); + } + + /* return success */ + return 1; + } + } + + /* return failure */ + return 0; +} + +/* ************************************************** */ +/* COMMON KEYFRAME MANAGEMENT (common_insertkey/deletekey) */ + +/* mode for common_modifykey */ +enum { + COMMONKEY_MODE_INSERT = 0, + COMMONKEY_MODE_DELETE, +} eCommonModifyKey_Modes; + +/* --------- KeyingSet Adrcode Getters ------------ */ + +/* initialise a channel-getter storage */ +static void ks_adrcodegetter_init (bKS_AdrcodeGetter *kag, bKeyingSet *ks, bCommonKeySrc *cks) +{ + /* error checking */ + if (kag == NULL) + return; + + if (ELEM(NULL, ks, cks)) { + /* set invalid settings that won't cause harm */ + kag->ks= NULL; + kag->cks= NULL; + kag->index= -2; + kag->tot= 0; + } + else { + /* store settings */ + kag->ks= ks; + kag->cks= cks; + + /* - index is -1, as that allows iterators to return first element + * - tot is chan_num by default, but may get overriden if -1 is encountered (for extension-type getters) + */ + kag->index= -1; + kag->tot= ks->chan_num; + } +} + +/* 'default' channel-getter that will be used when iterating through keyingset's channels + * - iteration will stop when adrcode <= 0 is encountered, so we use that as escape + */ +static short ks_getnextadrcode_default (bKS_AdrcodeGetter *kag) +{ + bKeyingSet *ks= (kag)? kag->ks : NULL; + + /* error checking */ + if (ELEM(NULL, kag, ks)) return 0; + if (kag->tot <= 0) return 0; + + kag->index++; + if ((kag->index < 0) || (kag->index >= kag->tot)) return 0; + + /* return the adrcode stored at index then */ + return ks->adrcodes[kag->index]; +} + +/* add map flag (for MTex channels, as certain ones need special offset) */ +static short ks_getnextadrcode_addmap (bKS_AdrcodeGetter *kag) +{ + short adrcode= ks_getnextadrcode_default(kag); + + /* if there was an adrcode returned, assume that kag stuff is set ok */ + if (adrcode) { + bCommonKeySrc *cks= kag->cks; + bKeyingSet *ks= kag->ks; + + if (ELEM3(ks->blocktype, ID_MA, ID_LA, ID_WO)) { + switch (adrcode) { + case MAP_OFS_X: case MAP_OFS_Y: case MAP_OFS_Z: + case MAP_SIZE_X: case MAP_SIZE_Y: case MAP_SIZE_Z: + case MAP_R: case MAP_G: case MAP_B: case MAP_DVAR: + case MAP_COLF: case MAP_NORF: case MAP_VARF: case MAP_DISP: + adrcode += cks->map; + break; + } + } + } + + /* adrcode must be returned! */ + return adrcode; +} + +/* extend posechannel keyingsets with rotation info (when KAG_CHAN_EXTEND is encountered) + * - iteration will stop when adrcode <= 0 is encountered, so we use that as escape + * - when we encounter KAG_CHAN_EXTEND as adrcode, start returning our own + */ +static short ks_getnextadrcode_pchanrot (bKS_AdrcodeGetter *kag) +{ + /* hardcoded adrcode channels used here only + * - length is keyed-channels + 1 (last item must be 0 to escape) + */ + static short quat_adrcodes[5] = {AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z, 0}; + static short eul_adrcodes[4] = {AC_EUL_X, AC_EUL_Y, AC_EUL_Z, 0}; + + /* useful variables */ + bKeyingSet *ks= (kag)? kag->ks : NULL; + bCommonKeySrc *cks= (kag) ? kag->cks : NULL; + short index, adrcode; + + /* error checking */ + if (ELEM3(NULL, kag, ks, cks)) return 0; + if (ks->chan_num <= 0) return 0; + + /* get index + * - if past the last item (kag->tot), return stuff from our static arrays + * - otherwise, just keep returning stuff from the keyingset (but check out for -1!) + */ + kag->index++; + if (kag->index < 0) + return 0; + + /* normal (static stuff) */ + if (kag->index < kag->tot) { + /* get adrcode, and return if not KAG_CHAN_EXTEND (i.e. point for start of iteration) */ + adrcode= ks->adrcodes[kag->index]; + + if (adrcode != KAG_CHAN_EXTEND) + return adrcode; + else + kag->tot= kag->index; + } + + /* based on current rotation-mode + * - index can be at most 5, if we are to prevent segfaults + */ + index= kag->index - kag->tot; + if ((index < 0) || (index > 5)) + return 0; + + if (cks->pchan && cks->pchan->rotmode) + return eul_adrcodes[index]; + else + return quat_adrcodes[index]; +} + +/* ------------- KeyingSet Defines ------------ */ +/* Note: these must all be named with the defks_* prefix, otherwise the template macro will not work! */ + +/* macro for defining keyingset contexts */ +#define KSC_TEMPLATE(ctx_name) {&defks_##ctx_name[0], NULL, sizeof(defks_##ctx_name)/sizeof(bKeyingSet)} + +/* --- */ + +/* check if option not available for deleting keys */ +static short incl_non_del_keys (bKeyingSet *ks, const char mode[]) +{ + /* as optimisation, assume that it is sufficient to check only first letter + * of mode (int comparison should be faster than string!) + */ + //if (strcmp(mode, "Delete")==0) + if (mode && mode[0]=='D') + return 0; + + return 1; +} + +/* Object KeyingSets ------ */ + +/* check if include shapekey entry */ +static short incl_v3d_ob_shapekey (bKeyingSet *ks, const char mode[]) +{ + Object *ob= (G.obedit)? (G.obedit) : (OBACT); + char *newname= NULL; + + if(ob==NULL) + return 0; + + /* not available for delete mode */ + if (strcmp(mode, "Delete")==0) + return 0; + + /* check if is geom object that can get shapekeys */ + switch (ob->type) { + /* geometry? */ + case OB_MESH: newname= "Mesh"; break; + case OB_CURVE: newname= "Curve"; break; + case OB_SURF: newname= "Surface"; break; + case OB_LATTICE: newname= "Lattice"; break; + + /* not geometry! */ + default: + return 0; + } + + /* if ks is shapekey entry (this could be callled for separator before too!) */ + if (ks->flag == -3) + sprintf(ks->name, newname); + + /* if it gets here, it's ok */ + return 1; +} + +/* array for object keyingset defines */ +bKeyingSet defks_v3d_object[] = +{ + /* include_cb, adrcode-getter, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Loc", ID_OB, 0, 3, {OB_LOC_X,OB_LOC_Y,OB_LOC_Z}}, + {NULL, "Rot", ID_OB, 0, 3, {OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + {NULL, "Scale", ID_OB, 0, 3, {OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "LocRot", ID_OB, 0, 6, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + + {NULL, "LocScale", ID_OB, 0, 6, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {NULL, "LocRotScale", ID_OB, 0, 9, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_ROT_X,OB_ROT_Y,OB_ROT_Z, + OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {NULL, "RotScale", ID_OB, 0, 6, + {OB_ROT_X,OB_ROT_Y,OB_ROT_Z, + OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {incl_non_del_keys, "%l", 0, -1, 0, {0}}, // separator + + {incl_non_del_keys, "VisualLoc", ID_OB, INSERTKEY_MATRIX, 3, {OB_LOC_X,OB_LOC_Y,OB_LOC_Z}}, + {incl_non_del_keys, "VisualRot", ID_OB, INSERTKEY_MATRIX, 3, {OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + + {incl_non_del_keys, "VisualLocRot", ID_OB, INSERTKEY_MATRIX, 6, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Layer", ID_OB, 0, 1, {OB_LAY}}, // icky option... + {NULL, "Available", ID_OB, -2, 0, {0}}, + + {incl_v3d_ob_shapekey, "%l%l", 0, -1, 0, {0}}, // separator (linked to shapekey entry) + {incl_v3d_ob_shapekey, "", ID_OB, -3, 0, {0}} +}; + +/* PoseChannel KeyingSets ------ */ + +/* array for posechannel keyingset defines */ +bKeyingSet defks_v3d_pchan[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Loc", ID_PO, 0, 3, {AC_LOC_X,AC_LOC_Y,AC_LOC_Z}}, + {NULL, "Rot", ID_PO, COMMONKEY_PCHANROT, 1, {KAG_CHAN_EXTEND}}, + {NULL, "Scale", ID_PO, 0, 3, {AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "LocRot", ID_PO, COMMONKEY_PCHANROT, 4, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, + KAG_CHAN_EXTEND}}, + + {NULL, "LocScale", ID_PO, 0, 6, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, + AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}}, + + {NULL, "LocRotScale", ID_PO, COMMONKEY_PCHANROT, 7, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z,AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z, + KAG_CHAN_EXTEND}}, + + {NULL, "RotScale", ID_PO, 0, 4, + {AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z, + KAG_CHAN_EXTEND}}, + + {incl_non_del_keys, "%l", 0, -1, 0, {0}}, // separator + + {incl_non_del_keys, "VisualLoc", ID_PO, INSERTKEY_MATRIX, 3, {AC_LOC_X,AC_LOC_Y,AC_LOC_Z}}, + {incl_non_del_keys, "VisualRot", ID_PO, INSERTKEY_MATRIX|COMMONKEY_PCHANROT, 1, {KAG_CHAN_EXTEND}}, + + {incl_non_del_keys, "VisualLocRot", ID_PO, INSERTKEY_MATRIX|COMMONKEY_PCHANROT, 4, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, KAG_CHAN_EXTEND}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_PO, -2, 0, {0}} +}; + +/* Material KeyingSets ------ */ + +/* array for material keyingset defines */ +bKeyingSet defks_buts_shading_mat[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "RGB", ID_MA, 0, 3, {MA_COL_R,MA_COL_G,MA_COL_B}}, + {NULL, "Alpha", ID_MA, 0, 1, {MA_ALPHA}}, + {NULL, "Halo Size", ID_MA, 0, 1, {MA_HASIZE}}, + {NULL, "Mode", ID_MA, 0, 1, {MA_MODE}}, // evil bitflags + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "All Color", ID_MA, 0, 18, + {MA_COL_R,MA_COL_G,MA_COL_B, + MA_ALPHA,MA_HASIZE, MA_MODE, + MA_SPEC_R,MA_SPEC_G,MA_SPEC_B, + MA_REF,MA_EMIT,MA_AMB,MA_SPEC,MA_HARD, + MA_MODE,MA_TRANSLU,MA_ADD}}, + + {NULL, "All Mirror", ID_MA, 0, 5, + {MA_RAYM,MA_FRESMIR,MA_FRESMIRI, + MA_FRESTRA,MA_FRESTRAI}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Ofs", ID_MA, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, + {NULL, "Size", ID_MA, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, + + {NULL, "All Mapping", ID_MA, COMMONKEY_ADDMAP, 14, + {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, + MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, + MAP_R,MAP_G,MAP_B,MAP_DVAR, + MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_MA, -2, 0, {0}} +}; + +/* World KeyingSets ------ */ + +/* array for world keyingset defines */ +bKeyingSet defks_buts_shading_wo[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Zenith RGB", ID_WO, 0, 3, {WO_ZEN_R,WO_ZEN_G,WO_ZEN_B}}, + {NULL, "Horizon RGB", ID_WO, 0, 3, {WO_HOR_R,WO_HOR_G,WO_HOR_B}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Mist", ID_WO, 0, 4, {WO_MISI,WO_MISTDI,WO_MISTSTA,WO_MISTHI}}, + {NULL, "Stars", ID_WO, 0, 5, {WO_STAR_R,WO_STAR_G,WO_STAR_B,WO_STARDIST,WO_STARSIZE}}, + + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Ofs", ID_WO, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, + {NULL, "Size", ID_WO, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, + + {NULL, "All Mapping", ID_WO, COMMONKEY_ADDMAP, 14, + {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, + MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, + MAP_R,MAP_G,MAP_B,MAP_DVAR, + MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_WO, -2, 0, {0}} +}; + +/* Lamp KeyingSets ------ */ + +/* array for lamp keyingset defines */ +bKeyingSet defks_buts_shading_la[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "RGB", ID_LA, 0, 3, {LA_COL_R,LA_COL_G,LA_COL_B}}, + {NULL, "Energy", ID_LA, 0, 1, {LA_ENERGY}}, + {NULL, "Spot Size", ID_LA, 0, 1, {LA_SPOTSI}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Ofs", ID_LA, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, + {NULL, "Size", ID_LA, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, + + {NULL, "All Mapping", ID_LA, COMMONKEY_ADDMAP, 14, + {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, + MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, + MAP_R,MAP_G,MAP_B,MAP_DVAR, + MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_LA, -2, 0, {0}} +}; + +/* Texture KeyingSets ------ */ + +/* array for texture keyingset defines */ +bKeyingSet defks_buts_shading_tex[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Clouds", ID_TE, 0, 5, + {TE_NSIZE,TE_NDEPTH,TE_NTYPE, + TE_MG_TYP,TE_N_BAS1}}, + + {NULL, "Marble", ID_TE, 0, 7, + {TE_NSIZE,TE_NDEPTH,TE_NTYPE, + TE_TURB,TE_MG_TYP,TE_N_BAS1,TE_N_BAS2}}, + + {NULL, "Stucci", ID_TE, 0, 5, + {TE_NSIZE,TE_NTYPE,TE_TURB, + TE_MG_TYP,TE_N_BAS1}}, + + {NULL, "Wood", ID_TE, 0, 6, + {TE_NSIZE,TE_NTYPE,TE_TURB, + TE_MG_TYP,TE_N_BAS1,TE_N_BAS2}}, + + {NULL, "Magic", ID_TE, 0, 2, {TE_NDEPTH,TE_TURB}}, + + {NULL, "Blend", ID_TE, 0, 1, {TE_MG_TYP}}, + + {NULL, "Musgrave", ID_TE, 0, 6, + {TE_MG_TYP,TE_MGH,TE_MG_LAC, + TE_MG_OCT,TE_MG_OFF,TE_MG_GAIN}}, + + {NULL, "Voronoi", ID_TE, 0, 9, + {TE_VNW1,TE_VNW2,TE_VNW3,TE_VNW4, + TE_VNMEXP,TE_VN_DISTM,TE_VN_COLT, + TE_ISCA,TE_NSIZE}}, + + {NULL, "Distorted Noise", ID_TE, 0, 4, + {TE_MG_OCT,TE_MG_OFF,TE_MG_GAIN,TE_DISTA}}, + + {NULL, "Color Filter", ID_TE, 0, 5, + {TE_COL_R,TE_COL_G,TE_COL_B,TE_BRIGHT,TE_CONTRA}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_TE, -2, 0, {0}} +}; + +/* Object Buttons KeyingSets ------ */ + +/* check if include particles entry */ +static short incl_buts_ob (bKeyingSet *ks, const char mode[]) +{ + Object *ob= OBACT; + /* only if object is mesh type */ + + if(ob==NULL) return 0; + return (ob->type == OB_MESH); +} + +/* array for texture keyingset defines */ +bKeyingSet defks_buts_object[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {incl_buts_ob, "Surface Damping", ID_OB, 0, 1, {OB_PD_SDAMP}}, + {incl_buts_ob, "Random Damping", ID_OB, 0, 1, {OB_PD_RDAMP}}, + {incl_buts_ob, "Permeability", ID_OB, 0, 1, {OB_PD_PERM}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Force Strength", ID_OB, 0, 1, {OB_PD_FSTR}}, + {NULL, "Force Falloff", ID_OB, 0, 1, {OB_PD_FFALL}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_OB, -2, 0, {0}} // this will include ob-transforms too! +}; + +/* Camera Buttons KeyingSets ------ */ + +/* check if include internal-renderer entry */ +static short incl_buts_cam1 (bKeyingSet *ks, const char mode[]) +{ + /* only if renderer is internal renderer */ + return (G.scene->r.renderer==R_INTERN); +} + +/* check if include external-renderer entry */ +static short incl_buts_cam2 (bKeyingSet *ks, const char mode[]) +{ + /* only if renderer is internal renderer */ + return (G.scene->r.renderer!=R_INTERN); +} + +/* array for camera keyingset defines */ +bKeyingSet defks_buts_cam[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Lens", ID_CA, 0, 1, {CAM_LENS}}, + {NULL, "Clipping", ID_CA, 0, 2, {CAM_STA,CAM_END}}, + {NULL, "Focal Distance", ID_CA, 0, 1, {CAM_YF_FDIST}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + + {incl_buts_cam2, "Aperture", ID_CA, 0, 1, {CAM_YF_APERT}}, + {incl_buts_cam1, "Viewplane Shift", ID_CA, 0, 2, {CAM_SHIFT_X,CAM_SHIFT_Y}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_CA, -2, 0, {0}} +}; + +/* --- */ + +/* Keying Context Defines - Must keep in sync with enumeration (eKS_Contexts) */ +bKeyingContext ks_contexts[] = +{ + KSC_TEMPLATE(v3d_object), + KSC_TEMPLATE(v3d_pchan), + + KSC_TEMPLATE(buts_shading_mat), + KSC_TEMPLATE(buts_shading_wo), + KSC_TEMPLATE(buts_shading_la), + KSC_TEMPLATE(buts_shading_tex), + + KSC_TEMPLATE(buts_object), + KSC_TEMPLATE(buts_cam) +}; + +/* Keying Context Enumeration - Must keep in sync with definitions*/ +typedef enum eKS_Contexts { + KSC_V3D_OBJECT = 0, + KSC_V3D_PCHAN, + + KSC_BUTS_MAT, + KSC_BUTS_WO, + KSC_BUTS_LA, + KSC_BUTS_TEX, + + KSC_BUTS_OB, + KSC_BUTS_CAM, + + /* make sure this last one remains untouched! */ + KSC_TOT_TYPES +} eKS_Contexts; + + +/* ---------------- KeyingSet Tools ------------------- */ + +/* helper for commonkey_context_get() - get keyingsets for 3d-view */ +static void commonkey_context_getv3d (ListBase *sources, bKeyingContext **ksc) +{ + Object *ob; + IpoCurve *icu; + + if ((OBACT) && (OBACT->flag & OB_POSEMODE)) { + bPoseChannel *pchan; + + /* pose-level */ + ob= OBACT; + *ksc= &ks_contexts[KSC_V3D_PCHAN]; + set_pose_keys(ob); /* sets pchan->flag to POSE_KEY if bone selected, and clears if not */ + + /* loop through posechannels */ + for (pchan=ob->pose->chanbase.first; pchan; pchan=pchan->next) { + if (pchan->flag & POSE_KEY) { + bCommonKeySrc *cks; + + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to, and action */ + cks->id= (ID *)ob; + cks->act= ob->action; + + /* set pchan */ + cks->pchan= pchan; + cks->actname= pchan->name; + } + } + } + else { + Base *base; + + /* object-level */ + *ksc= &ks_contexts[KSC_V3D_OBJECT]; + + /* loop through bases */ + for (base= FIRSTBASE; base; base= base->next) { + if (TESTBASELIB(base)) { + bCommonKeySrc *cks; + + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to */ + ob= base->object; + cks->id= (ID *)ob; + + /* when ob's keyframes are in an action, default to using 'Object' as achan name */ + if (ob->ipoflag & OB_ACTION_OB) + cks->actname= "Object"; + + /* set ipo-flags */ + // TODO: add checks for lib-linked data + if ((ob->ipo) || (ob->action)) { + if (ob->ipo) { + cks->ipo= ob->ipo; + } + else { + bActionChannel *achan; + + cks->act= ob->action; + achan= get_action_channel(ob->action, cks->actname); + + if (achan && achan->ipo) + cks->ipo= achan->ipo; + } + /* cks->ipo can be NULL while editing */ + if(cks->ipo) { + /* deselect all ipo-curves */ + for (icu= cks->ipo->curve.first; icu; icu= icu->next) { + icu->flag &= ~IPO_SELECT; + } + } + } + } + } + } +} + +/* helper for commonkey_context_get() - get keyingsets for buttons window */ +static void commonkey_context_getsbuts (ListBase *sources, bKeyingContext **ksc) +{ + bCommonKeySrc *cks; + +#if 0 // XXX dunno what's the future of this stuff... + /* check on tab-type */ + switch (G.buts->mainb) { + case CONTEXT_SHADING: /* ------------- Shading buttons ---------------- */ + /* subtabs include "Material", "Texture", "Lamp", "World"*/ + switch (G.buts->tab[CONTEXT_SHADING]) { + case TAB_SHADING_MAT: /* >------------- Material Tab -------------< */ + { + Material *ma= editnode_get_active_material(G.buts->lockpoin); + + if (ma) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)ma; + cks->ipo= ma->ipo; + cks->map= texchannel_to_adrcode(ma->texact); + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_MAT]; + return; + } + } + break; + case TAB_SHADING_WORLD: /* >------------- World Tab -------------< */ + { + World *wo= G.buts->lockpoin; + + if (wo) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)wo; + cks->ipo= wo->ipo; + cks->map= texchannel_to_adrcode(wo->texact); + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_WO]; + return; + } + } + break; + case TAB_SHADING_LAMP: /* >------------- Lamp Tab -------------< */ + { + Lamp *la= G.buts->lockpoin; + + if (la) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)la; + cks->ipo= la->ipo; + cks->map= texchannel_to_adrcode(la->texact); + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_LA]; + return; + } + } + break; + case TAB_SHADING_TEX: /* >------------- Texture Tab -------------< */ + { + Tex *tex= G.buts->lockpoin; + + if (tex) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)tex; + cks->ipo= tex->ipo; + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_TEX]; + return; + } + } + break; + } + break; + + case CONTEXT_OBJECT: /* ------------- Object buttons ---------------- */ + { + Object *ob= OBACT; + + if (ob) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to */ + cks->id= (ID *)ob; + cks->ipo= ob->ipo; + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_OB]; + return; + } + } + break; + + case CONTEXT_EDITING: /* ------------- Editing buttons ---------------- */ + { + Object *ob= OBACT; + + if ((ob) && (ob->type==OB_CAMERA) && (G.buts->lockpoin)) { /* >---------------- camera buttons ---------------< */ + Camera *ca= G.buts->lockpoin; + + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to */ + cks->id= (ID *)ca; + cks->ipo= ca->ipo; + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_CAM]; + return; + } + } + break; + } +#endif // XXX end of buttons stuff to port... + + /* if nothing happened... */ + *ksc= NULL; +} + + +/* get keyingsets for appropriate context */ +static void commonkey_context_get (ScrArea *sa, short mode, ListBase *sources, bKeyingContext **ksc) +{ + /* check view type */ + switch (sa->spacetype) { + /* 3d view - first one tested as most often used */ + case SPACE_VIEW3D: + { + commonkey_context_getv3d(sources, ksc); + } + break; + + /* buttons view */ + case SPACE_BUTS: + { + commonkey_context_getsbuts(sources, ksc); + } + break; + + /* spaces with their own methods */ + case SPACE_IPO: + //if (mode == COMMONKEY_MODE_INSERT) + // insertkey_editipo(); // XXX old calls... + return; + case SPACE_ACTION: + //if (mode == COMMONKEY_MODE_INSERT) + // insertkey_action(); // XXX old calls... + return; + + /* timeline view - keyframe buttons */ + case SPACE_TIME: + { + ScrArea *sab; + int bigarea= 0; + + /* try to find largest 3d-view available + * (mostly of the time, this is what when user will want this, + * as it's a standard feature in all other apps) + */ + //sab= find_biggest_area_of_type(SPACE_VIEW3D); + sab= NULL; // XXX for now... + if (sab) { + commonkey_context_getv3d(sources, ksc); + return; + } + + /* if not found, sab is now NULL, so perform own biggest area test */ + for (sa= G.curscreen->areabase.first; sa; sa= sa->next) { // XXX this has changed! + int area= sa->winx * sa->winy; + + if (sa->spacetype != SPACE_TIME) { + if ( (!sab) || (area > bigarea) ) { + sab= sa; + bigarea= area; + } + } + } + + /* use whichever largest area was found (it shouldn't be a time window) */ + if (sab) + commonkey_context_get(sab, mode, sources, ksc); + } + break; + } +} + +/* flush updates after all operations */ +static void commonkey_context_finish (ListBase *sources) +{ + /* check view type */ + switch (curarea->spacetype) { + /* 3d view - first one tested as most often used */ + case SPACE_VIEW3D: + { + /* either pose or object level */ + if (OBACT && (OBACT->pose)) { + Object *ob= OBACT; + + /* recalculate ipo handles, etc. */ + if (ob->action) + remake_action_ipos(ob->action); + + /* recalculate bone-paths on adding new keyframe? */ + // TODO: currently, there is no setting to turn this on/off globally + if (ob->pose->flag & POSE_RECALCPATHS) + pose_recalculate_paths(ob); + } + else { + bCommonKeySrc *cks; + + /* loop over bases (as seen in sources) */ + for (cks= sources->first; cks; cks= cks->next) { + Object *ob= (Object *)cks->id; + + /* simply set recalc flag */ + ob->recalc |= OB_RECALC_OB; + } + } + } + break; + } +} + +/* flush refreshes after undo */ +static void commonkey_context_refresh (void) +{ + /* check view type */ + switch (curarea->spacetype) { + /* 3d view - first one tested as most often used */ + case SPACE_VIEW3D: + { + /* do refreshes */ + DAG_scene_flush_update(G.scene, screen_view3d_layers(), 0); + + //allspace(REMAKEIPO, 0); + //allqueue(REDRAWVIEW3D, 0); + //allqueue(REDRAWMARKER, 0); + } + break; + + /* buttons window */ + case SPACE_BUTS: + { + //allspace(REMAKEIPO, 0); + //allqueue(REDRAWVIEW3D, 0); + //allqueue(REDRAWMARKER, 0); + } + break; + } +} + +/* --- */ + +/* Build menu-string of available keying-sets (allocates memory for string) + * NOTE: mode must not be longer than 64 chars + */ +static char *build_keyingsets_menu (bKeyingContext *ksc, const char mode[48]) +{ + DynStr *pupds= BLI_dynstr_new(); + bKeyingSet *ks; + char buf[64]; + char *str; + int i, n; + + /* add title first */ + BLI_snprintf(buf, 64, "%s Key %%t|", mode); + BLI_dynstr_append(pupds, buf); + + /* loop through keyingsets, adding them */ + for (ks=ksc->keyingsets, i=0, n=1; i < ksc->tot; ks++, i++, n++) { + /* check if keyingset can be used */ + if (ks->flag == -1) { + /* optional separator? */ + if (ks->include_cb) { + if (ks->include_cb(ks, mode)) { + BLI_snprintf( buf, 64, "%s%s", ks->name, ((n < ksc->tot)?"|":"") ); + BLI_dynstr_append(pupds, buf); + } + } + else { + BLI_snprintf( buf, 64, "%%l%s", ((n < ksc->tot)?"|":"") ); + BLI_dynstr_append(pupds, buf); + } + } + else if ( (ks->include_cb==NULL) || (ks->include_cb(ks, mode)) ) { + /* entry can be included */ + BLI_dynstr_append(pupds, ks->name); + + /* check if special "shapekey" entry */ + if (ks->flag == -3) + BLI_snprintf( buf, 64, "%%x0%s", ((n < ksc->tot)?"|":"") ); + else + BLI_snprintf( buf, 64, "%%x%d%s", n, ((n < ksc->tot)?"|":"") ); + BLI_dynstr_append(pupds, buf); + } + } + + /* convert to normal MEM_malloc'd string */ + str= BLI_dynstr_get_cstring(pupds); + BLI_dynstr_free(pupds); + + return str; +} + +/* Get the keying set that was chosen by the user from the menu */ +static bKeyingSet *get_keyingset_fromcontext (bKeyingContext *ksc, short index) +{ + /* check if index is valid */ + if (ELEM(NULL, ksc, ksc->keyingsets)) + return NULL; + if ((index < 1) || (index > ksc->tot)) + return NULL; + + /* index starts from 1, and should directly correspond to keyingset in array */ + return (bKeyingSet *)(ksc->keyingsets + (index - 1)); +} + +/* ---------------- Keyframe Management API -------------------- */ + +/* Display a menu for handling the insertion of keyframes based on the active view */ +// TODO: add back an option for repeating last keytype +void common_modifykey (short mode) +{ + ListBase dsources = {NULL, NULL}; + bKeyingContext *ksc= NULL; + bCommonKeySrc *cks; + bKeyingSet *ks = NULL; + char *menustr, buf[64]; + short menu_nr; + + /* check if mode is valid */ + if (ELEM(mode, COMMONKEY_MODE_INSERT, COMMONKEY_MODE_DELETE)==0) + return; + + /* delegate to other functions or get keyingsets to use + * - if the current area doesn't have its own handling, there will be data returned... + */ + commonkey_context_get(curarea, mode, &dsources, &ksc); + + /* check that there is data to operate on */ + if (ELEM(NULL, dsources.first, ksc)) { + BLI_freelistN(&dsources); + return; + } + + /* get menu and process it */ + if (mode == COMMONKEY_MODE_DELETE) + menustr= build_keyingsets_menu(ksc, "Delete"); + else + menustr= build_keyingsets_menu(ksc, "Insert"); + menu_nr= pupmenu(menustr); + if (menustr) MEM_freeN(menustr); + + /* no item selected or shapekey entry? */ + if (menu_nr < 1) { + /* free temp sources */ + BLI_freelistN(&dsources); + + /* check if insert new shapekey */ + if ((menu_nr == 0) && (mode == COMMONKEY_MODE_INSERT)) + insert_shapekey(OBACT); + else + ksc->lastused= NULL; + + return; + } + else { + /* try to get keyingset */ + ks= get_keyingset_fromcontext(ksc, menu_nr); + + if (ks == NULL) { + BLI_freelistN(&dsources); + return; + } + } + + /* loop over each destination, applying the keying set */ + for (cks= dsources.first; cks; cks= cks->next) { + short success= 0; + + /* special hacks for 'available' option */ + if (ks->flag == -2) { + IpoCurve *icu= NULL, *icn= NULL; + + /* get first IPO-curve */ + if (cks->act && cks->actname) { + bActionChannel *achan= get_action_channel(cks->act, cks->actname); + + // FIXME: what about constraint channels? + if (achan && achan->ipo) + icu= achan->ipo->curve.first; + } + else if(cks->ipo) + icu= cks->ipo->curve.first; + + /* we get adrcodes directly from IPO curves (see method below...) */ + for (; icu; icu= icn) { + short flag; + + /* get next ipo-curve in case current is deleted */ + icn= icu->next; + + /* insert mode or delete mode */ + if (mode == COMMONKEY_MODE_DELETE) { + /* local flags only add on to global flags */ + flag = 0; + + /* delete keyframe */ + success += deletekey(cks->id, ks->blocktype, cks->actname, cks->constname, icu->adrcode, flag); + } + else { + /* local flags only add on to global flags */ + flag = ks->flag; + if (IS_AUTOKEY_FLAG(AUTOMATKEY)) flag |= INSERTKEY_MATRIX; + if (IS_AUTOKEY_FLAG(INSERTNEEDED)) flag |= INSERTKEY_NEEDED; + // if (IS_AUTOKEY_MODE(EDITKEYS)) flag |= INSERTKEY_REPLACE; + + /* insert keyframe */ + success += insertkey(cks->id, ks->blocktype, cks->actname, cks->constname, icu->adrcode, flag); + } + } + } + else { + bKS_AdrcodeGetter kag; + short (*get_next_adrcode)(bKS_AdrcodeGetter *); + int adrcode; + + /* initialise keyingset channel iterator */ + ks_adrcodegetter_init(&kag, ks, cks); + + /* get iterator - only one can be in use at a time... the flags should be mutually exclusive in this regard */ + if (ks->flag & COMMONKEY_PCHANROT) + get_next_adrcode= ks_getnextadrcode_pchanrot; + else if (ks->flag & COMMONKEY_ADDMAP) + get_next_adrcode= ks_getnextadrcode_addmap; + else + get_next_adrcode= ks_getnextadrcode_default; + + /* loop over channels available in keyingset */ + for (adrcode= get_next_adrcode(&kag); adrcode > 0; adrcode= get_next_adrcode(&kag)) { + short flag; + + /* insert mode or delete mode */ + if (mode == COMMONKEY_MODE_DELETE) { + /* local flags only add on to global flags */ + flag = 0; + //flag &= ~COMMONKEY_MODES; + + /* delete keyframe */ + success += deletekey(cks->id, ks->blocktype, cks->actname, cks->constname, adrcode, flag); + } + else { + /* local flags only add on to global flags */ + flag = ks->flag; + if (IS_AUTOKEY_FLAG(AUTOMATKEY)) flag |= INSERTKEY_MATRIX; + if (IS_AUTOKEY_FLAG(INSERTNEEDED)) flag |= INSERTKEY_NEEDED; + // if (IS_AUTOKEY_MODE(EDITKEYS)) flag |= INSERTKEY_REPLACE; + flag &= ~COMMONKEY_MODES; + + /* insert keyframe */ + success += insertkey(cks->id, ks->blocktype, cks->actname, cks->constname, adrcode, flag); + } + } + } + + /* special handling for some key-sources */ + if (success) { + /* set pose recalc-paths flag */ + if (cks->pchan) { + Object *ob= (Object *)cks->id; + bPoseChannel *pchan= cks->pchan; + + /* set flag to trigger path recalc */ + if (pchan->path) + ob->pose->flag |= POSE_RECALCPATHS; + + /* clear unkeyed flag (it doesn't matter if it's set or not) */ + if (pchan->bone) + pchan->bone->flag &= ~BONE_UNKEYED; + } + } + } + + /* apply post-keying flushes for this data sources */ + commonkey_context_finish(&dsources); + ksc->lastused= ks; + + /* free temp data */ + BLI_freelistN(&dsources); + + /* undo pushes */ + if (mode == COMMONKEY_MODE_DELETE) + BLI_snprintf(buf, 64, "Delete %s Key", ks->name); + else + BLI_snprintf(buf, 64, "Insert %s Key", ks->name); + BIF_undo_push(buf); + + /* queue updates for contexts */ + commonkey_context_refresh(); +} + +/* ---- */ + +/* used to insert keyframes from any view */ +void common_insertkey (void) +{ + common_modifykey(COMMONKEY_MODE_INSERT); +} + +/* used to insert keyframes from any view */ +void common_deletekey (void) +{ + common_modifykey(COMMONKEY_MODE_DELETE); +} + +/* ************************************************** */ +/* KEYFRAME DETECTION */ + +/* --------------- API/Per-Datablock Handling ------------------- */ + +/* Checks whether an IPO-block has a keyframe for a given frame + * Since we're only concerned whether a keyframe exists, we can simply loop until a match is found... + */ +short ipo_frame_has_keyframe (Ipo *ipo, float frame, short filter) +{ + IpoCurve *icu; + + /* can only find if there is data */ + if (ipo == NULL) + return 0; + + /* if only check non-muted, check if muted */ + if ((filter & ANIMFILTER_MUTED) || (ipo->muteipo)) + return 0; + + /* loop over IPO-curves, using binary-search to try to find matches + * - this assumes that keyframes are only beztriples + */ + for (icu= ipo->curve.first; icu; icu= icu->next) { + /* only check if there are keyframes (currently only of type BezTriple) */ + if (icu->bezt) { + /* we either include all regardless of muting, or only non-muted */ + if ((filter & ANIMFILTER_MUTED) || (icu->flag & IPO_MUTE)==0) { + short replace = -1; + int i = binarysearch_bezt_index(icu->bezt, frame, icu->totvert, &replace); + + /* binarysearch_bezt_index will set replace to be 0 or 1 + * - obviously, 1 represents a match + */ + if (replace) { + /* sanity check: 'i' may in rare cases exceed arraylen */ + if ((i >= 0) && (i < icu->totvert)) + return 1; + } + } + } + } + + /* nothing found */ + return 0; +} + +/* Checks whether an action-block has a keyframe for a given frame + * Since we're only concerned whether a keyframe exists, we can simply loop until a match is found... + */ +short action_frame_has_keyframe (bAction *act, float frame, short filter) +{ + bActionChannel *achan; + + /* error checking */ + if (act == NULL) + return 0; + + /* check thorugh action-channels for match */ + for (achan= act->chanbase.first; achan; achan= achan->next) { + /* we either include all regardless of muting, or only non-muted + * - here we include 'hidden' channels in the muted definition + */ + if ((filter & ANIMFILTER_MUTED) || (achan->flag & ACHAN_HIDDEN)==0) { + if (ipo_frame_has_keyframe(achan->ipo, frame, filter)) + return 1; + } + } + + /* nothing found */ + return 0; +} + +/* Checks whether an Object has a keyframe for a given frame */ +short object_frame_has_keyframe (Object *ob, float frame, short filter) +{ + /* error checking */ + if (ob == NULL) + return 0; + + /* check for an action - actions take priority over normal IPO's */ + if (ob->action) { + float aframe; + + /* apply nla-action scaling if needed */ + if ((ob->nlaflag & OB_NLA_OVERRIDE) && (ob->nlastrips.first)) + aframe= get_action_frame(ob, frame); + else + aframe= frame; + + /* priority check here goes to pose-channel checks (for armatures) */ + if ((ob->pose) && (ob->flag & OB_POSEMODE)) { + /* only relevant check here is to only show active... */ + if (filter & ANIMFILTER_ACTIVE) { + bPoseChannel *pchan= get_active_posechannel(ob); + bActionChannel *achan= (pchan) ? get_action_channel(ob->action, pchan->name) : NULL; + + /* since we're only interested in whether the selected one has any keyframes... */ + return (achan && ipo_frame_has_keyframe(achan->ipo, aframe, filter)); + } + } + + /* for everything else, just use the standard test (only return if success) */ + if (action_frame_has_keyframe(ob->action, aframe, filter)) + return 1; + } + else if (ob->ipo) { + /* only return if success */ + if (ipo_frame_has_keyframe(ob->ipo, frame, filter)) + return 1; + } + + /* try shapekey keyframes (if available, and allowed by filter) */ + if ( !(filter & ANIMFILTER_LOCAL) && !(filter & ANIMFILTER_NOSKEY) ) { + Key *key= ob_get_key(ob); + + /* shapekeys can have keyframes ('Relative Shape Keys') + * or depend on time (old 'Absolute Shape Keys') + */ + + /* 1. test for relative (with keyframes) */ + if (id_frame_has_keyframe((ID *)key, frame, filter)) + return 1; + + /* 2. test for time */ + // TODO... yet to be implemented (this feature may evolve before then anyway) + } + + /* try materials */ + if ( !(filter & ANIMFILTER_LOCAL) && !(filter & ANIMFILTER_NOMAT) ) { + /* if only active, then we can skip a lot of looping */ + if (filter & ANIMFILTER_ACTIVE) { + Material *ma= give_current_material(ob, (ob->actcol + 1)); + + /* we only retrieve the active material... */ + if (id_frame_has_keyframe((ID *)ma, frame, filter)) + return 1; + } + else { + int a; + + /* loop over materials */ + for (a=0; atotcol; a++) { + Material *ma= give_current_material(ob, a+1); + + if (id_frame_has_keyframe((ID *)ma, frame, filter)) + return 1; + } + } + } + + /* nothing found */ + return 0; +} + +/* --------------- API ------------------- */ + +/* Checks whether a keyframe exists for the given ID-block one the given frame */ +short id_frame_has_keyframe (ID *id, float frame, short filter) +{ + /* error checking */ + if (id == NULL) + return 0; + + /* check for a valid id-type */ + switch (GS(id->name)) { + /* animation data-types */ + case ID_IP: /* ipo */ + return ipo_frame_has_keyframe((Ipo *)id, frame, filter); + case ID_AC: /* action */ + return action_frame_has_keyframe((bAction *)id, frame, filter); + + case ID_OB: /* object */ + return object_frame_has_keyframe((Object *)id, frame, filter); + + case ID_MA: /* material */ + { + Material *ma= (Material *)id; + + /* currently, material's only have an ipo-block */ + return ipo_frame_has_keyframe(ma->ipo, frame, filter); + } + break; + + case ID_KE: /* shapekey */ + { + Key *key= (Key *)id; + + /* currently, shapekey's only have an ipo-block */ + return ipo_frame_has_keyframe(key->ipo, frame, filter); + } + break; + } + + /* no keyframe found */ + return 0; +} + +/* ************************************************** */ + +#endif // XXX reenable this file again later... diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c new file mode 100644 index 00000000000..a428bc7559a --- /dev/null +++ b/source/blender/editors/animation/anim_markers.c @@ -0,0 +1,869 @@ +/** + * $Id: + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view2d_types.h" +#include "DNA_userdef_types.h" +#include "DNA_windowmanager_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "BLI_blenlib.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_utildefines.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "UI_interface.h" +#include "UI_interface_icons.h" +#include "UI_view2d.h" +#include "UI_resources.h" +#include "UI_text.h" + +#include "ED_markers.h" +#include "ED_screen.h" +#include "ED_types.h" +#include "ED_util.h" + +/* ************* Marker API **************** */ + +static ListBase *context_get_markers(const bContext *C) +{ + +#if 0 + /* XXX get them from pose */ + if ((slink->spacetype == SPACE_ACTION) && (saction->flag & SACTION_POSEMARKERS_MOVE)) { + if (saction->action) + markers= &saction->action->markers; + else + markers= NULL; + } + else +#endif + + return &CTX_data_scene(C)->markers; +} + +/* ************* Marker Drawing ************ */ + +/* XXX */ +extern void ui_rasterpos_safe(float x, float y, float aspect); + +/* function to draw markers */ +static void draw_marker(View2D *v2d, TimeMarker *marker, int cfra, int flag) +{ + float xpos, ypixels, xscale, yscale; + int icon_id= 0; + + xpos = marker->frame; + /* no time correction for framelen! space is drawn with old values */ + + ypixels= v2d->mask.ymax-v2d->mask.ymin; + UI_view2d_getscale(v2d, &xscale, &yscale); + + glScalef(1.0/xscale, 1.0, 1.0); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* vertical line */ + if (flag & DRAW_MARKERS_LINES) { + setlinestyle(3); + if(marker->flag & SELECT) + glColor4ub(255,255,255, 96); + else + glColor4ub(0,0,0, 96); + + glBegin(GL_LINES); + glVertex2f((xpos*xscale)+0.5, 12); + glVertex2f((xpos*xscale)+0.5, 34*yscale); /* a bit lazy but we know it cant be greater then 34 strips high*/ + glEnd(); + setlinestyle(0); + } + + /* 5 px to offset icon to align properly, space / pixels corrects for zoom */ + if (flag & DRAW_MARKERS_LOCAL) { + icon_id= (marker->flag & ACTIVE) ? ICON_PMARKER_ACT : + (marker->flag & SELECT) ? ICON_PMARKER_SEL : + ICON_PMARKER; + } + else { + icon_id= (marker->flag & SELECT) ? ICON_MARKER_HLT : + ICON_MARKER; + } + + UI_icon_draw(xpos*xscale-5.0, 16.0, icon_id); + + glBlendFunc(GL_ONE, GL_ZERO); + glDisable(GL_BLEND); + + /* and the marker name too, shifted slightly to the top-right */ + if(marker->name && marker->name[0]) { + if(marker->flag & SELECT) { + UI_ThemeColor(TH_TEXT_HI); + ui_rasterpos_safe(xpos*xscale+4.0, (ypixels<=39.0)?(ypixels-10.0):29.0, 1.0); + } + else { + UI_ThemeColor(TH_TEXT); + if((marker->frame <= cfra) && (marker->frame+5 > cfra)) + ui_rasterpos_safe(xpos*xscale+4.0, (ypixels<=39.0)?(ypixels-10.0):29.0, 1.0); + else + ui_rasterpos_safe(xpos*xscale+4.0, 17.0, 1.0); + } + UI_DrawString(G.font, marker->name, 0); + } + glScalef(xscale, 1.0, 1.0); +} + +/* Draw Scene-Markers in time window */ +void draw_markers_time(const bContext *C, int flag) +{ + ListBase *markers= context_get_markers(C); + View2D *v2d= UI_view2d_fromcontext(C); + TimeMarker *marker; + + /* unselected markers are drawn at the first time */ + for (marker= markers->first; marker; marker= marker->next) { + if (!(marker->flag & SELECT)) draw_marker(v2d, marker, CTX_data_scene(C)->r.cfra, flag); + } + + /* selected markers are drawn later */ + for (marker= markers->first; marker; marker= marker->next) { + if (marker->flag & SELECT) draw_marker(v2d, marker, CTX_data_scene(C)->r.cfra, flag); + } +} + + + +/* ************************** add markers *************************** */ + +/* add TimeMarker at curent frame */ +static int ed_marker_add(bContext *C, wmOperator *op) +{ + ListBase *markers= context_get_markers(C); + TimeMarker *marker; + int frame= CTX_data_scene(C)->r.cfra; + + /* two markers can't be at the same place */ + for(marker= markers->first; marker; marker= marker->next) + if(marker->frame == frame) + return OPERATOR_CANCELLED; + + /* deselect all */ + for(marker= markers->first; marker; marker= marker->next) + marker->flag &= ~SELECT; + + marker = MEM_callocN(sizeof(TimeMarker), "TimeMarker"); + marker->flag= SELECT; + marker->frame= frame; + sprintf(marker->name, "Frame %d", frame); // XXX - temp code only + BLI_addtail(markers, marker); + + WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); + //BIF_undo_push("Add Marker"); + + return OPERATOR_FINISHED; +} + +static void ED_MARKER_OT_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add Time Marker"; + ot->idname= "ED_MARKER_OT_add"; + + /* api callbacks */ + ot->exec= ed_marker_add; + ot->poll= ED_operator_areaactive; + +} + +/* ************************** transform markers *************************** */ + + +/* operator state vars used: + frs: delta movement + +functions: + + init() check selection, add customdata with old values and some lookups + + apply() do the actual movement + + exit() cleanup, send notifier + + cancel() to escpae from modal + +callbacks: + + exec() calls init, apply, exit + + invoke() calls init, adds modal handler + + modal() accept modal events while doing it, ends with apply and exit, or cancel + +*/ + +typedef struct MarkerMove { + SpaceLink *slink; + ListBase *markers; + int event_type; /* store invoke-event, to verify */ + int *oldframe, evtx, firstx; +} MarkerMove; + +/* copy selection to temp buffer */ +/* return 0 if not OK */ +static int ed_marker_move_init(bContext *C, wmOperator *op) +{ + ListBase *markers= context_get_markers(C); + MarkerMove *mm; + TimeMarker *marker; + int totmark=0; + int a; + + for (marker= markers->first; marker; marker= marker->next) + if (marker->flag & SELECT) totmark++; + + if (totmark==0) return 0; + + op->customdata= mm= MEM_callocN(sizeof(MarkerMove), "Markermove"); + mm->slink= CTX_wm_space_data(C); + mm->markers= markers; + mm->oldframe= MEM_callocN(totmark*sizeof(int), "MarkerMove oldframe"); + + for (a=0, marker= markers->first; marker; marker= marker->next) { + if (marker->flag & SELECT) { + mm->oldframe[a]= marker->frame; + a++; + } + } + + return 1; +} + +/* free stuff */ +static void ed_marker_move_exit(bContext *C, wmOperator *op) +{ + MarkerMove *mm= op->customdata; + + MEM_freeN(mm->oldframe); + MEM_freeN(op->customdata); + op->customdata= NULL; +} + +static int ed_marker_move_invoke(bContext *C, wmOperator *op, wmEvent *evt) +{ + if(ed_marker_move_init(C, op)) { + MarkerMove *mm= op->customdata; + + mm->evtx= evt->x; + mm->firstx= evt->x; + mm->event_type= evt->type; + + /* add temp handler */ + WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op); + + /* reset frs delta */ + RNA_int_set(op->ptr, "frs", 0); + + return OPERATOR_RUNNING_MODAL; + } + + return OPERATOR_CANCELLED; +} + +/* note, init has to be called succesfully */ +static void ed_marker_move_apply(bContext *C, wmOperator *op) +{ + MarkerMove *mm= op->customdata; + TimeMarker *marker; + int a, offs; + + offs= RNA_int_get(op->ptr, "frs"); + for (a=0, marker= mm->markers->first; marker; marker= marker->next) { + if (marker->flag & SELECT) { + marker->frame= mm->oldframe[a] + offs; + a++; + } + } +} + +/* only for modal */ +static void ed_marker_move_cancel(bContext *C, wmOperator *op) +{ + + RNA_int_set(op->ptr, "frs", 0); + ed_marker_move_apply(C, op); + ed_marker_move_exit(C, op); + + WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); +} + + +/* for tweak handlers, check configuration for how to interpret events */ +int WM_modal_tweak_check(wmEvent *evt, int tweak_event) +{ + /* user preset?? dunno... */ + int tweak_modal= 1; + + switch(tweak_event) { + case EVT_TWEAK_L: + case EVT_TWEAK_M: + case EVT_TWEAK_R: + if(evt->val==tweak_modal) + return 1; + default: + /* this case is when modal callcback didnt get started with a tweak */ + if(evt->val) + return 1; + } + return 0; +} + +static int ed_marker_move_modal(bContext *C, wmOperator *op, wmEvent *evt) +{ + MarkerMove *mm= op->customdata; + View2D *v2d= UI_view2d_fromcontext(C); + TimeMarker *marker, *selmarker=NULL; + float dx, fac; + char str[256]; + + switch(evt->type) { + case ESCKEY: + ed_marker_move_cancel(C, op); + return OPERATOR_CANCELLED; + + case LEFTMOUSE: + case MIDDLEMOUSE: + case RIGHTMOUSE: + if(WM_modal_tweak_check(evt, mm->event_type)) { + ed_marker_move_exit(C, op); + WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); + return OPERATOR_FINISHED; + } + + break; + case MOUSEMOVE: + + dx= v2d->mask.xmax-v2d->mask.xmin; + dx= (v2d->cur.xmax-v2d->cur.xmin)/dx; + + if (evt->x != mm->evtx) { /* XXX maybe init for firsttime */ + int a, offs, totmark=0; + + mm->evtx= evt->x; + + fac= ((float)(evt->x - mm->firstx)*dx); + + if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) + apply_keyb_grid(evt->shift, evt->ctrl, &fac, 0.0, FPS, 0.1*FPS, 0); + else + apply_keyb_grid(evt->shift, evt->ctrl, &fac, 0.0, 1.0, 0.1, U.flag & USER_AUTOGRABGRID); + + offs= (int)fac; + RNA_int_set(op->ptr, "frs", offs); + ed_marker_move_apply(C, op); + + /* cruft below is for header print */ + for (a=0, marker= mm->markers->first; marker; marker= marker->next) { + if (marker->flag & SELECT) { + selmarker= marker; + a++; totmark++; + } + } + + if (totmark==1) { + /* we print current marker value */ + if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) { + SpaceTime *stime= (SpaceTime *)mm->slink; + if (stime->flag & TIME_DRAWFRAMES) + sprintf(str, "Marker %d offset %d", selmarker->frame, offs); + else + sprintf(str, "Marker %.2f offset %.2f", FRA2TIME(selmarker->frame), FRA2TIME(offs)); + } + else if (mm->slink->spacetype == SPACE_ACTION) { +#if 0 +XXX if (saction->flag & SACTION_DRAWTIME) + sprintf(str, "Marker %.2f offset %.2f", FRA2TIME(selmarker->frame), FRA2TIME(offs)); + else + sprintf(str, "Marker %.2f offset %.2f", (double)(selmarker->frame), (double)(offs)); +#endif + } + else { + sprintf(str, "Marker %.2f offset %.2f", (double)(selmarker->frame), (double)(offs)); + } + } + else { + /* we only print the offset */ + if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) { + SpaceTime *stime= (SpaceTime *)mm->slink; + if (stime->flag & TIME_DRAWFRAMES) + sprintf(str, "Marker offset %d ", offs); + else + sprintf(str, "Marker offset %.2f ", FRA2TIME(offs)); + } +#if 0 +XXX else if (mm->slink->spacetype == SPACE_ACTION) { + if (saction->flag & SACTION_DRAWTIME) + sprintf(str, "Marker offset %.2f ", FRA2TIME(offs)); + else + sprintf(str, "Marker offset %.2f ", (double)(offs)); + } +#endif + else { + sprintf(str, "Marker offset %.2f ", (double)(offs)); + } + } + + WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); + // headerprint(str); XXX + } + } + + return OPERATOR_RUNNING_MODAL; +} + +static int ed_marker_move_exec(bContext *C, wmOperator *op) +{ + if(ed_marker_move_init(C, op)) { + ed_marker_move_apply(C, op); + ed_marker_move_exit(C, op); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +static void ED_MARKER_OT_move(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Move Time Marker"; + ot->idname= "ED_MARKER_OT_move"; + + /* api callbacks */ + ot->exec= ed_marker_move_exec; + ot->invoke= ed_marker_move_invoke; + ot->modal= ed_marker_move_modal; + ot->poll= ED_operator_areaactive; + + /* rna storage */ + RNA_def_property(ot->srna, "frs", PROP_INT, PROP_NONE); +} + +/* ************************** duplicate markers *************************** */ + +/* operator state vars used: + frs: delta movement + +functions: + + apply() do the actual duplicate + +callbacks: + + exec() calls apply, move_exec + + invoke() calls apply, move_invoke + + modal() uses move_modal + +*/ + + +/* duplicate selected TimeMarkers */ +static void ed_marker_duplicate_apply(bContext *C, wmOperator *op) +{ + ListBase *markers= context_get_markers(C); + TimeMarker *marker, *newmarker; + + /* go through the list of markers, duplicate selected markers and add duplicated copies + * to the begining of the list (unselect original markers) */ + for(marker= markers->first; marker; marker= marker->next) { + if(marker->flag & SELECT){ + /* unselect selected marker */ + marker->flag &= ~SELECT; + /* create and set up new marker */ + newmarker = MEM_callocN(sizeof(TimeMarker), "TimeMarker"); + newmarker->flag= SELECT; + newmarker->frame= marker->frame; + BLI_strncpy(newmarker->name, marker->name, sizeof(marker->name)); + /* new marker is added to the begining of list */ + BLI_addhead(markers, newmarker); + } + } +} + +static int ed_marker_duplicate_exec(bContext *C, wmOperator *op) +{ + ed_marker_duplicate_apply(C, op); + ed_marker_move_exec(C, op); /* assumes frs delta set */ + + return OPERATOR_FINISHED; + +} + +static int ed_marker_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *evt) +{ + ed_marker_duplicate_apply(C, op); + return ed_marker_move_invoke(C, op, evt); +} + +static void ED_MARKER_OT_duplicate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Duplicate Time Marker"; + ot->idname= "ED_MARKER_OT_duplicate"; + + /* api callbacks */ + ot->exec= ed_marker_duplicate_exec; + ot->invoke= ed_marker_duplicate_invoke; + ot->modal= ed_marker_move_modal; + ot->poll= ED_operator_areaactive; + + /* rna storage */ + RNA_def_property(ot->srna, "frs", PROP_INT, PROP_NONE); +} + +/* ************************** selection ************************************/ + +/* select/deselect TimeMarker at current frame */ +static void select_timeline_marker_frame(ListBase *markers, int frame, unsigned char shift) +{ + TimeMarker *marker; + int select=0; + + for(marker= markers->first; marker; marker= marker->next) { + /* if Shift is not set, then deselect Markers */ + if(!shift) marker->flag &= ~SELECT; + /* this way a not-shift select will allways give 1 selected marker */ + if((marker->frame == frame) && (!select)) { + if(marker->flag & SELECT) + marker->flag &= ~SELECT; + else + marker->flag |= SELECT; + select = 1; + } + } +} + +static int find_nearest_marker_time(ListBase *markers, float dx) +{ + TimeMarker *marker, *nearest= NULL; + float dist, min_dist= 1000000; + + for(marker= markers->first; marker; marker= marker->next) { + dist = ABS((float)marker->frame - dx); + if(dist < min_dist){ + min_dist= dist; + nearest= marker; + } + } + + if(nearest) return nearest->frame; + else return (int)floor(dx); /* hrmf? */ +} + + +static int ed_marker_select(bContext *C, wmEvent *evt, int extend) +{ + ListBase *markers= context_get_markers(C); + View2D *v2d= UI_view2d_fromcontext(C); + float viewx; + int x, y, cfra; + + x= evt->x - CTX_wm_region(C)->winrct.xmin; + y= evt->y - CTX_wm_region(C)->winrct.ymin; + + UI_view2d_region_to_view(v2d, x, y, &viewx, NULL); + + cfra= find_nearest_marker_time(markers, viewx); + + if (extend) + select_timeline_marker_frame(markers, cfra, 1); + else + select_timeline_marker_frame(markers, cfra, 0); + + WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); + + return OPERATOR_PASS_THROUGH; +} + +static int ed_marker_select_extend_invoke(bContext *C, wmOperator *op, wmEvent *evt) +{ + return ed_marker_select(C, evt, 1); +} + +static int ed_marker_select_invoke(bContext *C, wmOperator *op, wmEvent *evt) +{ + return ed_marker_select(C, evt, 0); +} + +static void ED_MARKER_OT_mouseselect(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Time Marker"; + ot->idname= "ED_MARKER_OT_mouseselect"; + + /* api callbacks */ + ot->invoke= ed_marker_select_invoke; + ot->poll= ED_operator_areaactive; +} + +static void ED_MARKER_OT_mouseselect_extend(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Extend Select Time Marker"; + ot->idname= "ED_MARKER_OT_mouseselect_extend"; + + /* api callbacks */ + ot->invoke= ed_marker_select_extend_invoke; + ot->poll= ED_operator_areaactive; +} + +/* *************************** border select markers **************** */ + +/* operator state vars used: (added by default WM callbacks) + xmin, ymin + xmax, ymax + +customdata: the wmGesture pointer, with subwindow + +callbacks: + + exec() has to be filled in by user + + invoke() default WM function + adds modal handler + + modal() default WM function + accept modal events while doing it, calls exec(), handles ESC and border drawing + + poll() has to be filled in by user for context +*/ + +static int ed_marker_border_select_exec(bContext *C, wmOperator *op) +{ + View2D *v2d= UI_view2d_fromcontext(C); + ListBase *markers= context_get_markers(C); + TimeMarker *marker; + float xminf, xmaxf, yminf, ymaxf; + int event_type= RNA_int_get(op->ptr, "event_type"); + int xmin= RNA_int_get(op->ptr, "xmin"); + int xmax= RNA_int_get(op->ptr, "xmax"); + int ymin= RNA_int_get(op->ptr, "ymin"); + int ymax= RNA_int_get(op->ptr, "ymax"); + + UI_view2d_region_to_view(v2d, xmin, ymin, &xminf, &yminf); + UI_view2d_region_to_view(v2d, xmax, ymax, &xmaxf, &ymaxf); + + /* XXX disputable */ + if(yminf > 30.0f || ymaxf < 0.0f) + return 0; + + /* XXX marker context */ + for(marker= markers->first; marker; marker= marker->next) { + if ((marker->frame > xminf) && (marker->frame <= xmaxf)) { + switch (event_type) { + case LEFTMOUSE: + if ((marker->flag & SELECT) == 0) + marker->flag |= SELECT; + break; + case RIGHTMOUSE: + if (marker->flag & SELECT) + marker->flag &= ~SELECT; + break; + } + } + } + + WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); + + return 1; +} + +static void ED_MARKER_OT_border_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Marker Border select"; + ot->idname= "ED_MARKER_OT_border_select"; + + /* api callbacks */ + ot->exec= ed_marker_border_select_exec; + ot->invoke= WM_border_select_invoke; + ot->modal= WM_border_select_modal; + + ot->poll= ED_operator_areaactive; + + /* rna */ + RNA_def_property(ot->srna, "event_type", PROP_INT, PROP_NONE); + RNA_def_property(ot->srna, "xmin", PROP_INT, PROP_NONE); + RNA_def_property(ot->srna, "xmax", PROP_INT, PROP_NONE); + RNA_def_property(ot->srna, "ymin", PROP_INT, PROP_NONE); + RNA_def_property(ot->srna, "ymax", PROP_INT, PROP_NONE); + +} + +/* *********************** (de)select all ***************** */ + +static int ed_marker_select_all_exec(bContext *C, wmOperator *op) +{ + ListBase *markers= context_get_markers(C); + TimeMarker *marker; + int select= RNA_int_get(op->ptr, "select_type"); + + if(RNA_int_get(op->ptr, "select_swap")) { + for(marker= markers->first; marker; marker= marker->next) { + if(marker->flag & SELECT) + break; + } + if(marker) + select= 0; + else + select= 1; + } + + for(marker= markers->first; marker; marker= marker->next) { + if(select) + marker->flag |= SELECT; + else + marker->flag &= ~SELECT; + } + + WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); + + return OPERATOR_FINISHED; +} + +static int ed_marker_select_all_invoke(bContext *C, wmOperator *op, wmEvent *evt) +{ + RNA_int_set(op->ptr, "select_swap", 1); + + return ed_marker_select_all_exec(C, op); +} + +static void ED_MARKER_OT_select_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "(De)select all markers"; + ot->idname= "ED_MARKER_OT_select_all"; + + /* api callbacks */ + ot->exec= ed_marker_select_all_exec; + ot->invoke= ed_marker_select_all_invoke; + ot->poll= ED_operator_areaactive; + + /* rna */ + RNA_def_property(ot->srna, "select_swap", PROP_INT, PROP_NONE); + RNA_def_property(ot->srna, "select_type", PROP_INT, PROP_NONE); + +} + +/* ******************************* remove marker ***************** */ + +/* remove selected TimeMarkers */ +static int ed_marker_delete_exec(bContext *C, wmOperator *op) +{ + ListBase *markers= context_get_markers(C); + TimeMarker *marker, *nmarker; + short changed= 0; + + for(marker= markers->first; marker; marker= nmarker) { + nmarker= marker->next; + if(marker->flag & SELECT) { + BLI_freelinkN(markers, marker); + changed= 1; + } + } + + if(changed) { + WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); + } + return OPERATOR_FINISHED; +} + + +static void ED_MARKER_OT_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Delete Markers"; + ot->idname= "ED_MARKER_OT_delete"; + + /* api callbacks */ + ot->invoke= WM_operator_confirm; + ot->exec= ed_marker_delete_exec; + ot->poll= ED_operator_areaactive; + +} + +/* ************************** registration **********************************/ + +/* called in screen_ops.c:ED_operatortypes_screen() */ +void ED_marker_operatortypes(void) +{ + WM_operatortype_append(ED_MARKER_OT_add); + WM_operatortype_append(ED_MARKER_OT_move); + WM_operatortype_append(ED_MARKER_OT_duplicate); + WM_operatortype_append(ED_MARKER_OT_mouseselect); + WM_operatortype_append(ED_MARKER_OT_mouseselect_extend); + WM_operatortype_append(ED_MARKER_OT_border_select); + WM_operatortype_append(ED_MARKER_OT_select_all); + WM_operatortype_append(ED_MARKER_OT_delete); +} + +/* called in screen_ops.c:ED_keymap_screen() */ +void ED_marker_keymap(wmWindowManager *wm) +{ + ListBase *keymap= WM_keymap_listbase(wm, "Markers", 0, 0); + + WM_keymap_verify_item(keymap, "ED_MARKER_OT_add", MKEY, KM_PRESS, 0, 0); + WM_keymap_verify_item(keymap, "ED_MARKER_OT_move", EVT_TWEAK_R, KM_ANY, 0, 0); + WM_keymap_verify_item(keymap, "ED_MARKER_OT_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_verify_item(keymap, "ED_MARKER_OT_mouseselect", RIGHTMOUSE, KM_PRESS, 0, 0); + WM_keymap_verify_item(keymap, "ED_MARKER_OT_mouseselect_extend", RIGHTMOUSE, KM_PRESS, KM_SHIFT, 0); + WM_keymap_verify_item(keymap, "ED_MARKER_OT_border_select", BKEY, KM_PRESS, 0, 0); + WM_keymap_verify_item(keymap, "ED_MARKER_OT_select_all", AKEY, KM_PRESS, 0, 0); + WM_keymap_verify_item(keymap, "ED_MARKER_OT_delete", XKEY, KM_PRESS, 0, 0); + + WM_keymap_add_item(keymap, "ED_MARKER_OT_move", GKEY, KM_PRESS, 0, 0); + + /* generates event, in end to make select work */ + WM_keymap_verify_item(keymap, "WM_OT_tweak_gesture", RIGHTMOUSE, KM_PRESS, 0, 0); + +} diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h new file mode 100644 index 00000000000..70f9011417a --- /dev/null +++ b/source/blender/editors/include/ED_anim_api.h @@ -0,0 +1,172 @@ +/** + * $Id: + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef ED_ANIM_API_H +#define ED_ANIM_API_H + +struct ID; +struct ListBase; +struct bContext; +struct View2D; +struct bActionGroup; + +/* ************************************************ */ +/* ANIMATION CHANNEL FILTERING */ + +/* --------------- Data Types -------------------- */ + +/* This struct defines a structure used for quick and uniform access for + * channels of animation data + */ +typedef struct bAnimListElem { + struct bAnimListElem *next, *prev; + + void *data; /* source data this elem represents */ + int type; /* one of the ANIMTYPE_* values */ + int flag; /* copy of elem's flags for quick access */ + int index; /* copy of adrcode where applicable */ + + void *key_data; /* motion data - ipo or ipo-curve */ + short datatype; /* type of motion data to expect */ + + struct ID *id; /* ID block (ID_SC, ID_SCE, or ID_OB) that owns the channel */ + struct bActionGroup *grp; /* action group that owns the channel (only for Action/Dopesheet) */ + + void *owner; /* will either be an action channel or fake ipo-channel (for keys) */ + short ownertype; /* type of owner */ +} bAnimListElem; + + +/* Some types for easier type-testing */ +typedef enum eAnim_ChannelType { + ANIMTYPE_NONE= 0, + ANIMTYPE_SPECIALDATA, + + ANIMTYPE_OBJECT, + ANIMTYPE_GROUP, + + ANIMTYPE_FILLIPO, + ANIMTYPE_FILLCON, + + ANIMTYPE_FILLACTD, + ANIMTYPE_FILLIPOD, + ANIMTYPE_FILLCOND, + ANIMTYPE_FILLMATD, + + ANIMTYPE_DSMAT, + ANIMTYPE_DSLAM, + ANIMTYPE_DSCAM, + ANIMTYPE_DSCUR, + ANIMTYPE_DSSKEY, + + ANIMTYPE_ACHAN, + ANIMTYPE_CONCHAN, + ANIMTYPE_CONCHAN2, + ANIMTYPE_ICU, + ANIMTYPE_IPO, + + ANIMTYPE_SHAPEKEY, + ANIMTYPE_GPDATABLOCK, + ANIMTYPE_GPLAYER, +} eAnim_ChannelType; + +/* types of keyframe data in bAnimListElem */ +typedef enum eAnim_KeyType { + ALE_NONE = 0, /* no keyframe data */ + ALE_IPO, /* IPO block */ + ALE_ICU, /* IPO-Curve block */ + ALE_GPFRAME, /* Grease Pencil Frames */ + + // XXX the following are for summaries... should these be kept? + ALE_OB, /* Object summary */ + ALE_ACT, /* Action summary */ + ALE_GROUP, /* Action Group summary */ +} eAnim_KeyType; + +/* Main Data container types */ +typedef enum eAnimCont_Types { + ANIMCONT_NONE = 0, /* invalid or no data */ + ANIMCONT_ACTION, /* action (bAction) */ + ANIMCONT_SHAPEKEY, /* shapekey (Key) */ + ANIMCONT_GPENCIL, /* grease pencil (screen) */ + ANIMCONT_DOPESHEET, /* dopesheet (bDopesheet) */ +} eAnimCont_Types; + +/* filtering flags - under what circumstances should a channel be added */ +typedef enum eAnimFilter_Flags { + ALEFILTER_VISIBLE = (1<<0), /* should channels be visible */ + ALEFILTER_SEL = (1<<1), /* should channels be selected */ + ALEFILTER_FOREDIT = (1<<2), /* does editable status matter */ + ALEFILTER_CHANNELS = (1<<3), /* do we only care that it is a channel */ + ALEFILTER_IPOKEYS = (1<<4), /* only channels referencing ipo's */ + ALEFILTER_ONLYICU = (1<<5), /* only reference ipo-curves */ + ALEFILTER_FORDRAWING = (1<<6), /* make list for interface drawing */ + ALEFILTER_ACTGROUPED = (1<<7), /* belongs to the active actiongroup */ +} eAnimFilter_Flags; + + +/* ---------------- API -------------------- */ + +/* Obtain list of filtered Animation channels to operate on */ +void animdata_filter(struct ListBase *act_data, int filter_mode, void *data, short datatype); + +/* Obtain current anim-data context from Blender Context info */ +void *animdata_get_context(const struct bContext *C, short *datatype); + +/* ************************************************ */ +/* DRAWING API */ +// XXX should this get its own header file? + +/* ---------- Current Frame Drawing ---------------- */ + +/* flags for Current Frame Drawing */ +enum { + /* plain time indicator with no special indicators */ + DRAWCFRA_PLAIN = 0, + /* draw box indicating current frame number */ + DRAWCFRA_SHOW_NUMBOX = (1<<0), + /* time indication in seconds or frames */ + DRAWCFRA_UNIT_SECONDS = (1<<1), + /* show time-offset line */ + DRAWCFRA_SHOW_TIMEOFS = (1<<2), +} eAnimEditDraw_CurrentFrame; + +/* main call to draw current-frame indicator in an Animation Editor */ +void ANIM_draw_cfra(const bContext *C, struct View2D *v2d, short flag); + +/* ------------- Preview Range Drawing -------------- */ + +// XXX should preview range get its own file? + +/* main call to draw preview range curtains */ +void ANIM_draw_previewrange(const bContext *C, struct View2D *v2d); + +/* ************************************************* */ + +#endif /* ED_ANIM_API_H */ + diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h new file mode 100644 index 00000000000..29d19cd18e3 --- /dev/null +++ b/source/blender/editors/include/ED_keyframing.h @@ -0,0 +1,134 @@ +/** + * $Id: BIF_keyframing.h 17216 2008-10-29 11:20:02Z aligorith $ + * + * ***** 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., 59 Temple Place * Suite 330, Boston, MA 02111*1307, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender (with some old code) + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BIF_KEYFRAMING_H +#define BIF_KEYFRAMING_H + +struct ListBase; +struct ID; + +struct IpoCurve; +struct BezTriple; + +/* ************ Keyframing Management **************** */ + +/* Lesser Keyframing API call: + * Use this when validation of necessary animation data isn't necessary as it already + * exists, and there is a beztriple that can be directly copied into the array. + */ +int insert_bezt_icu(struct IpoCurve *icu, struct BezTriple *bezt); + +/* Main Keyframing API call: + * Use this when validation of necessary animation data isn't necessary as it + * already exists. It will insert a keyframe using the current value being keyframed. + */ +void insert_vert_icu(struct IpoCurve *icu, float x, float y, short flag); + + +/* flags for use by keyframe creation/deletion calls */ +enum { + /* used by isnertkey() and insert_vert_icu() */ + INSERTKEY_NEEDED = (1<<0), /* only insert keyframes where they're needed */ + INSERTKEY_MATRIX = (1<<1), /* insert 'visual' keyframes where possible/needed */ + INSERTKEY_FAST = (1<<2), /* don't recalculate handles,etc. after adding key */ + INSERTKEY_FASTR = (1<<3), /* don't realloc mem (or increase count, as array has already been set out) */ + INSERTKEY_REPLACE = (1<<4), /* only replace an existing keyframe (this overrides INSERTKEY_NEEDED) */ + + /* used by common_*key() functions - Note: these are generally mutually exclusive (only one will work at a time) */ + COMMONKEY_ADDMAP = (1<<10), /* common key: add texture-slot offset bitflag to adrcode before use */ + COMMONKEY_PCHANROT = (1<<11), /* common key: extend channel list using relevant pchan-rotations */ + /* all possible items for common_*key() functions */ + COMMONKEY_MODES = (COMMONKEY_ADDMAP|COMMONKEY_PCHANROT) +} eInsertKeyFlags; + +/* -------- */ + +/* Main Keyframing API calls: + * Use this to create any necessary animation data, and then insert a keyframe + * using the current value being keyframed, in the relevant place. Returns success. + */ + // TODO: adapt this for new data-api -> this blocktype, etc. stuff is evil! +short insertkey(struct ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag); + +/* Main Keyframing API call: + * Use this to delete keyframe on current frame for relevant channel. Will perform checks just in case. + */ +short deletekey(struct ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag); + + +/* Main Keyframe Management calls: + * These handle keyframes management from various spaces. They will handle the menus + * required for each space. + */ +void common_insertkey(void); +void common_deletekey(void); + +/* ************ Auto-Keyframing ********************** */ +/* Notes: + * - All the defines for this (User-Pref settings and Per-Scene settings) + * are defined in DNA_userdef_types.h + * - Scene settings take presidence over those for userprefs, with old files + * inheriting userpref settings for the scene settings + * - "On/Off + Mode" are stored per Scene, but "settings" are currently stored + * as userprefs + */ + +/* Auto-Keying macros for use by various tools */ + /* check if auto-keyframing is enabled (per scene takes presidence) */ +#define IS_AUTOKEY_ON ((scene) ? (scene->autokey_mode & AUTOKEY_ON) : (U.autokey_mode & AUTOKEY_ON)) + /* check the mode for auto-keyframing (per scene takes presidence) */ +#define IS_AUTOKEY_MODE(mode) ((scene) ? (scene->autokey_mode == AUTOKEY_MODE_##mode) : (U.autokey_mode == AUTOKEY_MODE_##mode)) + /* check if a flag is set for auto-keyframing (as userprefs only!) */ +#define IS_AUTOKEY_FLAG(flag) (U.autokey_flag & AUTOKEY_FLAG_##flag) + +/* ************ Keyframe Checking ******************** */ + +/* Main Keyframe Checking API call: + * Checks whether a keyframe exists for the given ID-block one the given frame. + * - It is recommended to call this method over the other keyframe-checkers directly, + * in case some detail of the implementation changes... + * - frame: the value of this is quite often result of frame_to_float(CFRA) + */ +short id_frame_has_keyframe(struct ID *id, float frame, short filter); + +/* filter flags for id_cfra_has_keyframe + * + * WARNING: do not alter order of these, as also stored in files + * (for v3d->keyflags) + */ +enum { + /* general */ + ANIMFILTER_LOCAL = (1<<0), /* only include locally available anim data */ + ANIMFILTER_MUTED = (1<<1), /* include muted elements */ + ANIMFILTER_ACTIVE = (1<<2), /* only include active-subelements */ + + /* object specific */ + ANIMFILTER_NOMAT = (1<<9), /* don't include material keyframes */ + ANIMFILTER_NOSKEY = (1<<10), /* don't include shape keys (for geometry) */ +} eAnimFilterFlags; + +#endif /* BIF_KEYFRAMING_H */ diff --git a/source/blender/editors/space_action/SConscript b/source/blender/editors/space_action/SConscript index f1152e95d0c..3d22e8ed5ab 100644 --- a/source/blender/editors/space_action/SConscript +++ b/source/blender/editors/space_action/SConscript @@ -6,4 +6,4 @@ sources = env.Glob('*.c') incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' -env.BlenderLib ( 'bf_editors_space_action', sources, Split(incs), [], libtype=['core','intern'], priority=[35, 40] ) +env.BlenderLib ( 'bf_editors_space_action', sources, Split(incs), [], libtype=['core','intern'], priority=[33, 37] ) diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 0ebcd88ffb2..12666bbd41b 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -57,6 +57,7 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "ED_anim_api.h" #include "ED_markers.h" #include "action_intern.h" // own include @@ -167,7 +168,7 @@ static void action_main_area_draw(const bContext *C, ARegion *ar) View2DGrid *grid; View2DScrollers *scrollers; float col[3]; - int unit; + short unit=0, flag=0; /* clear and setup matrix */ UI_GetThemeColor3fv(TH_BACK, col); @@ -184,6 +185,13 @@ static void action_main_area_draw(const bContext *C, ARegion *ar) /* data? */ + /* current frame */ + if (saction->flag & SACTION_DRAWTIME) flag |= DRAWCFRA_UNIT_SECONDS; + if ((saction->flag & SACTION_NODRAWCFRANUM)==0) flag |= DRAWCFRA_SHOW_NUMBOX; + ANIM_draw_cfra(C, v2d, flag); + + /* preview range */ + ANIM_draw_previewrange(C, v2d); /* reset view matrix */ UI_view2d_view_restore(C); @@ -298,7 +306,7 @@ void ED_spacetype_action(void) art->init= action_main_area_init; art->draw= action_main_area_draw; art->listener= action_main_area_listener; - art->keymapflag= ED_KEYMAP_VIEW2D; + art->keymapflag= ED_KEYMAP_VIEW2D|ED_KEYMAP_MARKERS; BLI_addhead(&st->regiontypes, art); diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c index e89a64faf94..5cadd521b66 100644 --- a/source/blender/editors/space_time/space_time.c +++ b/source/blender/editors/space_time/space_time.c @@ -85,7 +85,8 @@ static void time_draw_cfra_time(const bContext *C, SpaceTime *stime, ARegion *ar static void time_draw_sfra_efra(const bContext *C, SpaceTime *stime, ARegion *ar) { - View2D *v2d= UI_view2d_fromcontext(C); + View2D *v2d= UI_view2d_fromcontext(C); + //Scene *scene= CTX_data_scene(C); /* draw darkened area outside of active timeline * frame range used is preview range or scene range */ diff --git a/source/blender/editors/space_time/time_ops.c b/source/blender/editors/space_time/time_ops.c index 30b0e808e05..e8704457c00 100644 --- a/source/blender/editors/space_time/time_ops.c +++ b/source/blender/editors/space_time/time_ops.c @@ -181,16 +181,18 @@ void ED_TIME_OT_change_frame(wmOperatorType *ot) static int toggle_time_exec(bContext *C, wmOperator *op) { - SpaceTime *stime; + SpaceTime *stime= (SpaceTime *)CTX_wm_space_data(C); + ScrArea *curarea= CTX_wm_area(C); - if (ELEM(NULL, CTX_wm_area(C), CTX_wm_space_data(C))) + if (ELEM(NULL, curarea, stime)) return OPERATOR_CANCELLED; /* simply toggle draw frames flag for now */ - // XXX in past, this displayed menu to choose... (for later!) - stime= (SpaceTime*)CTX_wm_space_data(C); + // in past, this asked user to choose in a menu beforehand, but that is clumsy stime->flag ^= TIME_DRAWFRAMES; + ED_area_tag_redraw(curarea); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/util/ed_markers.c b/source/blender/editors/util/ed_markers.c deleted file mode 100644 index 6c27219c9c7..00000000000 --- a/source/blender/editors/util/ed_markers.c +++ /dev/null @@ -1,869 +0,0 @@ -/** - * $Id: - * - * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - * - * - * Contributor(s): Blender Foundation - * - * ***** END GPL LICENSE BLOCK ***** - */ - -#include -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "DNA_view2d_types.h" -#include "DNA_userdef_types.h" -#include "DNA_windowmanager_types.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "BLI_blenlib.h" - -#include "BKE_context.h" -#include "BKE_global.h" -#include "BKE_utildefines.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "BIF_gl.h" -#include "BIF_glutil.h" - -#include "UI_interface.h" -#include "UI_interface_icons.h" -#include "UI_view2d.h" -#include "UI_resources.h" -#include "UI_text.h" - -#include "ED_markers.h" -#include "ED_screen.h" -#include "ED_types.h" -#include "ED_util.h" - -/* ************* Marker API **************** */ - -static ListBase *context_get_markers(const bContext *C) -{ - -#if 0 - /* XXX get them from pose */ - if ((slink->spacetype == SPACE_ACTION) && (saction->flag & SACTION_POSEMARKERS_MOVE)) { - if (saction->action) - markers= &saction->action->markers; - else - markers= NULL; - } - else -#endif - - return &CTX_data_scene(C)->markers; -} - -/* ************* Marker Drawing ************ */ - -/* XXX */ -extern void ui_rasterpos_safe(float x, float y, float aspect); - -/* function to draw markers */ -static void draw_marker(View2D *v2d, TimeMarker *marker, int cfra, int flag) -{ - float xpos, ypixels, xscale, yscale; - int icon_id= 0; - - xpos = marker->frame; - /* no time correction for framelen! space is drawn with old values */ - - ypixels= v2d->mask.ymax-v2d->mask.ymin; - UI_view2d_getscale(v2d, &xscale, &yscale); - - glScalef(1.0/xscale, 1.0, 1.0); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - /* verticle line */ - if (flag & DRAW_MARKERS_LINES) { - setlinestyle(3); - if(marker->flag & SELECT) - glColor4ub(255,255,255, 96); - else - glColor4ub(0,0,0, 96); - - glBegin(GL_LINES); - glVertex2f((xpos*xscale)+0.5, 12); - glVertex2f((xpos*xscale)+0.5, 34*yscale); /* a bit lazy but we know it cant be greater then 34 strips high*/ - glEnd(); - setlinestyle(0); - } - - /* 5 px to offset icon to align properly, space / pixels corrects for zoom */ - if (flag & DRAW_MARKERS_LOCAL) { - icon_id= (marker->flag & ACTIVE) ? ICON_PMARKER_ACT : - (marker->flag & SELECT) ? ICON_PMARKER_SEL : - ICON_PMARKER; - } - else { - icon_id= (marker->flag & SELECT) ? ICON_MARKER_HLT : - ICON_MARKER; - } - - UI_icon_draw(xpos*xscale-5.0, 16.0, icon_id); - - glBlendFunc(GL_ONE, GL_ZERO); - glDisable(GL_BLEND); - - /* and the marker name too, shifted slightly to the top-right */ - if(marker->name && marker->name[0]) { - if(marker->flag & SELECT) { - UI_ThemeColor(TH_TEXT_HI); - ui_rasterpos_safe(xpos*xscale+4.0, (ypixels<=39.0)?(ypixels-10.0):29.0, 1.0); - } - else { - UI_ThemeColor(TH_TEXT); - if((marker->frame <= cfra) && (marker->frame+5 > cfra)) - ui_rasterpos_safe(xpos*xscale+4.0, (ypixels<=39.0)?(ypixels-10.0):29.0, 1.0); - else - ui_rasterpos_safe(xpos*xscale+4.0, 17.0, 1.0); - } - UI_DrawString(G.font, marker->name, 0); - } - glScalef(xscale, 1.0, 1.0); -} - -/* Draw Scene-Markers in time window */ -void draw_markers_time(const bContext *C, int flag) -{ - ListBase *markers= context_get_markers(C); - View2D *v2d= UI_view2d_fromcontext(C); - TimeMarker *marker; - - /* unselected markers are drawn at the first time */ - for (marker= markers->first; marker; marker= marker->next) { - if (!(marker->flag & SELECT)) draw_marker(v2d, marker, CTX_data_scene(C)->r.cfra, flag); - } - - /* selected markers are drawn later */ - for (marker= markers->first; marker; marker= marker->next) { - if (marker->flag & SELECT) draw_marker(v2d, marker, CTX_data_scene(C)->r.cfra, flag); - } -} - - - -/* ************************** add markers *************************** */ - -/* add TimeMarker at curent frame */ -static int ed_marker_add(bContext *C, wmOperator *op) -{ - ListBase *markers= context_get_markers(C); - TimeMarker *marker; - int frame= CTX_data_scene(C)->r.cfra; - - /* two markers can't be at the same place */ - for(marker= markers->first; marker; marker= marker->next) - if(marker->frame == frame) - return OPERATOR_CANCELLED; - - /* deselect all */ - for(marker= markers->first; marker; marker= marker->next) - marker->flag &= ~SELECT; - - marker = MEM_callocN(sizeof(TimeMarker), "TimeMarker"); - marker->flag= SELECT; - marker->frame= frame; - sprintf(marker->name, "Frame %d", frame); // XXX - temp code only - BLI_addtail(markers, marker); - - WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); - //BIF_undo_push("Add Marker"); - - return OPERATOR_FINISHED; -} - -static void ED_MARKER_OT_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Add Time Marker"; - ot->idname= "ED_MARKER_OT_add"; - - /* api callbacks */ - ot->exec= ed_marker_add; - ot->poll= ED_operator_areaactive; - -} - -/* ************************** transform markers *************************** */ - - -/* operator state vars used: - frs: delta movement - -functions: - - init() check selection, add customdata with old values and some lookups - - apply() do the actual movement - - exit() cleanup, send notifier - - cancel() to escpae from modal - -callbacks: - - exec() calls init, apply, exit - - invoke() calls init, adds modal handler - - modal() accept modal events while doing it, ends with apply and exit, or cancel - -*/ - -typedef struct MarkerMove { - SpaceLink *slink; - ListBase *markers; - int event_type; /* store invoke-event, to verify */ - int *oldframe, evtx, firstx; -} MarkerMove; - -/* copy selection to temp buffer */ -/* return 0 if not OK */ -static int ed_marker_move_init(bContext *C, wmOperator *op) -{ - ListBase *markers= context_get_markers(C); - MarkerMove *mm; - TimeMarker *marker; - int totmark=0; - int a; - - for (marker= markers->first; marker; marker= marker->next) - if (marker->flag & SELECT) totmark++; - - if (totmark==0) return 0; - - op->customdata= mm= MEM_callocN(sizeof(MarkerMove), "Markermove"); - mm->slink= CTX_wm_space_data(C); - mm->markers= markers; - mm->oldframe= MEM_callocN(totmark*sizeof(int), "MarkerMove oldframe"); - - for (a=0, marker= markers->first; marker; marker= marker->next) { - if (marker->flag & SELECT) { - mm->oldframe[a]= marker->frame; - a++; - } - } - - return 1; -} - -/* free stuff */ -static void ed_marker_move_exit(bContext *C, wmOperator *op) -{ - MarkerMove *mm= op->customdata; - - MEM_freeN(mm->oldframe); - MEM_freeN(op->customdata); - op->customdata= NULL; -} - -static int ed_marker_move_invoke(bContext *C, wmOperator *op, wmEvent *evt) -{ - if(ed_marker_move_init(C, op)) { - MarkerMove *mm= op->customdata; - - mm->evtx= evt->x; - mm->firstx= evt->x; - mm->event_type= evt->type; - - /* add temp handler */ - WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op); - - /* reset frs delta */ - RNA_int_set(op->ptr, "frs", 0); - - return OPERATOR_RUNNING_MODAL; - } - - return OPERATOR_CANCELLED; -} - -/* note, init has to be called succesfully */ -static void ed_marker_move_apply(bContext *C, wmOperator *op) -{ - MarkerMove *mm= op->customdata; - TimeMarker *marker; - int a, offs; - - offs= RNA_int_get(op->ptr, "frs"); - for (a=0, marker= mm->markers->first; marker; marker= marker->next) { - if (marker->flag & SELECT) { - marker->frame= mm->oldframe[a] + offs; - a++; - } - } -} - -/* only for modal */ -static void ed_marker_move_cancel(bContext *C, wmOperator *op) -{ - - RNA_int_set(op->ptr, "frs", 0); - ed_marker_move_apply(C, op); - ed_marker_move_exit(C, op); - - WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); -} - - -/* for tweak handlers, check configuration for how to interpret events */ -int WM_modal_tweak_check(wmEvent *evt, int tweak_event) -{ - /* user preset?? dunno... */ - int tweak_modal= 1; - - switch(tweak_event) { - case EVT_TWEAK_L: - case EVT_TWEAK_M: - case EVT_TWEAK_R: - if(evt->val==tweak_modal) - return 1; - default: - /* this case is when modal callcback didnt get started with a tweak */ - if(evt->val) - return 1; - } - return 0; -} - -static int ed_marker_move_modal(bContext *C, wmOperator *op, wmEvent *evt) -{ - MarkerMove *mm= op->customdata; - View2D *v2d= UI_view2d_fromcontext(C); - TimeMarker *marker, *selmarker=NULL; - float dx, fac; - char str[256]; - - switch(evt->type) { - case ESCKEY: - ed_marker_move_cancel(C, op); - return OPERATOR_CANCELLED; - - case LEFTMOUSE: - case MIDDLEMOUSE: - case RIGHTMOUSE: - if(WM_modal_tweak_check(evt, mm->event_type)) { - ed_marker_move_exit(C, op); - WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); - return OPERATOR_FINISHED; - } - - break; - case MOUSEMOVE: - - dx= v2d->mask.xmax-v2d->mask.xmin; - dx= (v2d->cur.xmax-v2d->cur.xmin)/dx; - - if (evt->x != mm->evtx) { /* XXX maybe init for firsttime */ - int a, offs, totmark=0; - - mm->evtx= evt->x; - - fac= ((float)(evt->x - mm->firstx)*dx); - - if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) - apply_keyb_grid(evt->shift, evt->ctrl, &fac, 0.0, FPS, 0.1*FPS, 0); - else - apply_keyb_grid(evt->shift, evt->ctrl, &fac, 0.0, 1.0, 0.1, U.flag & USER_AUTOGRABGRID); - - offs= (int)fac; - RNA_int_set(op->ptr, "frs", offs); - ed_marker_move_apply(C, op); - - /* cruft below is for header print */ - for (a=0, marker= mm->markers->first; marker; marker= marker->next) { - if (marker->flag & SELECT) { - selmarker= marker; - a++; totmark++; - } - } - - if (totmark==1) { - /* we print current marker value */ - if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) { - SpaceTime *stime= (SpaceTime *)mm->slink; - if (stime->flag & TIME_DRAWFRAMES) - sprintf(str, "Marker %d offset %d", selmarker->frame, offs); - else - sprintf(str, "Marker %.2f offset %.2f", FRA2TIME(selmarker->frame), FRA2TIME(offs)); - } - else if (mm->slink->spacetype == SPACE_ACTION) { -#if 0 -XXX if (saction->flag & SACTION_DRAWTIME) - sprintf(str, "Marker %.2f offset %.2f", FRA2TIME(selmarker->frame), FRA2TIME(offs)); - else - sprintf(str, "Marker %.2f offset %.2f", (double)(selmarker->frame), (double)(offs)); -#endif - } - else { - sprintf(str, "Marker %.2f offset %.2f", (double)(selmarker->frame), (double)(offs)); - } - } - else { - /* we only print the offset */ - if (ELEM(mm->slink->spacetype, SPACE_TIME, SPACE_SOUND)) { - SpaceTime *stime= (SpaceTime *)mm->slink; - if (stime->flag & TIME_DRAWFRAMES) - sprintf(str, "Marker offset %d ", offs); - else - sprintf(str, "Marker offset %.2f ", FRA2TIME(offs)); - } -#if 0 -XXX else if (mm->slink->spacetype == SPACE_ACTION) { - if (saction->flag & SACTION_DRAWTIME) - sprintf(str, "Marker offset %.2f ", FRA2TIME(offs)); - else - sprintf(str, "Marker offset %.2f ", (double)(offs)); - } -#endif - else { - sprintf(str, "Marker offset %.2f ", (double)(offs)); - } - } - - WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); - // headerprint(str); XXX - } - } - - return OPERATOR_RUNNING_MODAL; -} - -static int ed_marker_move_exec(bContext *C, wmOperator *op) -{ - if(ed_marker_move_init(C, op)) { - ed_marker_move_apply(C, op); - ed_marker_move_exit(C, op); - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -static void ED_MARKER_OT_move(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Move Time Marker"; - ot->idname= "ED_MARKER_OT_move"; - - /* api callbacks */ - ot->exec= ed_marker_move_exec; - ot->invoke= ed_marker_move_invoke; - ot->modal= ed_marker_move_modal; - ot->poll= ED_operator_areaactive; - - /* rna storage */ - RNA_def_property(ot->srna, "frs", PROP_INT, PROP_NONE); -} - -/* ************************** duplicate markers *************************** */ - -/* operator state vars used: - frs: delta movement - -functions: - - apply() do the actual duplicate - -callbacks: - - exec() calls apply, move_exec - - invoke() calls apply, move_invoke - - modal() uses move_modal - -*/ - - -/* duplicate selected TimeMarkers */ -static void ed_marker_duplicate_apply(bContext *C, wmOperator *op) -{ - ListBase *markers= context_get_markers(C); - TimeMarker *marker, *newmarker; - - /* go through the list of markers, duplicate selected markers and add duplicated copies - * to the begining of the list (unselect original markers) */ - for(marker= markers->first; marker; marker= marker->next) { - if(marker->flag & SELECT){ - /* unselect selected marker */ - marker->flag &= ~SELECT; - /* create and set up new marker */ - newmarker = MEM_callocN(sizeof(TimeMarker), "TimeMarker"); - newmarker->flag= SELECT; - newmarker->frame= marker->frame; - BLI_strncpy(newmarker->name, marker->name, sizeof(marker->name)); - /* new marker is added to the begining of list */ - BLI_addhead(markers, newmarker); - } - } -} - -static int ed_marker_duplicate_exec(bContext *C, wmOperator *op) -{ - ed_marker_duplicate_apply(C, op); - ed_marker_move_exec(C, op); /* assumes frs delta set */ - - return OPERATOR_FINISHED; - -} - -static int ed_marker_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *evt) -{ - ed_marker_duplicate_apply(C, op); - return ed_marker_move_invoke(C, op, evt); -} - -static void ED_MARKER_OT_duplicate(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Duplicate Time Marker"; - ot->idname= "ED_MARKER_OT_duplicate"; - - /* api callbacks */ - ot->exec= ed_marker_duplicate_exec; - ot->invoke= ed_marker_duplicate_invoke; - ot->modal= ed_marker_move_modal; - ot->poll= ED_operator_areaactive; - - /* rna storage */ - RNA_def_property(ot->srna, "frs", PROP_INT, PROP_NONE); -} - -/* ************************** selection ************************************/ - -/* select/deselect TimeMarker at current frame */ -static void select_timeline_marker_frame(ListBase *markers, int frame, unsigned char shift) -{ - TimeMarker *marker; - int select=0; - - for(marker= markers->first; marker; marker= marker->next) { - /* if Shift is not set, then deselect Markers */ - if(!shift) marker->flag &= ~SELECT; - /* this way a not-shift select will allways give 1 selected marker */ - if((marker->frame == frame) && (!select)) { - if(marker->flag & SELECT) - marker->flag &= ~SELECT; - else - marker->flag |= SELECT; - select = 1; - } - } -} - -static int find_nearest_marker_time(ListBase *markers, float dx) -{ - TimeMarker *marker, *nearest= NULL; - float dist, min_dist= 1000000; - - for(marker= markers->first; marker; marker= marker->next) { - dist = ABS((float)marker->frame - dx); - if(dist < min_dist){ - min_dist= dist; - nearest= marker; - } - } - - if(nearest) return nearest->frame; - else return (int)floor(dx); /* hrmf? */ -} - - -static int ed_marker_select(bContext *C, wmEvent *evt, int extend) -{ - ListBase *markers= context_get_markers(C); - View2D *v2d= UI_view2d_fromcontext(C); - float viewx; - int x, y, cfra; - - x= evt->x - CTX_wm_region(C)->winrct.xmin; - y= evt->y - CTX_wm_region(C)->winrct.ymin; - - UI_view2d_region_to_view(v2d, x, y, &viewx, NULL); - - cfra= find_nearest_marker_time(markers, viewx); - - if (extend) - select_timeline_marker_frame(markers, cfra, 1); - else - select_timeline_marker_frame(markers, cfra, 0); - - WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); - - return OPERATOR_PASS_THROUGH; -} - -static int ed_marker_select_extend_invoke(bContext *C, wmOperator *op, wmEvent *evt) -{ - return ed_marker_select(C, evt, 1); -} - -static int ed_marker_select_invoke(bContext *C, wmOperator *op, wmEvent *evt) -{ - return ed_marker_select(C, evt, 0); -} - -static void ED_MARKER_OT_mouseselect(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Select Time Marker"; - ot->idname= "ED_MARKER_OT_mouseselect"; - - /* api callbacks */ - ot->invoke= ed_marker_select_invoke; - ot->poll= ED_operator_areaactive; -} - -static void ED_MARKER_OT_mouseselect_extend(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Extend Select Time Marker"; - ot->idname= "ED_MARKER_OT_mouseselect_extend"; - - /* api callbacks */ - ot->invoke= ed_marker_select_extend_invoke; - ot->poll= ED_operator_areaactive; -} - -/* *************************** border select markers **************** */ - -/* operator state vars used: (added by default WM callbacks) - xmin, ymin - xmax, ymax - -customdata: the wmGesture pointer, with subwindow - -callbacks: - - exec() has to be filled in by user - - invoke() default WM function - adds modal handler - - modal() default WM function - accept modal events while doing it, calls exec(), handles ESC and border drawing - - poll() has to be filled in by user for context -*/ - -static int ed_marker_border_select_exec(bContext *C, wmOperator *op) -{ - View2D *v2d= UI_view2d_fromcontext(C); - ListBase *markers= context_get_markers(C); - TimeMarker *marker; - float xminf, xmaxf, yminf, ymaxf; - int event_type= RNA_int_get(op->ptr, "event_type"); - int xmin= RNA_int_get(op->ptr, "xmin"); - int xmax= RNA_int_get(op->ptr, "xmax"); - int ymin= RNA_int_get(op->ptr, "ymin"); - int ymax= RNA_int_get(op->ptr, "ymax"); - - UI_view2d_region_to_view(v2d, xmin, ymin, &xminf, &yminf); - UI_view2d_region_to_view(v2d, xmax, ymax, &xmaxf, &ymaxf); - - /* XXX disputable */ - if(yminf > 30.0f || ymaxf < 0.0f) - return 0; - - /* XXX marker context */ - for(marker= markers->first; marker; marker= marker->next) { - if ((marker->frame > xminf) && (marker->frame <= xmaxf)) { - switch (event_type) { - case LEFTMOUSE: - if ((marker->flag & SELECT) == 0) - marker->flag |= SELECT; - break; - case RIGHTMOUSE: - if (marker->flag & SELECT) - marker->flag &= ~SELECT; - break; - } - } - } - - WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); - - return 1; -} - -static void ED_MARKER_OT_border_select(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Marker Border select"; - ot->idname= "ED_MARKER_OT_border_select"; - - /* api callbacks */ - ot->exec= ed_marker_border_select_exec; - ot->invoke= WM_border_select_invoke; - ot->modal= WM_border_select_modal; - - ot->poll= ED_operator_areaactive; - - /* rna */ - RNA_def_property(ot->srna, "event_type", PROP_INT, PROP_NONE); - RNA_def_property(ot->srna, "xmin", PROP_INT, PROP_NONE); - RNA_def_property(ot->srna, "xmax", PROP_INT, PROP_NONE); - RNA_def_property(ot->srna, "ymin", PROP_INT, PROP_NONE); - RNA_def_property(ot->srna, "ymax", PROP_INT, PROP_NONE); - -} - -/* *********************** (de)select all ***************** */ - -static int ed_marker_select_all_exec(bContext *C, wmOperator *op) -{ - ListBase *markers= context_get_markers(C); - TimeMarker *marker; - int select= RNA_int_get(op->ptr, "select_type"); - - if(RNA_int_get(op->ptr, "select_swap")) { - for(marker= markers->first; marker; marker= marker->next) { - if(marker->flag & SELECT) - break; - } - if(marker) - select= 0; - else - select= 1; - } - - for(marker= markers->first; marker; marker= marker->next) { - if(select) - marker->flag |= SELECT; - else - marker->flag &= ~SELECT; - } - - WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); - - return OPERATOR_FINISHED; -} - -static int ed_marker_select_all_invoke(bContext *C, wmOperator *op, wmEvent *evt) -{ - RNA_int_set(op->ptr, "select_swap", 1); - - return ed_marker_select_all_exec(C, op); -} - -static void ED_MARKER_OT_select_all(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "(De)select all markers"; - ot->idname= "ED_MARKER_OT_select_all"; - - /* api callbacks */ - ot->exec= ed_marker_select_all_exec; - ot->invoke= ed_marker_select_all_invoke; - ot->poll= ED_operator_areaactive; - - /* rna */ - RNA_def_property(ot->srna, "select_swap", PROP_INT, PROP_NONE); - RNA_def_property(ot->srna, "select_type", PROP_INT, PROP_NONE); - -} - -/* ******************************* remove marker ***************** */ - -/* remove selected TimeMarkers */ -static int ed_marker_delete_exec(bContext *C, wmOperator *op) -{ - ListBase *markers= context_get_markers(C); - TimeMarker *marker, *nmarker; - short changed= 0; - - for(marker= markers->first; marker; marker= nmarker) { - nmarker= marker->next; - if(marker->flag & SELECT) { - BLI_freelinkN(markers, marker); - changed= 1; - } - } - - if(changed) { - WM_event_add_notifier(C, WM_NOTE_MARKERS_CHANGED, 0, NULL); - } - return OPERATOR_FINISHED; -} - - -static void ED_MARKER_OT_delete(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Delete Markers"; - ot->idname= "ED_MARKER_OT_delete"; - - /* api callbacks */ - ot->invoke= WM_operator_confirm; - ot->exec= ed_marker_delete_exec; - ot->poll= ED_operator_areaactive; - -} - -/* ************************** registration **********************************/ - -/* called in screen_ops.c:ED_operatortypes_screen() */ -void ED_marker_operatortypes(void) -{ - WM_operatortype_append(ED_MARKER_OT_add); - WM_operatortype_append(ED_MARKER_OT_move); - WM_operatortype_append(ED_MARKER_OT_duplicate); - WM_operatortype_append(ED_MARKER_OT_mouseselect); - WM_operatortype_append(ED_MARKER_OT_mouseselect_extend); - WM_operatortype_append(ED_MARKER_OT_border_select); - WM_operatortype_append(ED_MARKER_OT_select_all); - WM_operatortype_append(ED_MARKER_OT_delete); -} - -/* called in screen_ops.c:ED_keymap_screen() */ -void ED_marker_keymap(wmWindowManager *wm) -{ - ListBase *keymap= WM_keymap_listbase(wm, "Markers", 0, 0); - - WM_keymap_verify_item(keymap, "ED_MARKER_OT_add", MKEY, KM_PRESS, 0, 0); - WM_keymap_verify_item(keymap, "ED_MARKER_OT_move", EVT_TWEAK_R, KM_ANY, 0, 0); - WM_keymap_verify_item(keymap, "ED_MARKER_OT_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_verify_item(keymap, "ED_MARKER_OT_mouseselect", RIGHTMOUSE, KM_PRESS, 0, 0); - WM_keymap_verify_item(keymap, "ED_MARKER_OT_mouseselect_extend", RIGHTMOUSE, KM_PRESS, KM_SHIFT, 0); - WM_keymap_verify_item(keymap, "ED_MARKER_OT_border_select", BKEY, KM_PRESS, 0, 0); - WM_keymap_verify_item(keymap, "ED_MARKER_OT_select_all", AKEY, KM_PRESS, 0, 0); - WM_keymap_verify_item(keymap, "ED_MARKER_OT_delete", XKEY, KM_PRESS, 0, 0); - - WM_keymap_add_item(keymap, "ED_MARKER_OT_move", GKEY, KM_PRESS, 0, 0); - - /* generates event, in end to make select work */ - WM_keymap_verify_item(keymap, "WM_OT_tweak_gesture", RIGHTMOUSE, KM_PRESS, 0, 0); - -} -- cgit v1.2.3