Quick patch for action-filtering system, so that the IKEY (Insert Key) -> "Key Only Selected" doesn't key in channels that are hidden. Documented the convoulted logic there a bit. Note: constraint channel keys in group channels seems to have become broken again at some point before this commit
1020 lines
24 KiB
C
1020 lines
24 KiB
C
/**
|
|
* $Id: BIF_edittime.c
|
|
*
|
|
* ***** 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) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): none yet.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_arithb.h"
|
|
|
|
#include "DNA_action_types.h"
|
|
#include "DNA_ipo_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_space_types.h"
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
|
|
#include "BKE_ipo.h"
|
|
#include "BKE_utildefines.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_material.h"
|
|
#include "BKE_library.h"
|
|
|
|
#include "BIF_space.h"
|
|
#include "BIF_screen.h"
|
|
#include "BIF_interface.h"
|
|
#include "BIF_toolbox.h"
|
|
#include "BIF_mywindow.h"
|
|
#include "BIF_editaction.h"
|
|
|
|
#include "BSE_drawipo.h"
|
|
#include "BSE_edit.h"
|
|
#include "BSE_headerbuttons.h"
|
|
#include "BSE_time.h"
|
|
|
|
#include "BDR_editobject.h"
|
|
|
|
#include "blendef.h"
|
|
|
|
#include "mydevice.h"
|
|
|
|
#include "PIL_time.h"
|
|
|
|
/* declarations */
|
|
void winqreadtimespace(ScrArea *, void *, BWinEvent *);
|
|
|
|
/* ************* Marker API **************** */
|
|
|
|
/* add TimeMarker at curent frame */
|
|
void add_marker(int frame)
|
|
{
|
|
TimeMarker *marker;
|
|
|
|
/* two markers can't be at the same place */
|
|
for(marker= G.scene->markers.first; marker; marker= marker->next)
|
|
if(marker->frame == frame) return;
|
|
/* deselect all */
|
|
for(marker= G.scene->markers.first; marker; marker= marker->next)
|
|
marker->flag &= ~SELECT;
|
|
|
|
marker = MEM_callocN(sizeof(TimeMarker), "TimeMarker");
|
|
marker->flag= SELECT;
|
|
marker->frame= frame;
|
|
BLI_addtail(&(G.scene->markers), marker);
|
|
|
|
BIF_undo_push("Add Marker");
|
|
}
|
|
|
|
|
|
|
|
/* remove selected TimeMarkers */
|
|
void remove_marker(void)
|
|
{
|
|
TimeMarker *marker, *nmarker;
|
|
short changed= 0;
|
|
|
|
for(marker= G.scene->markers.first; marker; marker= nmarker) {
|
|
nmarker= marker->next;
|
|
if(marker->flag & SELECT) {
|
|
BLI_freelinkN(&(G.scene->markers), marker);
|
|
changed= 1;
|
|
}
|
|
}
|
|
|
|
if (changed)
|
|
BIF_undo_push("Remove Marker");
|
|
}
|
|
|
|
/* rename first selected TimeMarker */
|
|
void rename_marker(void)
|
|
{
|
|
TimeMarker *marker;
|
|
char name[64];
|
|
|
|
for(marker= G.scene->markers.first; marker; marker= marker->next) {
|
|
if(marker->flag & SELECT) {
|
|
strcpy(name, marker->name);
|
|
if (sbutton(name, 0, sizeof(name)-1, "Name: "))
|
|
BLI_strncpy(marker->name, name, sizeof(marker->name));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// BIF_undo_push("Rename Marker");
|
|
}
|
|
|
|
/* duplicate selected TimeMarkers */
|
|
void duplicate_marker(void)
|
|
{
|
|
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= G.scene->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(&(G.scene->markers), newmarker);
|
|
}
|
|
}
|
|
|
|
transform_markers('g', 0);
|
|
}
|
|
|
|
void transform_markers(int mode, int smode) // mode and smode unused here, for callback
|
|
{
|
|
SpaceLink *slink= curarea->spacedata.first;
|
|
SpaceTime *stime= curarea->spacedata.first;
|
|
SpaceAction *saction = curarea->spacedata.first;
|
|
ListBase *markers;
|
|
TimeMarker *marker, *selmarker=NULL;
|
|
float dx, fac;
|
|
int a, ret_val= 0, totmark=0, *oldframe, offs, firsttime=1;
|
|
unsigned short event;
|
|
short val, pmval[2], mval[2], mvalo[2];
|
|
char str[32];
|
|
|
|
/* hack for pose-markers in action editor */
|
|
if ((slink->spacetype == SPACE_ACTION) && (saction->flag & SACTION_POSEMARKERS_MOVE)) {
|
|
if (saction->action)
|
|
markers= &saction->action->markers;
|
|
else
|
|
markers= NULL;
|
|
}
|
|
else
|
|
markers= &G.scene->markers;
|
|
|
|
for (marker= markers->first; marker; marker= marker->next) {
|
|
if (marker->flag & SELECT) totmark++;
|
|
}
|
|
if (totmark==0) return;
|
|
|
|
oldframe= MEM_mallocN(totmark*sizeof(int), "marker array");
|
|
for (a=0, marker= markers->first; marker; marker= marker->next) {
|
|
if (marker->flag & SELECT) {
|
|
oldframe[a]= marker->frame;
|
|
selmarker= marker; // used for headerprint
|
|
a++;
|
|
}
|
|
}
|
|
|
|
dx= G.v2d->mask.xmax-G.v2d->mask.xmin;
|
|
dx= (G.v2d->cur.xmax-G.v2d->cur.xmin)/dx;
|
|
|
|
getmouseco_areawin(pmval);
|
|
mvalo[0]= pmval[0];
|
|
|
|
while (ret_val == 0) {
|
|
getmouseco_areawin(mval);
|
|
|
|
if (mval[0] != mvalo[0] || firsttime) {
|
|
mvalo[0]= mval[0];
|
|
firsttime= 0;
|
|
|
|
fac= (((float)(mval[0] - pmval[0]))*dx);
|
|
|
|
if (ELEM(slink->spacetype, SPACE_TIME, SPACE_SOUND))
|
|
apply_keyb_grid(&fac, 0.0, FPS, 0.1*FPS, 0);
|
|
else
|
|
apply_keyb_grid(&fac, 0.0, 1.0, 0.1, U.flag & USER_AUTOGRABGRID);
|
|
offs= (int)fac;
|
|
|
|
for (a=0, marker= markers->first; marker; marker= marker->next) {
|
|
if (marker->flag & SELECT) {
|
|
marker->frame= oldframe[a] + offs;
|
|
a++;
|
|
}
|
|
}
|
|
|
|
if (totmark==1) {
|
|
/* we print current marker value */
|
|
if (ELEM(slink->spacetype, SPACE_TIME, SPACE_SOUND)) {
|
|
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 (slink->spacetype == SPACE_ACTION) {
|
|
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));
|
|
}
|
|
else {
|
|
sprintf(str, "Marker %.2f offset %.2f", (double)(selmarker->frame), (double)(offs));
|
|
}
|
|
}
|
|
else {
|
|
/* we only print the offset */
|
|
if (ELEM(slink->spacetype, SPACE_TIME, SPACE_SOUND)) {
|
|
if (stime->flag & TIME_DRAWFRAMES)
|
|
sprintf(str, "Marker offset %d ", offs);
|
|
else
|
|
sprintf(str, "Marker offset %.2f ", FRA2TIME(offs));
|
|
}
|
|
else if (slink->spacetype == SPACE_ACTION) {
|
|
if (saction->flag & SACTION_DRAWTIME)
|
|
sprintf(str, "Marker offset %.2f ", FRA2TIME(offs));
|
|
else
|
|
sprintf(str, "Marker offset %.2f ", (double)(offs));
|
|
}
|
|
else {
|
|
sprintf(str, "Marker offset %.2f ", (double)(offs));
|
|
}
|
|
}
|
|
headerprint(str);
|
|
|
|
force_draw(0); // areas identical to this, 0 = no header
|
|
}
|
|
else PIL_sleep_ms(10); // idle
|
|
|
|
/* emptying queue and reading events */
|
|
while ( qtest() ) {
|
|
event= extern_qread(&val);
|
|
|
|
if (val) {
|
|
if (ELEM(event, ESCKEY, RIGHTMOUSE)) ret_val= 2;
|
|
else if (ELEM3(event, LEFTMOUSE, RETKEY, SPACEKEY)) ret_val= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* restore? */
|
|
if (ret_val==2) {
|
|
for (a=0, marker= markers->first; marker; marker= marker->next) {
|
|
if (marker->flag & SELECT) {
|
|
marker->frame= oldframe[a];
|
|
a++;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
BIF_undo_push("Move Markers");
|
|
}
|
|
MEM_freeN(oldframe);
|
|
allqueue(REDRAWMARKER, 0);
|
|
}
|
|
|
|
/* select/deselect all TimeMarkers
|
|
* test - based on current selections?
|
|
* sel - selection status to set all markers to if blanket apply status
|
|
*/
|
|
void deselect_markers(short test, short sel)
|
|
{
|
|
TimeMarker *marker;
|
|
|
|
/* check if need to find out whether to how to select markers */
|
|
if (test) {
|
|
/* dependant on existing selection */
|
|
/* determine if select all or deselect all */
|
|
sel = 1;
|
|
for (marker= G.scene->markers.first; marker; marker= marker->next) {
|
|
if (marker->flag & SELECT) {
|
|
sel = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* do selection */
|
|
for (marker= G.scene->markers.first; marker; marker= marker->next) {
|
|
if (sel == 2) {
|
|
marker->flag ^= SELECT;
|
|
}
|
|
else if (sel == 1) {
|
|
if ((marker->flag & SELECT)==0)
|
|
marker->flag |= SELECT;
|
|
}
|
|
else {
|
|
if (marker->flag & SELECT)
|
|
marker->flag &= ~SELECT;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* not dependant on existing selection */
|
|
for (marker= G.scene->markers.first; marker; marker= marker->next) {
|
|
if (sel==2) {
|
|
marker->flag ^= SELECT;
|
|
}
|
|
else if (sel==1) {
|
|
if ((marker->flag & SELECT)==0)
|
|
marker->flag |= SELECT;
|
|
}
|
|
else {
|
|
if (marker->flag & SELECT)
|
|
marker->flag &= ~SELECT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void borderselect_markers_func(float xmin, float xmax, int selectmode)
|
|
{
|
|
TimeMarker *marker;
|
|
|
|
for(marker= G.scene->markers.first; marker; marker= marker->next) {
|
|
if ((marker->frame > xmin) && (marker->frame <= xmax)) {
|
|
switch (selectmode) {
|
|
case SELECT_ADD:
|
|
if ((marker->flag & SELECT) == 0)
|
|
marker->flag |= SELECT;
|
|
break;
|
|
case SELECT_SUBTRACT:
|
|
if (marker->flag & SELECT)
|
|
marker->flag &= ~SELECT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* border-select markers */
|
|
void borderselect_markers(void)
|
|
{
|
|
rcti rect;
|
|
rctf rectf;
|
|
int val, selectmode;
|
|
short mval[2];
|
|
|
|
if ( (val = get_border(&rect, 3)) ){
|
|
if (val == LEFTMOUSE)
|
|
selectmode = SELECT_ADD;
|
|
else
|
|
selectmode = SELECT_SUBTRACT;
|
|
|
|
mval[0]= rect.xmin;
|
|
mval[1]= rect.ymin+2;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
|
|
mval[0]= rect.xmax;
|
|
mval[1]= rect.ymax-2;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
|
|
|
|
/* do markers */
|
|
borderselect_markers_func(rectf.xmin, rectf.xmax, selectmode);
|
|
|
|
BIF_undo_push("Border Select Markers");
|
|
allqueue(REDRAWMARKER, 0);
|
|
}
|
|
}
|
|
|
|
void nextprev_marker(short dir)
|
|
{
|
|
TimeMarker *marker, *cur=NULL, *first, *last;
|
|
int mindist= MAXFRAME, dist;
|
|
|
|
first= last= G.scene->markers.first;
|
|
for(marker= G.scene->markers.first; marker; marker= marker->next) {
|
|
/* find closest to current frame first */
|
|
dist= (marker->frame/G.scene->r.framelen) - CFRA;
|
|
if(dir==1 && dist>0 && dist<mindist) {
|
|
mindist= dist;
|
|
cur= marker;
|
|
}
|
|
else if(dir==-1 && dist<0 && -dist<mindist) {
|
|
mindist= -dist;
|
|
cur= marker;
|
|
}
|
|
/* find first/last */
|
|
if(marker->frame > last->frame) last= marker;
|
|
if(marker->frame < first->frame) first= marker;
|
|
}
|
|
|
|
if(cur==NULL) {
|
|
if(dir==1) cur= first;
|
|
else cur= last;
|
|
}
|
|
if(cur) {
|
|
CFRA= cur->frame/G.scene->r.framelen;
|
|
update_for_newframe();
|
|
allqueue(REDRAWALL, 0);
|
|
}
|
|
}
|
|
|
|
void get_minmax_markers(short sel, float *first, float *last)
|
|
{
|
|
TimeMarker *marker;
|
|
ListBase *markers;
|
|
float min, max;
|
|
int selcount = 0;
|
|
|
|
markers= &(G.scene->markers);
|
|
|
|
if (sel)
|
|
for (marker= markers->first; marker; marker= marker->next) {
|
|
if (marker->flag & SELECT)
|
|
selcount++;
|
|
}
|
|
else {
|
|
selcount= BLI_countlist(markers);
|
|
}
|
|
|
|
if (markers->first && markers->last) {
|
|
min= ((TimeMarker *)markers->first)->frame;
|
|
max= ((TimeMarker *)markers->last)->frame;
|
|
}
|
|
else {
|
|
*first = 0.0f;
|
|
*last = 0.0f;
|
|
return;
|
|
}
|
|
|
|
if (selcount > 1) {
|
|
for (marker= markers->first; marker; marker= marker->next) {
|
|
if (sel) {
|
|
if (marker->flag & SELECT) {
|
|
if (marker->frame < min)
|
|
min= marker->frame;
|
|
else if (marker->frame > max)
|
|
max= marker->frame;
|
|
}
|
|
}
|
|
else {
|
|
if (marker->frame < min)
|
|
min= marker->frame;
|
|
else if (marker->frame > max)
|
|
max= marker->frame;
|
|
}
|
|
}
|
|
}
|
|
|
|
*first= min;
|
|
*last= max;
|
|
}
|
|
|
|
TimeMarker *find_nearest_marker(ListBase *markers, int clip_y)
|
|
{
|
|
TimeMarker *marker;
|
|
float xmin, xmax;
|
|
rctf rectf;
|
|
short mval[2];
|
|
|
|
getmouseco_areawin (mval);
|
|
|
|
/* first clip selection in Y */
|
|
if ((clip_y) && (mval[1] > 30))
|
|
return NULL;
|
|
|
|
mval[0]-=7;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
|
|
mval[0]+=14;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
|
|
|
|
xmin= rectf.xmin;
|
|
xmax= rectf.xmax;
|
|
|
|
for (marker= markers->first; marker; marker= marker->next) {
|
|
if ((marker->frame > xmin) && (marker->frame <= xmax)) {
|
|
return marker;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Adds a marker to list of cfra elems */
|
|
void add_marker_to_cfra_elem(ListBase *lb, TimeMarker *marker, short only_sel)
|
|
{
|
|
CfraElem *ce, *cen;
|
|
|
|
/* should this one only be considered if it is selected? */
|
|
if ((only_sel) && ((marker->flag & SELECT)==0))
|
|
return;
|
|
|
|
/* try to find a previous cfra elem */
|
|
ce= lb->first;
|
|
while(ce) {
|
|
|
|
if( ce->cfra==marker->frame ) {
|
|
/* do because of double keys */
|
|
if(marker->flag & SELECT) ce->sel= marker->flag;
|
|
return;
|
|
}
|
|
else if(ce->cfra > marker->frame) break;
|
|
|
|
ce= ce->next;
|
|
}
|
|
|
|
cen= MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem");
|
|
if(ce) BLI_insertlinkbefore(lb, ce, cen);
|
|
else BLI_addtail(lb, cen);
|
|
|
|
cen->cfra= marker->frame;
|
|
cen->sel= marker->flag;
|
|
}
|
|
|
|
/* This function makes a list of all the markers. The only_sel
|
|
* argument is used to specify whether only the selected markers
|
|
* are added.
|
|
*/
|
|
void make_marker_cfra_list(ListBase *lb, short only_sel)
|
|
{
|
|
TimeMarker *marker;
|
|
|
|
for (marker= G.scene->markers.first; marker; marker= marker->next) {
|
|
add_marker_to_cfra_elem(lb, marker, only_sel);
|
|
}
|
|
}
|
|
|
|
int find_nearest_marker_time(float dx)
|
|
{
|
|
TimeMarker *marker, *nearest= NULL;
|
|
float dist, min_dist= 1000000;
|
|
|
|
for(marker= G.scene->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);
|
|
}
|
|
|
|
/* *********** End Markers - Markers API *************** */
|
|
/* select/deselect TimeMarker at current frame */
|
|
static void select_timeline_marker_frame(int frame, unsigned char shift)
|
|
{
|
|
TimeMarker *marker;
|
|
int select=0;
|
|
|
|
for(marker= G.scene->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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *********** end Markers - TimeLine *************** */
|
|
|
|
/* set the animation preview range of scene */
|
|
void anim_previewrange_set()
|
|
{
|
|
rcti rect;
|
|
rctf rectf;
|
|
short val, mval[2];
|
|
|
|
/* set range by drawing border-select rectangle */
|
|
if ( (val = get_border(&rect, 5)) ) {
|
|
/* get frame numbers */
|
|
mval[0]= rect.xmin;
|
|
mval[1]= rect.ymin+2;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
|
|
mval[0]= rect.xmax;
|
|
mval[1]= rect.ymax-2;
|
|
areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
|
|
|
|
/* set preview-range */
|
|
if (rectf.xmin < 1) rectf.xmin = 1.0f;
|
|
if (rectf.xmax < 1) rectf.xmax = 1.0f;
|
|
G.scene->r.psfra= rectf.xmin;
|
|
G.scene->r.pefra= rectf.xmax;
|
|
|
|
BIF_undo_push("Set anim-preview range");
|
|
allqueue(REDRAWTIME, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWIPO, 0);
|
|
allqueue(REDRAWBUTSALL, 0);
|
|
}
|
|
}
|
|
|
|
/* clear the animation preview range for scene */
|
|
void anim_previewrange_clear()
|
|
{
|
|
G.scene->r.psfra = 0;
|
|
G.scene->r.pefra = 0;
|
|
|
|
BIF_undo_push("Clear anim-preview range");
|
|
allqueue(REDRAWTIME, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWNLA, 0);
|
|
allqueue(REDRAWBUTSALL, 0);
|
|
}
|
|
|
|
/* ************ end Animation Preview Range ********** */
|
|
|
|
|
|
static int float_to_frame(float frame)
|
|
{
|
|
int to= (int) floor(0.5 + frame/G.scene->r.framelen );
|
|
|
|
return to;
|
|
}
|
|
|
|
static float find_closest_cfra_elem(ListBase elems, int dir, float closest)
|
|
{
|
|
CfraElem *ce;
|
|
|
|
for(ce= elems.first; ce; ce= ce->next) {
|
|
if (dir==-1) {
|
|
if( float_to_frame(ce->cfra)<CFRA) {
|
|
if ((ce->cfra > closest) || (closest == CFRA)) {
|
|
closest= ce->cfra;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if(float_to_frame(ce->cfra)>CFRA) {
|
|
if ((ce->cfra < closest) || (closest == CFRA)) {
|
|
closest= ce->cfra;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return closest;
|
|
}
|
|
|
|
void nextprev_timeline_key(short dir)
|
|
{
|
|
/*mostly copied from drawobject.c, draw_object() AND editipo.c, movekey_obipo() */
|
|
Object *ob;
|
|
bActionChannel *achan;
|
|
bAction *act;
|
|
ListBase elems;
|
|
float closest= CFRA;
|
|
int a;
|
|
|
|
if (OBACT) {
|
|
ob = OBACT;
|
|
|
|
if(ob) {
|
|
if(ob!=G.obedit) {
|
|
if(ob->ipo) {
|
|
/* convert the ipo to a list of 'current frame elements' */
|
|
|
|
elems.first= elems.last= NULL;
|
|
make_cfra_list(ob->ipo, &elems);
|
|
|
|
closest= find_closest_cfra_elem(elems, dir, closest);
|
|
|
|
BLI_freelistN(&elems);
|
|
}
|
|
|
|
if(ob->action) {
|
|
act = ob->action;
|
|
/* go through each channel in the action */
|
|
for (achan=act->chanbase.first; achan; achan=achan->next){
|
|
/* convert the ipo to a list of 'current frame elements' */
|
|
|
|
if(achan->ipo) {
|
|
elems.first= elems.last= NULL;
|
|
make_cfra_list(achan->ipo, &elems);
|
|
|
|
closest= find_closest_cfra_elem(elems, dir, closest);
|
|
|
|
BLI_freelistN(&elems);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(a=0; a<ob->totcol; a++) {
|
|
Material *ma= give_current_material(ob, a+1);
|
|
if(ma && ma->ipo) {
|
|
elems.first= elems.last= NULL;
|
|
make_cfra_list(ma->ipo, &elems);
|
|
|
|
closest= find_closest_cfra_elem(elems, dir, closest);
|
|
|
|
BLI_freelistN(&elems);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
a= float_to_frame(closest);
|
|
|
|
if (a!=CFRA) {
|
|
CFRA= a;
|
|
update_for_newframe();
|
|
}
|
|
|
|
BIF_undo_push("Next/Prev Key");
|
|
allqueue(REDRAWALL, 0);
|
|
}
|
|
}
|
|
|
|
/* return the current marker for this frame,
|
|
we can have more then 1 marker per frame, this just returns the first :/ */
|
|
TimeMarker *get_frame_marker(int frame)
|
|
{
|
|
TimeMarker *marker, *best_marker = NULL;
|
|
int best_frame = -MAXFRAME*2;
|
|
for (marker= G.scene->markers.first; marker; marker= marker->next) {
|
|
if (marker->frame==frame) {
|
|
return marker;
|
|
}
|
|
|
|
if ( marker->frame > best_frame && marker->frame < frame) {
|
|
best_marker = marker;
|
|
best_frame = marker->frame;
|
|
}
|
|
}
|
|
|
|
return best_marker;
|
|
}
|
|
|
|
|
|
void timeline_frame_to_center(void)
|
|
{
|
|
float dtime;
|
|
|
|
dtime= CFRA*(G.scene->r.framelen) - (G.v2d->cur.xmin + G.v2d->cur.xmax)/2.0;
|
|
G.v2d->cur.xmin += dtime;
|
|
G.v2d->cur.xmax += dtime;
|
|
scrarea_queue_winredraw(curarea);
|
|
}
|
|
|
|
/* copy of this is actually in editscreen.c, but event based */
|
|
static void timeline_force_draw(short val)
|
|
{
|
|
ScrArea *sa, *tempsa, *samin= NULL;
|
|
int dodraw;
|
|
|
|
if(val & TIME_LEFTMOST_3D_WIN) {
|
|
ScrArea *sa= G.curscreen->areabase.first;
|
|
int min= 10000;
|
|
for(; sa; sa= sa->next) {
|
|
if(sa->spacetype==SPACE_VIEW3D) {
|
|
if(sa->winrct.xmin - sa->winrct.ymin < min) {
|
|
samin= sa;
|
|
min= sa->winrct.xmin - sa->winrct.ymin;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tempsa= curarea;
|
|
sa= G.curscreen->areabase.first;
|
|
while(sa) {
|
|
dodraw= 0;
|
|
if(sa->spacetype==SPACE_VIEW3D) {
|
|
if(sa==samin || (val & TIME_ALL_3D_WIN)) dodraw= 1;
|
|
}
|
|
else if(ELEM6(sa->spacetype, SPACE_NLA, SPACE_IPO, SPACE_SEQ, SPACE_BUTS, SPACE_ACTION, SPACE_SOUND)) {
|
|
if(val & TIME_ALL_ANIM_WIN) dodraw= 1;
|
|
}
|
|
else if(sa->spacetype==SPACE_BUTS) {
|
|
if(val & TIME_ALL_BUTS_WIN) dodraw= 1;
|
|
}
|
|
else if(sa->spacetype==SPACE_IMAGE) {
|
|
if (val & TIME_ALL_IMAGE_WIN) dodraw = 1;
|
|
}
|
|
else if(sa->spacetype==SPACE_SEQ) {
|
|
if (val & TIME_SEQ) dodraw = 1;
|
|
}
|
|
else if(sa->spacetype==SPACE_TIME) dodraw= 2;
|
|
|
|
if(dodraw) {
|
|
areawinset(sa->win);
|
|
scrarea_do_windraw(sa);
|
|
if(dodraw==2) scrarea_do_headdraw(sa);
|
|
}
|
|
sa= sa->next;
|
|
}
|
|
areawinset(tempsa->win);
|
|
|
|
screen_swapbuffers();
|
|
|
|
}
|
|
|
|
/* ***************************** */
|
|
|
|
/* Right. Now for some implementation: */
|
|
void winqreadtimespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
|
|
{
|
|
SpaceTime *stime= spacedata;
|
|
unsigned short event= evt->event;
|
|
short val= evt->val;
|
|
float dx, dy;
|
|
int doredraw= 0, cfra, first = 0;
|
|
short mval[2], nr;
|
|
short mousebut = L_MOUSE;
|
|
|
|
if(sa->win==0) return;
|
|
|
|
if(val) {
|
|
|
|
if( uiDoBlocks(&sa->uiblocks, event, 1)!=UI_NOTHING ) event= 0;
|
|
|
|
/* swap mouse buttons based on user preference */
|
|
if (U.flag & USER_LMOUSESELECT) {
|
|
if (event == LEFTMOUSE) {
|
|
event = RIGHTMOUSE;
|
|
mousebut = L_MOUSE;
|
|
} else if (event == RIGHTMOUSE) {
|
|
event = LEFTMOUSE;
|
|
mousebut = R_MOUSE;
|
|
}
|
|
}
|
|
|
|
switch(event) {
|
|
case LEFTMOUSE:
|
|
stime->flag |= TIME_CFRA_NUM;
|
|
do {
|
|
getmouseco_areawin(mval);
|
|
areamouseco_to_ipoco(G.v2d, mval, &dx, &dy);
|
|
|
|
cfra = (int)(dx+0.5f);
|
|
if(cfra< MINFRAME) cfra= MINFRAME;
|
|
|
|
if( cfra!=CFRA || first )
|
|
{
|
|
first= 0;
|
|
CFRA= cfra;
|
|
update_for_newframe_nodraw(0); // 1= nosound
|
|
timeline_force_draw(stime->redraws);
|
|
}
|
|
else PIL_sleep_ms(30);
|
|
|
|
} while(get_mbut() & mousebut);
|
|
|
|
stime->flag &= ~TIME_CFRA_NUM;
|
|
allqueue(REDRAWALL, 0);
|
|
break;
|
|
|
|
case RIGHTMOUSE: /* select/deselect marker */
|
|
getmouseco_areawin(mval);
|
|
areamouseco_to_ipoco(G.v2d, mval, &dx, &dy);
|
|
|
|
cfra= find_nearest_marker_time(dx);
|
|
|
|
if (G.qual && LR_SHIFTKEY)
|
|
select_timeline_marker_frame(cfra, 1);
|
|
else
|
|
select_timeline_marker_frame(cfra, 0);
|
|
|
|
force_draw(0);
|
|
std_rmouse_transform(transform_markers);
|
|
|
|
break;
|
|
case MIDDLEMOUSE:
|
|
case WHEELUPMOUSE:
|
|
case WHEELDOWNMOUSE:
|
|
view2dmove(event); /* in drawipo.c */
|
|
break;
|
|
case PADPLUSKEY:
|
|
dx= (float)(0.1154*(G.v2d->cur.xmax-G.v2d->cur.xmin));
|
|
G.v2d->cur.xmin+= dx;
|
|
G.v2d->cur.xmax-= dx;
|
|
test_view2d(G.v2d, sa->winx, sa->winy);
|
|
view2d_do_locks(curarea, V2D_LOCK_COPY);
|
|
doredraw= 1;
|
|
break;
|
|
case PADMINUS:
|
|
dx= (float)(0.15*(G.v2d->cur.xmax-G.v2d->cur.xmin));
|
|
G.v2d->cur.xmin-= dx;
|
|
G.v2d->cur.xmax+= dx;
|
|
test_view2d(G.v2d, sa->winx, sa->winy);
|
|
view2d_do_locks(curarea, V2D_LOCK_COPY);
|
|
doredraw= 1;
|
|
break;
|
|
case HOMEKEY:
|
|
first= G.scene->r.sfra;
|
|
if(first >= G.scene->r.efra) first= G.scene->r.efra;
|
|
G.v2d->cur.xmin=G.v2d->tot.xmin= (float)first-2;
|
|
G.v2d->cur.xmax=G.v2d->tot.xmax= (float)G.scene->r.efra+2;
|
|
doredraw= 1;
|
|
break;
|
|
|
|
case PAGEUPKEY: /* next keyframe */
|
|
if(G.qual==LR_CTRLKEY)
|
|
nextprev_timeline_key(1);
|
|
else
|
|
nextprev_marker(1);
|
|
break;
|
|
case PAGEDOWNKEY: /* prev keyframe */
|
|
if(G.qual==LR_CTRLKEY)
|
|
nextprev_timeline_key(-1);
|
|
else
|
|
nextprev_marker(-1);
|
|
break;
|
|
|
|
case AKEY:
|
|
/* deselect all TimeMarkers */
|
|
deselect_markers(1, 0);
|
|
allqueue(REDRAWMARKER, 0);
|
|
break;
|
|
case BKEY:
|
|
/* borderselect markers */
|
|
borderselect_markers();
|
|
break;
|
|
case DKEY:
|
|
if(G.qual==LR_SHIFTKEY)
|
|
duplicate_marker();
|
|
break;
|
|
case CKEY:
|
|
timeline_frame_to_center();
|
|
break;
|
|
case GKEY: /* move marker */
|
|
transform_markers('g', 0);
|
|
break;
|
|
case EKEY: /* set end frame */
|
|
if (G.scene->r.psfra) {
|
|
if (CFRA < G.scene->r.psfra)
|
|
G.scene->r.psfra= CFRA;
|
|
G.scene->r.pefra= CFRA;
|
|
}
|
|
else
|
|
G.scene->r.efra = CFRA;
|
|
allqueue(REDRAWALL, 1);
|
|
break;
|
|
case MKEY: /* add, rename marker */
|
|
if (G.qual & LR_CTRLKEY)
|
|
rename_marker();
|
|
else
|
|
add_marker(CFRA);
|
|
allqueue(REDRAWMARKER, 0);
|
|
break;
|
|
case PKEY: /* preview-range stuff */
|
|
if (G.qual & LR_CTRLKEY) /* set preview range */
|
|
anim_previewrange_set();
|
|
else if (G.qual & LR_ALTKEY) /* clear preview range */
|
|
anim_previewrange_clear();
|
|
break;
|
|
case SKEY: /* set start frame */
|
|
if (G.scene->r.psfra) {
|
|
if (G.scene->r.pefra < CFRA)
|
|
G.scene->r.pefra= CFRA;
|
|
G.scene->r.psfra= CFRA;
|
|
}
|
|
else
|
|
G.scene->r.sfra = CFRA;
|
|
allqueue(REDRAWALL, 1);
|
|
break;
|
|
case TKEY: /* popup menu */
|
|
nr= pupmenu("Time value%t|Frames %x1|Seconds%x2");
|
|
if (nr>0) {
|
|
if(nr==1) stime->flag |= TIME_DRAWFRAMES;
|
|
else stime->flag &= ~TIME_DRAWFRAMES;
|
|
doredraw= 1;
|
|
}
|
|
break;
|
|
case DELKEY:
|
|
case XKEY:
|
|
if( okee("Erase selected")==0 ) break;
|
|
|
|
remove_marker();
|
|
allqueue(REDRAWMARKER, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(doredraw)
|
|
scrarea_queue_winredraw(sa);
|
|
}
|
|
|
|
|