diff --git a/source/blender/include/BIF_editaction.h b/source/blender/include/BIF_editaction.h index eb4adb4bbcf..938d6d3d672 100644 --- a/source/blender/include/BIF_editaction.h +++ b/source/blender/include/BIF_editaction.h @@ -118,6 +118,7 @@ void sample_action_keys(void); void column_select_action_keys(int mode); void selectall_action_keys(short mval[], short mode, short selectmode); void markers_selectkeys_between(void); +void nextprev_action_keyframe(short dir); /* Action Data Copying */ void free_actcopybuf(void); @@ -130,6 +131,9 @@ void down_sel_action(void); void top_sel_action(void); void bottom_sel_action(void); +void expand_all_action(void); +void openclose_level_action(short mode); + /* IPO/Handle Types */ void sethandles_action_keys(int code); void action_set_ipo_flags(short mode, short event); diff --git a/source/blender/src/editaction.c b/source/blender/src/editaction.c index 62ee9563b3f..311f2a653d5 100644 --- a/source/blender/src/editaction.c +++ b/source/blender/src/editaction.c @@ -2149,6 +2149,7 @@ void selectall_action_keys (short mval[], short mode, short select_mode) allqueue(REDRAWIPO, 0); } +/* Selects all visible keyframes between the specified markers */ void markers_selectkeys_between (void) { ListBase act_data = {NULL, NULL}; @@ -2188,6 +2189,7 @@ void markers_selectkeys_between (void) BLI_freelistN(&act_data); } +/* Selects all the keyframes on either side of the current frame (depends on which side the mouse is on) */ void selectkeys_leftright (short leftright, short select_mode) { ListBase act_data = {NULL, NULL}; @@ -2206,12 +2208,12 @@ void selectkeys_leftright (short leftright, short select_mode) data = get_action_context(&datatype); if (data == NULL) return; - if (leftright==1) { + if (leftright == 1) { min = -MAXFRAMEF; - max = (float)CFRA+0.1f; + max = (float)(CFRA + 0.1f); } else { - min = (float)CFRA-0.1f; + min = (float)(CFRA - 0.1f); max = MAXFRAMEF; } @@ -2219,7 +2221,7 @@ void selectkeys_leftright (short leftright, short select_mode) filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); actdata_filter(&act_data, filter, data, datatype); - /* select keys to the right */ + /* select keys on the side where most data occurs */ for (ale= act_data.first; ale; ale= ale->next) { if(NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) { actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1); @@ -2237,22 +2239,95 @@ void selectkeys_leftright (short leftright, short select_mode) allqueue(REDRAWNLA, 0); allqueue(REDRAWACTION, 0); allqueue(REDRAWIPO, 0); - } +/* ----------------------------------------- */ + +/* Jumps to the frame where the next/previous keyframe (that is visible) occurs + * dir: indicates direction + */ +void nextprev_action_keyframe (short dir) +{ + ListBase act_data = {NULL, NULL}; + bActListElem *ale; + int filter; + void *data; + short datatype; + + ListBase elems= {NULL, NULL}; + CfraElem *ce, *nearest=NULL; + float dist, min_dist= 1000000; + + + /* determine what type of data we are operating on */ + data = get_action_context(&datatype); + if (data == NULL) return; + + /* abort if no direction */ + if (dir == 0) + return; + + /* get list of keyframes that can be used (in global-time) */ + filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); + actdata_filter(&act_data, filter, data, datatype); + + for (ale= act_data.first; ale; ale= ale->next) { + if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) { + actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1); + make_cfra_list(ale->key_data, &elems); + actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1); + } + else + make_cfra_list(ale->key_data, &elems); + } + + BLI_freelistN(&act_data); + + /* find nearest keyframe to current frame */ + for (ce= elems.first; ce; ce= ce->next) { + dist= ABS(ce->cfra - CFRA); + + if (dist < min_dist) { + min_dist= dist; + nearest= ce; + } + } + + /* if a nearest keyframe was found, use the one either side */ + if (nearest) { + short changed= 0; + + if ((dir > 0) && (nearest->next)) { + CFRA= nearest->next->cfra; + changed= 1; + } + else if ((dir < 0) && (nearest->prev)) { + CFRA= nearest->prev->cfra; + changed= 1; + } + + if (changed) { + update_for_newframe(); + allqueue(REDRAWALL, 0); + } + } + + /* free temp data */ + BLI_freelistN(&elems); +} /* ----------------------------------------- */ /* This function makes a list of the selected keyframes * in the ipo curves it has been passed */ -static void make_sel_cfra_list(Ipo *ipo, ListBase *elems) +static void make_sel_cfra_list (Ipo *ipo, ListBase *elems) { IpoCurve *icu; if (ipo == NULL) return; - for(icu= ipo->curve.first; icu; icu= icu->next) { + for (icu= ipo->curve.first; icu; icu= icu->next) { BezTriple *bezt; int a= 0; @@ -2266,7 +2341,7 @@ static void make_sel_cfra_list(Ipo *ipo, ListBase *elems) /* This function selects all key frames in the same column(s) as a already selected key(s) * or marker(s), or all the keyframes on a particular frame (triggered by a RMB on x-scrollbar) */ -void column_select_action_keys(int mode) +void column_select_action_keys (int mode) { ListBase elems= {NULL, NULL}; CfraElem *ce; @@ -2329,6 +2404,7 @@ void column_select_action_keys(int mode) BLI_freelistN(&elems); } + /* some quick defines for borderselect modes */ enum { ACTEDIT_BORDERSEL_ALL = 0, @@ -2713,9 +2789,9 @@ void top_sel_action () act = G.saction->action; if (!act) return; - for (achan= act->chanbase.first; achan; achan= achan->next){ + for (achan= act->chanbase.first; achan; achan= achan->next) { if (VISIBLE_ACHAN(achan)) { - if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)){ + if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)) { /* take it out off the chain keep data */ BLI_remlink (&act->chanbase, achan); /* make it first element */ @@ -2727,12 +2803,12 @@ void top_sel_action () } } /* clear temp flags */ - for (achan= act->chanbase.first; achan; achan= achan->next){ + for (achan= act->chanbase.first; achan; achan= achan->next) { achan->flag = achan->flag & ~ACHAN_MOVED; } /* Clean up and redraw stuff */ - remake_action_ipos (act); + remake_action_ipos(act); BIF_undo_push("Top Action channel"); allspace(REMAKEIPO, 0); allqueue(REDRAWACTION, 0); @@ -2751,7 +2827,7 @@ void up_sel_action () for (achan=act->chanbase.first; achan; achan= achan->next) { if (VISIBLE_ACHAN(achan)) { - if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)){ + if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)) { prev = achan->prev; if (prev) { /* take it out off the chain keep data */ @@ -2766,12 +2842,12 @@ void up_sel_action () } } /* clear temp flags */ - for (achan=act->chanbase.first; achan; achan= achan->next){ + for (achan=act->chanbase.first; achan; achan= achan->next) { achan->flag = achan->flag & ~ACHAN_MOVED; } /* Clean up and redraw stuff */ - remake_action_ipos (act); + remake_action_ipos(act); BIF_undo_push("Up Action channel"); allspace(REMAKEIPO, 0); allqueue(REDRAWACTION, 0); @@ -2790,7 +2866,7 @@ void down_sel_action () for (achan= act->chanbase.last; achan; achan= achan->prev) { if (VISIBLE_ACHAN(achan)) { - if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)){ + if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)) { next = achan->next; if (next) next = next->next; if (next) { @@ -2811,12 +2887,12 @@ void down_sel_action () } } /* clear temp flags */ - for (achan= act->chanbase.first; achan; achan= achan->next){ + for (achan= act->chanbase.first; achan; achan= achan->next) { achan->flag = achan->flag & ~ACHAN_MOVED; } /* Clean up and redraw stuff */ - remake_action_ipos (act); + remake_action_ipos(act); BIF_undo_push("Down Action channel"); allspace(REMAKEIPO, 0); allqueue(REDRAWACTION, 0); @@ -2850,7 +2926,7 @@ void bottom_sel_action () } /* Clean up and redraw stuff */ - remake_action_ipos (act); + remake_action_ipos(act); BIF_undo_push("Bottom Action channel"); allspace(REMAKEIPO, 0); allqueue(REDRAWACTION, 0); @@ -2858,6 +2934,84 @@ void bottom_sel_action () allqueue(REDRAWNLA, 0); } + +/* Expand all channels to show full hierachy */ +void expand_all_action (void) +{ + bAction *act; + bActionChannel *achan; + short mode= 0; + + /* Get the selected action, exit if none are selected */ + // TODO: really this should be done with the "action editor api" stuff, but this will suffice for now + act = G.saction->action; + if (act == NULL) return; + + /* check if expand all, or close all */ + for (achan=act->chanbase.first; achan; achan= achan->next) { + if (VISIBLE_ACHAN(achan)) { + if (EXPANDED_ACHAN(achan)) + mode= 1; + break; + } + } + + /* expand/collapse depending on mode */ + for (achan=act->chanbase.first; achan; achan= achan->next) { + if (VISIBLE_ACHAN(achan)) { + if (mode == 1) + achan->flag |= (ACHAN_EXPANDED|ACHAN_SHOWIPO|ACHAN_SHOWCONS); + else + achan->flag &= ~(ACHAN_EXPANDED|ACHAN_SHOWIPO|ACHAN_SHOWCONS); + } + } + + /* Cleanup and do redraws */ + BIF_undo_push("Expand Action Hierachy"); + allqueue(REDRAWACTION, 0); +} + +/* For visible channels, expand/collapse one level */ +void openclose_level_action (short mode) +{ + bAction *act; + bActionChannel *achan; + + /* Get the selected action, exit if none are selected */ + // TODO: really this should be done with the "action editor api" stuff, but this will suffice for now + act = G.saction->action; + if (act == NULL) return; + + /* Abort if no operation required */ + if (mode == 0) return; + + /* Only affect selected channels */ + for (achan=act->chanbase.first; achan; achan= achan->next) { + if (VISIBLE_ACHAN(achan) && SEL_ACHAN(achan)) { + if (EXPANDED_ACHAN(achan)) { + if (FILTER_IPO_ACHAN(achan) || FILTER_CON_ACHAN(achan)) { + if (mode < 0) + achan->flag &= ~(ACHAN_SHOWIPO|ACHAN_SHOWCONS); + } + else { + if (mode > 0) + achan->flag |= (ACHAN_SHOWIPO|ACHAN_SHOWCONS); + else + achan->flag &= ~ACHAN_EXPANDED; + } + } + else { + if (mode > 0) + achan->flag |= ACHAN_EXPANDED; + } + } + } + + /* Cleanup and do redraws */ + BIF_undo_push("Expand/Collapse Action Level"); + allqueue(REDRAWACTION, 0); +} + /* **************************************************** */ /* ACTION MARKERS (PoseLib features) */ /* NOTE: yes, these duplicate code from edittime.c a bit, but these do a bit more... @@ -3243,30 +3397,40 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt) case PAGEUPKEY: if (datatype == ACTCONT_ACTION) { - if(G.qual & LR_SHIFTKEY) + if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) top_sel_action(); - else if (G.qual & LR_CTRLKEY) + else if (G.qual == LR_SHIFTKEY) up_sel_action(); + else if (G.qual == LR_CTRLKEY) + nextprev_action_keyframe(1); else nextprev_marker(1); } else if (datatype == ACTCONT_SHAPEKEY) { /* only jump to markers possible (key channels can't be moved yet) */ - nextprev_marker(1); + if (G.qual == LR_CTRLKEY) + nextprev_action_keyframe(1); + else + nextprev_marker(1); } break; case PAGEDOWNKEY: if (datatype == ACTCONT_ACTION) { - if(G.qual & LR_SHIFTKEY) + if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) bottom_sel_action(); - else if (G.qual & LR_CTRLKEY) + else if (G.qual == LR_SHIFTKEY) down_sel_action(); + else if (G.qual == LR_CTRLKEY) + nextprev_action_keyframe(-1); else nextprev_marker(-1); } else if (datatype == ACTCONT_SHAPEKEY) { /* only jump to markers possible (key channels can't be moved yet) */ - nextprev_marker(-1); + if (G.qual == LR_CTRLKEY) + nextprev_action_keyframe(-1); + else + nextprev_marker(-1); } break; @@ -3356,20 +3520,39 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt) mouse_action(select_mode); } break; + + case ACCENTGRAVEKEY: + if (datatype == ACTCONT_ACTION) + expand_all_action(); + break; + case PADPLUSKEY: - view2d_zoom(G.v2d, 0.1154, sa->winx, sa->winy); - test_view2d(G.v2d, sa->winx, sa->winy); - view2d_do_locks(curarea, V2D_LOCK_COPY); - - doredraw= 1; + if (G.qual == LR_CTRLKEY) { + if (datatype == ACTCONT_ACTION) + openclose_level_action(1); + } + else { + view2d_zoom(G.v2d, 0.1154, sa->winx, sa->winy); + test_view2d(G.v2d, sa->winx, sa->winy); + view2d_do_locks(curarea, V2D_LOCK_COPY); + + doredraw= 1; + } break; case PADMINUS: - view2d_zoom(G.v2d, -0.15, sa->winx, sa->winy); - test_view2d(G.v2d, sa->winx, sa->winy); - view2d_do_locks(curarea, V2D_LOCK_COPY); + if (G.qual == LR_CTRLKEY) { + if (datatype == ACTCONT_ACTION) + openclose_level_action(-1); + } + else { + view2d_zoom(G.v2d, -0.15, sa->winx, sa->winy); + test_view2d(G.v2d, sa->winx, sa->winy); + view2d_do_locks(curarea, V2D_LOCK_COPY); + + doredraw= 1; + } + break; - doredraw= 1; - break; case MIDDLEMOUSE: case WHEELUPMOUSE: case WHEELDOWNMOUSE: diff --git a/source/blender/src/header_action.c b/source/blender/src/header_action.c index ab30c4cad52..c9edd6cd1f0 100644 --- a/source/blender/src/header_action.c +++ b/source/blender/src/header_action.c @@ -92,8 +92,13 @@ enum { ACTMENU_VIEW_SLIDERS, ACTMENU_VIEW_NEXTMARKER, ACTMENU_VIEW_PREVMARKER, + ACTMENU_VIEW_NEXTKEYFRAME, + ACTMENU_VIEW_PREVKEYFRAME, ACTMENU_VIEW_TIME, - ACTMENU_VIEW_NOHIDE + ACTMENU_VIEW_NOHIDE, + ACTMENU_VIEW_OPENLEVELS, + ACTMENU_VIEW_CLOSELEVELS, + ACTMENU_VIEW_EXPANDALL }; enum { @@ -286,7 +291,7 @@ static void do_action_viewmenu(void *arg, int event) break; case ACTMENU_VIEW_LOCK: G.v2d->flag ^= V2D_VIEWLOCK; - if(G.v2d->flag & V2D_VIEWLOCK) + if (G.v2d->flag & V2D_VIEWLOCK) view2d_do_locks(curarea, 0); break; case ACTMENU_VIEW_SLIDERS: /* Show sliders (when applicable) */ @@ -295,10 +300,10 @@ static void do_action_viewmenu(void *arg, int event) case ACTMENU_VIEW_MAXIMIZE: /* Maximize Window */ /* using event B_FULL */ break; - case ACTMENU_VIEW_NEXTMARKER: /* jump to next marker */ + case ACTMENU_VIEW_NEXTMARKER: /* Jump to next marker */ nextprev_marker(1); break; - case ACTMENU_VIEW_PREVMARKER: /* jump to previous marker */ + case ACTMENU_VIEW_PREVMARKER: /* Jump to previous marker */ nextprev_marker(-1); break; case ACTMENU_VIEW_TIME: /* switch between frames and seconds display */ @@ -307,6 +312,21 @@ static void do_action_viewmenu(void *arg, int event) case ACTMENU_VIEW_NOHIDE: /* Show hidden channels */ G.saction->flag ^= SACTION_NOHIDE; break; + case ACTMENU_VIEW_NEXTKEYFRAME: /* Jump to next keyframe */ + nextprev_action_keyframe(1); + break; + case ACTMENU_VIEW_PREVKEYFRAME: /* Jump to previous keyframe */ + nextprev_action_keyframe(-1); + break; + case ACTMENU_VIEW_OPENLEVELS: /* Unfold channels one step */ + openclose_level_action(1); + break; + case ACTMENU_VIEW_CLOSELEVELS: /* Fold channels one step */ + openclose_level_action(-1); + break; + case ACTMENU_VIEW_EXPANDALL: /* Expands all channels */ + expand_all_action(); + break; } allqueue(REDRAWVIEW3D, 0); } @@ -363,6 +383,26 @@ static uiBlock *action_viewmenu(void *arg_unused) menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_VIEW_AUTOUPDATE, ""); + uiDefBut(block, SEPR, 0, "", 0, yco-=6, + menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); + + /* only if editing action... */ + // TODO: improve this code! + + if (G.saction->action) { + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Toggle Show Hierachy|~", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_VIEW_EXPANDALL, ""); + + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Expand One Level|Ctrl NumPad+", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_VIEW_OPENLEVELS, ""); + + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Collapse One Level|Ctrl NumPad-", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_VIEW_CLOSELEVELS, ""); + } + uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); @@ -372,6 +412,13 @@ static uiBlock *action_viewmenu(void *arg_unused) uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Jump To Prev Marker|PageDown", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_VIEW_PREVMARKER, ""); + + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Jump To Next Keyframe|Ctrl PageUp", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_VIEW_NEXTKEYFRAME, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Jump To Prev Keyframe|Ctrl PageDown", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_VIEW_PREVKEYFRAME, ""); uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); @@ -850,11 +897,11 @@ static uiBlock *action_keymenu_chanposmenu(void *arg_unused) uiBlockSetButmFunc(block, do_action_keymenu_chanposmenu, NULL); uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Move Up|Ctrl Page Up", 0, yco-=20, + "Move Up|Shift Page Up", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_KEY_CHANPOS_MOVE_CHANNEL_UP, ""); uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Move Down|Ctrl Page Down", 0, yco-=20, + "Move Down|Shift Page Down", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_KEY_CHANPOS_MOVE_CHANNEL_DOWN, ""); @@ -862,11 +909,11 @@ static uiBlock *action_keymenu_chanposmenu(void *arg_unused) menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Move to Top|Shift Page Up", 0, yco-=20, + "Move to Top|Ctrl Shift Page Up", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_KEY_CHANPOS_MOVE_CHANNEL_TOP, ""); uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Move to Bottom|Shift Page Down", 0, yco-=20, + "Move to Bottom|Ctrl Shift Page Down", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_KEY_CHANPOS_MOVE_CHANNEL_BOTTOM, "");