コード例 #1
0
ファイル: AMTimeline.cs プロジェクト: nathanfunk/MateAnimator
    void showFrames(AMTrack _track, ref float track_y, Event e, bool birdseye, Vector2 scrollViewBounds)
    {
        //string tooltip = "";
        int t = _track.id;
        int selectedTrack = aData.getCurrentTake().selectedTrack;
        // frames start
        if(!_track.foldout && !oData.showFramesForCollapsedTracks) {
            track_y += height_track_foldin;
            return;
        }
        float numFrames = (aData.getCurrentTake().numFrames < numFramesToRender ? aData.getCurrentTake().numFrames : numFramesToRender);
        Rect rectFrames = new Rect(width_track, track_y, current_width_frame * numFrames, height_track);
        if(!_track.foldout) track_y += height_track_foldin;
        else track_y += height_track;
        if(track_y < scrollViewBounds.x) return; // if end y is before min y
        float _current_height_frame = (_track.foldout ? current_height_frame : height_track_foldin);
        #region frames
        GUI.BeginGroup(rectFrames);
        // draw frames
        bool selected;
        bool ghost = isDragging && aData.getCurrentTake().hasGhostSelection();
        bool isTrackSelected = t == selectedTrack || aData.getCurrentTake().contextSelectionTracks.Contains(t);
        Rect rectFramesBirdsEye = new Rect(0f, 0f, rectFrames.width, _current_height_frame);
        float width_birdseye = current_height_frame * 0.5f;
        if(birdseye) {
            GUI.color = colBirdsEyeFrames;
            GUI.DrawTexture(rectFramesBirdsEye, EditorGUIUtility.whiteTexture);
        }
        else {
            texFrSet.wrapMode = TextureWrapMode.Repeat;
            float startPos = aData.getCurrentTake().startFrame % 5f;
            GUI.DrawTextureWithTexCoords(rectFramesBirdsEye, texFrSet, new Rect(startPos / 5f, 0f, numFrames / 5f, 1f));
            float birdsEyeFadeAlpha = (1f - (current_width_frame - width_frame_birdseye_min)) / 1.2f;
            if(birdsEyeFadeAlpha > 0f) {
                GUI.color = new Color(colBirdsEyeFrames.r, colBirdsEyeFrames.g, colBirdsEyeFrames.b, birdsEyeFadeAlpha);
                GUI.DrawTexture(rectFramesBirdsEye, EditorGUIUtility.whiteTexture);
            }
        }
        GUI.color = new Color(72f / 255f, 72f / 255f, 72f / 255f, 1f);
        GUI.DrawTexture(new Rect(rectFramesBirdsEye.x, rectFramesBirdsEye.y, rectFramesBirdsEye.width, 1f), EditorGUIUtility.whiteTexture);
        GUI.DrawTexture(new Rect(rectFramesBirdsEye.x, rectFramesBirdsEye.y + rectFramesBirdsEye.height - 1f, rectFramesBirdsEye.width, 1f), EditorGUIUtility.whiteTexture);
        GUI.color = Color.white;
        // draw birds eye selection
        if(isTrackSelected) {
            if(ghost) {
                // dragging only one frame that has a key. do not show ghost selection
                if(birdseye && aData.getCurrentTake().contextSelection.Count == 2 && aData.getCurrentTake().contextSelection[0] == aData.getCurrentTake().contextSelection[1] && _track.hasKeyOnFrame(aData.getCurrentTake().contextSelection[0])) {
                    GUI.color = new Color(0f, 0f, 1f, .5f);
                    GUI.DrawTexture(new Rect(current_width_frame * (aData.getCurrentTake().ghostSelection[0] - aData.getCurrentTake().startFrame) - width_birdseye / 2f + current_width_frame / 2f, 0f, width_birdseye, _current_height_frame), texKeyBirdsEye);
                    GUI.color = Color.white;
                }
                else if(aData.getCurrentTake().ghostSelection != null) {
                    // birds eye ghost selection
                    GUI.color = new Color(156f / 255f, 162f / 255f, 216f / 255f, .9f);
                    for(int i = 0; i < aData.getCurrentTake().ghostSelection.Count; i += 2) {
                        int contextFrameStart = aData.getCurrentTake().ghostSelection[i];
                        int contextFrameEnd = aData.getCurrentTake().ghostSelection[i + 1];
                        if(contextFrameStart < (int)aData.getCurrentTake().startFrame) contextFrameStart = (int)aData.getCurrentTake().startFrame;
                        if(contextFrameEnd > (int)aData.getCurrentTake().endFrame) contextFrameEnd = (int)aData.getCurrentTake().endFrame;
                        float contextWidth = (contextFrameEnd - contextFrameStart + 1) * current_width_frame;
                        GUI.DrawTexture(new Rect(rectFramesBirdsEye.x + (contextFrameStart - aData.getCurrentTake().startFrame) * current_width_frame, rectFramesBirdsEye.y + 1f, contextWidth, rectFramesBirdsEye.height - 2f), EditorGUIUtility.whiteTexture);
                    }
                    // draw birds eye ghost key frames
                    GUI.color = new Color(0f, 0f, 1f, .5f);
                    foreach(int _key_frame in aData.getCurrentTake().getKeyFramesInGhostSelection((int)aData.getCurrentTake().startFrame, (int)aData.getCurrentTake().endFrame, t)) {
                        if(birdseye)
                            GUI.DrawTexture(new Rect(current_width_frame * (_key_frame - aData.getCurrentTake().startFrame) - width_birdseye / 2f + current_width_frame / 2f, 0f, width_birdseye, _current_height_frame), texKeyBirdsEye);
                        else {
                            Rect rectFrame = new Rect(current_width_frame * (_key_frame - aData.getCurrentTake().startFrame), 0f, current_width_frame, _current_height_frame);
                            GUI.DrawTexture(new Rect(rectFrame.x + 2f, rectFrame.y + rectFrame.height - (rectFrame.width - 4f) - 2f, rectFrame.width - 4f, rectFrame.width - 4f), texFrKey);
                        }
                    }
                    GUI.color = Color.white;
                }
            }
            else if(aData.getCurrentTake().contextSelection.Count > 0 && /*do not show single frame selection in birdseye*/!(birdseye && aData.getCurrentTake().contextSelection.Count == 2 && aData.getCurrentTake().contextSelection[0] == aData.getCurrentTake().contextSelection[1])) {
                // birds eye context selection
                for(int i = 0; i < aData.getCurrentTake().contextSelection.Count; i += 2) {
                    //GUI.color = new Color(121f/255f,127f/255f,184f/255f,(birdseye ? 1f : .9f));
                    GUI.color = new Color(86f / 255f, 95f / 255f, 178f / 255f, .8f);
                    int contextFrameStart = aData.getCurrentTake().contextSelection[i];
                    int contextFrameEnd = aData.getCurrentTake().contextSelection[i + 1];
                    if(contextFrameStart < (int)aData.getCurrentTake().startFrame) contextFrameStart = (int)aData.getCurrentTake().startFrame;
                    if(contextFrameEnd > (int)aData.getCurrentTake().endFrame) contextFrameEnd = (int)aData.getCurrentTake().endFrame;
                    float contextWidth = (contextFrameEnd - contextFrameStart + 1) * current_width_frame;
                    Rect rectContextSelection = new Rect(rectFramesBirdsEye.x + (contextFrameStart - aData.getCurrentTake().startFrame) * current_width_frame, rectFramesBirdsEye.y + 1f, contextWidth, rectFramesBirdsEye.height - 2f);
                    GUI.DrawTexture(rectContextSelection, EditorGUIUtility.whiteTexture);
                    if(dragType != (int)DragType.ContextSelection) EditorGUIUtility.AddCursorRect(rectContextSelection, MouseCursor.SlideArrow);
                }
                GUI.color = Color.white;
            }
        }
        // birds eye keyframe information, used to draw buttons in proper order
        List<int> birdseyeKeyFrames = new List<int>();
        List<Rect> birdseyeKeyRects = new List<Rect>();
        if(birdseye) {
            // draw birds eye keyframe textures, prepare button rects
            foreach(AMKey key in _track.keys) {
                if(!key) continue;

                selected = ((isTrackSelected) && aData.getCurrentTake().isFrameSelected(key.frame));
                //_track.sortKeys();
                if(key.frame < aData.getCurrentTake().startFrame) continue;
                if(key.frame > aData.getCurrentTake().endFrame) break;
                Rect rectKeyBirdsEye = new Rect(current_width_frame * (key.frame - aData.getCurrentTake().startFrame) - width_birdseye / 2f + current_width_frame / 2f, 0f, width_birdseye, _current_height_frame);
                if(selected) GUI.color = Color.blue;
                GUI.DrawTexture(rectKeyBirdsEye, texKeyBirdsEye);
                GUI.color = Color.white;
                birdseyeKeyFrames.Add(key.frame);
                birdseyeKeyRects.Add(rectKeyBirdsEye);
            }

            // birds eye buttons
            if(birdseyeKeyFrames.Count > 0) {
                for(int i = birdseyeKeyFrames.Count - 1; i >= 0; i--) {
                    selected = ((isTrackSelected) && aData.getCurrentTake().isFrameSelected(birdseyeKeyFrames[i]));
                    if(dragType != (int)DragType.MoveSelection && dragType != (int)DragType.ContextSelection && !isRenamingTake && isRenamingTrack == -1 && mouseOverFrame == 0 && birdseyeKeyRects[i].Contains(e.mousePosition) && mouseOverElement == (int)ElementType.None) {
                        mouseOverFrame = birdseyeKeyFrames[i];
                        mouseOverTrack = t;
                        mouseOverSelectedFrame = (selected);
                    }
                    if(selected && dragType != (int)DragType.ContextSelection) EditorGUIUtility.AddCursorRect(birdseyeKeyRects[i], MouseCursor.SlideArrow);
                }
            }
        }
        else {
            selected = (isTrackSelected);
            foreach(AMKey key in _track.keys) {
                if(!key) continue;

                //_track.sortKeys();
                if(key.frame < aData.getCurrentTake().startFrame) continue;
                if(key.frame > aData.getCurrentTake().endFrame) break;
                Rect rectFrame = new Rect(current_width_frame * (key.frame - aData.getCurrentTake().startFrame), 0f, current_width_frame, _current_height_frame);
                GUI.DrawTexture(new Rect(rectFrame.x + 2f, rectFrame.y + rectFrame.height - (rectFrame.width - 4f) - 2f, rectFrame.width - 4f, rectFrame.width - 4f), texFrKey);
            }
        }
        // click on empty frames
        if(GUI.Button(rectFramesBirdsEye, "", "label") && dragType == (int)DragType.None) {
            int prevFrame = aData.getCurrentTake().selectedFrame;
            bool clickedOnBirdsEyeKey = false;
            for(int i = birdseyeKeyFrames.Count - 1; i >= 0; i--) {
                if(birdseyeKeyFrames[i] > (int)aData.getCurrentTake().endFrame) continue;
                if(birdseyeKeyFrames[i] < (int)aData.getCurrentTake().startFrame) break;
                if(birdseyeKeyRects[i].Contains(e.mousePosition)) {
                    clickedOnBirdsEyeKey = true;
                    // left click
                    if(e.button == 0) {
                        // select the frame
                        timelineSelectFrame(t, birdseyeKeyFrames[i]);
                        // add frame to context selection
                        contextSelectFrame(birdseyeKeyFrames[i], prevFrame);
                        // right click
                    }
                    else if(e.button == 1) {

                        // select track
                        timelineSelectTrack(t);
                        // if context selection is empty, select frame
                        buildContextMenu(birdseyeKeyFrames[i]);
                        // show context menu
                        contextMenu.ShowAsContext();
                    }
                    break;
                }
            }
            if(!clickedOnBirdsEyeKey) {
                int _frame_num_birdseye = (int)aData.getCurrentTake().startFrame + Mathf.CeilToInt(e.mousePosition.x / current_width_frame) - 1;
                // left click
                if(e.button == 0) {
                    // select the frame
                    timelineSelectFrame(t, _frame_num_birdseye);
                    // add frame to context selection
                    contextSelectFrame(_frame_num_birdseye, prevFrame);
                    // right click
                }
                else if(e.button == 1) {
                    timelineSelectTrack(t);
                    // if context selection is empty, select frame
                    buildContextMenu(_frame_num_birdseye);
                    // show context menu
                    contextMenu.ShowAsContext();
                }
            }
        }
        if(!isRenamingTake && isRenamingTrack == -1 && mouseOverFrame == 0 && e.mousePosition.x >= rectFramesBirdsEye.x && e.mousePosition.x <= (rectFramesBirdsEye.x + rectFramesBirdsEye.width)) {
            if(rectFramesBirdsEye.Contains(e.mousePosition) && mouseOverElement == (int)ElementType.None) {
                mouseOverFrame = mouseXOverFrame;
                mouseOverTrack = t;
            }
            mouseOverSelectedFrame = ((isTrackSelected) && aData.getCurrentTake().isFrameSelected(mouseXOverFrame));
        }
        #endregion
        if(!oData.disableTimelineActions && _track.foldout) {
            #region timeline actions
            //AudioClip audioClip = null;
            bool drawEachAction = false;
            if(_track is AMAnimationTrack || _track is AMAudioTrack) drawEachAction = true;	// draw each action with seperate textures and buttons for these tracks
            int _startFrame = (int)aData.getCurrentTake().startFrame;
            int _endFrame = (int)(_startFrame + numFrames - 1);
            int action_startFrame, action_endFrame, renderFrameStart, renderFrameEnd;
            int cached_action_startFrame = -1, cached_action_endFrame = -1;
            Texture texBox = texBoxBorder;
            #region group textures / buttons (performance increase)
            Rect rectTimelineActions = new Rect(0f, _current_height_frame, 0f, height_track - current_height_frame);	// used to group textures into one draw call
            if(!drawEachAction) {
                if(_track.keys.Count > 0) {
                    if(_track is AMTranslationTrack && _track.keys.Count > 1 && _track.keys[0] && _track.keys[_track.keys.Count - 1]) {
                        // translation track, from first action frame to end action frame
                        cached_action_startFrame = _track.keys[0].getStartFrame();
                        cached_action_endFrame = (_track.keys[_track.keys.Count - 1] as AMTranslationKey).endFrame;
                        texBox = texBoxGreen;
                    }
                    else if(_track is AMRotationTrack && _track.keys.Count > 1 && _track.keys[0] && _track.keys[_track.keys.Count - 1]) {
                        // rotation track, from first action start frame to last action start frame
                        cached_action_startFrame = _track.keys[0].getStartFrame();
                        cached_action_endFrame = _track.keys[_track.keys.Count - 1].getStartFrame();
                        texBox = texBoxYellow;
                    }
                    else if(_track is AMOrientationTrack && _track.keys.Count > 1 && _track.keys[0] && _track.keys[_track.keys.Count - 1]) {
                        // orientation track, from first action start frame to last action start frame
                        cached_action_startFrame = _track.keys[0].getStartFrame();
                        cached_action_endFrame = _track.keys[_track.keys.Count - 1].getStartFrame();
                        texBox = texBoxOrange;
                    }
                    else if(_track is AMPropertyTrack) {
                        // property track, full track width
                        cached_action_startFrame = _startFrame;
                        cached_action_endFrame = _endFrame;
                        texBox = texBoxLightBlue;
                    }
                    else if(_track is AMEventTrack && _track.keys[0]) {
                        // event track, from first action start frame to end frame
                        cached_action_startFrame = _track.keys[0].getStartFrame();
                        cached_action_endFrame = _endFrame;
                        texBox = texBoxDarkBlue;
                    }
                    else if(_track is AMGOSetActiveTrack && _track.keys[0]) {
                        // go set active track, from first action start frame to end frame
                        cached_action_startFrame = _track.keys[0].getStartFrame();
                        cached_action_endFrame = _endFrame;
                        texBox = texBoxDarkBlue;
                    }
                }
                if(cached_action_startFrame > 0 && cached_action_endFrame > 0) {
                    if(cached_action_startFrame <= _startFrame) {
                        rectTimelineActions.x = 0f;
                    }
                    else {
                        rectTimelineActions.x = (cached_action_startFrame - _startFrame) * current_width_frame;
                    }
                    if(cached_action_endFrame >= _endFrame) {
                        rectTimelineActions.width = rectFramesBirdsEye.width;
                    }
                    else {
                        rectTimelineActions.width = (cached_action_endFrame - (_startFrame >= cached_action_startFrame ? _startFrame : cached_action_startFrame) + 1) * current_width_frame;
                    }
                    // draw timeline action texture

                    if(rectTimelineActions.width > 0f) GUI.DrawTexture(rectTimelineActions, texBox);
                }

            }
            #endregion
            string txtInfo;
            Rect rectBox;
            // draw box for each action in track
            bool didClampBackwards = false;	// whether or not clamped backwards, used to break infinite loop
            int last_action_startFrame = -1;
            for(int i = 0; i < _track.keys.Count; i++) {
                if(_track.keys[i] == null) continue;

                #region calculate dimensions
                int clamped = 0; // 0 = no clamp, -1 = backwards clamp, 1 = forwards clamp
                if(_track.keys[i].version != _track.version) {
                    // if cache is null, recheck for component and update caches
                    //aData = (AnimatorData)GameObject.Find("AnimatorData").GetComponent("AnimatorData");
                    aData.getCurrentTake().maintainCaches();
                }
                if((_track is AMAudioTrack) && ((_track.keys[i] as AMAudioKey).getNumberOfFrames(aData.getCurrentTake().frameRate)) > -1 && (_track.keys[i].getStartFrame() + (_track.keys[i] as AMAudioKey).getNumberOfFrames(aData.getCurrentTake().frameRate) <= aData.getCurrentTake().numFrames)) {
                    // based on audio clip length
                    action_startFrame = _track.keys[i].getStartFrame();
                    action_endFrame = _track.keys[i].getStartFrame() + (_track.keys[i] as AMAudioKey).getNumberOfFrames(aData.getCurrentTake().frameRate);
                    //audioClip = (_track.cache[i] as AMAudioAction).audioClip;
                    // if intersects new audio clip, then cut
                    if(i < _track.keys.Count - 1) {
                        if(action_endFrame > _track.keys[i + 1].getStartFrame()) action_endFrame = _track.keys[i + 1].getStartFrame();
                    }
                }
                else if((_track is AMAnimationTrack) && ((_track.keys[i] as AMAnimationKey).getNumberOfFrames(aData.getCurrentTake().frameRate)) > -1 && (_track.keys[i].getStartFrame() + (_track.keys[i] as AMAnimationKey).getNumberOfFrames(aData.getCurrentTake().frameRate) <= aData.getCurrentTake().numFrames)) {
                    // based on animation clip length
                    action_startFrame = _track.keys[i].getStartFrame();
                    action_endFrame = _track.keys[i].getStartFrame() + (_track.keys[i] as AMAnimationKey).getNumberOfFrames(aData.getCurrentTake().frameRate);
                    // if intersects new animation clip, then cut
                    if(i < _track.keys.Count - 1) {
                        if(action_endFrame > _track.keys[i + 1].getStartFrame()) action_endFrame = _track.keys[i + 1].getStartFrame();
                    }
                }
                else if((i == 0) && (!didClampBackwards) && (_track is AMPropertyTrack || _track is AMGOSetActiveTrack)) {
                    // clamp behind if first action
                    action_startFrame = 1;
                    action_endFrame = _track.keys[0].getStartFrame();
                    i--;
                    didClampBackwards = true;
                    clamped = -1;
                }
                else if((_track is AMAnimationTrack) || (_track is AMAudioTrack) || (_track is AMPropertyTrack) || (_track is AMEventTrack) || (_track is AMGOSetActiveTrack)) {
                    // single frame tracks (clamp box to last frame) (if audio track not set, clamp)
                    action_startFrame = _track.keys[i].getStartFrame();
                    if(i < _track.keys.Count - 1) {
                        action_endFrame = _track.keys[i + 1].getStartFrame();
                    }
                    else {
                        clamped = 1;
                        action_endFrame = _endFrame;
                        if(action_endFrame > aData.getCurrentTake().numFrames) action_endFrame = aData.getCurrentTake().numFrames + 1;
                    }
                }
                else {
                    // tracks with start frame and end frame (do not clamp box, stop before last key)
                    if(_track.keys[i].getNumberOfFrames() <= 0) continue;
                    action_startFrame = _track.keys[i].getStartFrame();
                    action_endFrame = _track.keys[i].getStartFrame() + _track.keys[i].getNumberOfFrames();
                }
                if(action_startFrame > _endFrame) {
                    last_action_startFrame = action_startFrame;
                    continue;
                }
                if(action_endFrame < _startFrame) {
                    last_action_startFrame = action_startFrame;
                    continue;
                }
                if(i >= 0) txtInfo = getInfoTextForAction(_track, _track.keys[i], false, clamped);
                else txtInfo = getInfoTextForAction(_track, _track.keys[0], true, clamped);
                float rectLeft, rectWidth; ;
                float rectTop = current_height_frame;
                float rectHeight = height_track - current_height_frame;
                // set info box position and dimensions
                bool showLeftAnchor = true;
                bool showRightAnchor = true;
                if(action_startFrame < _startFrame) {
                    rectLeft = 0f;
                    renderFrameStart = _startFrame;
                    showLeftAnchor = false;
                }
                else {
                    rectLeft = (action_startFrame - _startFrame) * current_width_frame;
                    renderFrameStart = action_startFrame;
                }
                if(action_endFrame > _endFrame) {
                    renderFrameEnd = _endFrame;
                    showRightAnchor = false;
                }
                else {
                    renderFrameEnd = action_endFrame;
                }
                rectWidth = (renderFrameEnd - renderFrameStart + 1) * current_width_frame;
                rectBox = new Rect(rectLeft, rectTop, rectWidth, rectHeight);
                #endregion
                #region draw action
                if(_track is AMAnimationTrack) texBox = texBoxRed;
                else if(_track is AMPropertyTrack) texBox = texBoxLightBlue;
                else if(_track is AMTranslationTrack) texBox = texBoxGreen;
                else if(_track is AMAudioTrack) texBox = texBoxPink;
                else if(_track is AMRotationTrack) texBox = texBoxYellow;
                else if(_track is AMOrientationTrack) texBox = texBoxOrange;
                else if(_track is AMEventTrack) texBox = texBoxDarkBlue;
                else if(_track is AMGOSetActiveTrack) texBox = texBoxDarkBlue;
                else texBox = texBoxBorder;
                if(drawEachAction) {
                    GUI.DrawTexture(rectBox, texBox);
                    //if(audioClip) GUI.DrawTexture(rectBox,AssetPreview.GetAssetPreview(audioClip));
                }
                // info tex label
                bool hideTxtInfo = (GUI.skin.label.CalcSize(new GUIContent(txtInfo)).x > rectBox.width);
                GUIStyle styleTxtInfo = new GUIStyle(GUI.skin.label);
                styleTxtInfo.normal.textColor = Color.white;
                styleTxtInfo.alignment = (hideTxtInfo ? TextAnchor.MiddleLeft : TextAnchor.MiddleCenter);
                bool isLastAction;
                if(_track is AMPropertyTrack || _track is AMEventTrack || _track is AMGOSetActiveTrack) isLastAction = (i == _track.keys.Count - 1);
                else if(_track is AMAudioTrack || _track is AMAnimationTrack) isLastAction = false;
                else isLastAction = (i == _track.keys.Count - 2);
                if(rectBox.width > 5f) EditorGUI.DropShadowLabel(new Rect(rectBox.x, rectBox.y, rectBox.width - (!isLastAction ? current_width_frame : 0f), rectBox.height), txtInfo, styleTxtInfo);
                // if clicked on info box, select the starting frame for action. show tooltip if text does not fit
                if(drawEachAction && GUI.Button(rectBox, /*(hideTxtInfo ? new GUIContent("",txtInfo) : new GUIContent(""))*/"", "label") && dragType != (int)DragType.ResizeAction) {
                    int prevFrame = aData.getCurrentTake().selectedFrame;
                    // timeline select
                    timelineSelectFrame(t, (clamped == -1 ? action_endFrame : action_startFrame));
                    // clear and add frame to context selection
                    contextSelectFrame((clamped == -1 ? action_endFrame : action_startFrame), prevFrame);
                }
                if(rectBox.Contains(e.mousePosition) && mouseOverElement == (int)ElementType.None) {
                    mouseOverElement = (int)ElementType.TimelineAction;
                    mouseOverTrack = t;
                    if(hideTxtInfo) tooltip = txtInfo;
                }
                #endregion
                #region draw anchors
                if(showLeftAnchor) {
                    Rect rectBoxAnchorLeft = new Rect(rectBox.x - 1f, rectBox.y, 2f, rectBox.height);
                    GUI.DrawTexture(rectBoxAnchorLeft, texBoxBorder);
                    Rect rectBoxAnchorLeftOffset = new Rect(rectBoxAnchorLeft);
                    rectBoxAnchorLeftOffset.width += 6f;
                    rectBoxAnchorLeftOffset.x -= 3f;
                    // info box anchor cursor
                    if(i >= 0) {
                        EditorGUIUtility.AddCursorRect(new Rect(rectBoxAnchorLeftOffset.x + 1f, rectBoxAnchorLeftOffset.y, rectBoxAnchorLeftOffset.width - 2f, rectBoxAnchorLeftOffset.height), MouseCursor.ResizeHorizontal);
                        if(rectBoxAnchorLeftOffset.Contains(e.mousePosition) && (mouseOverElement == (int)ElementType.None || mouseOverElement == (int)ElementType.TimelineAction)) {
                            mouseOverElement = (int)ElementType.ResizeAction;
                            if(dragType == (int)DragType.None) {
                                if(_track.hasKeyOnFrame(last_action_startFrame)) startResizeActionFrame = last_action_startFrame;
                                else startResizeActionFrame = -1;
                                resizeActionFrame = action_startFrame;
                                if(_track is AMAnimationTrack || _track is AMAudioTrack) {
                                    endResizeActionFrame = _track.getKeyFrameAfterFrame(action_startFrame, false);
                                }
                                else endResizeActionFrame = action_endFrame;
                                mouseOverTrack = t;
                                arrKeyRatiosLeft = _track.getKeyFrameRatiosInBetween(startResizeActionFrame, resizeActionFrame);
                                arrKeyRatiosRight = _track.getKeyFrameRatiosInBetween(resizeActionFrame, endResizeActionFrame);
                                arrKeysLeft = _track.getKeyFramesInBetween(startResizeActionFrame, resizeActionFrame);
                                arrKeysRight = _track.getKeyFramesInBetween(resizeActionFrame, endResizeActionFrame);
                            }
                        }
                    }
                }
                // draw right anchor if last timeline action
                if(showRightAnchor && isLastAction) {
                    Rect rectBoxAnchorRight = new Rect(rectBox.x + rectBox.width - 1f, rectBox.y, 2f, rectBox.height);
                    GUI.DrawTexture(rectBoxAnchorRight, texBoxBorder);
                    Rect rectBoxAnchorRightOffset = new Rect(rectBoxAnchorRight);
                    rectBoxAnchorRightOffset.width += 6f;
                    rectBoxAnchorRightOffset.x -= 3f;
                    EditorGUIUtility.AddCursorRect(new Rect(rectBoxAnchorRightOffset.x + 1f, rectBoxAnchorRightOffset.y, rectBoxAnchorRightOffset.width - 2f, rectBoxAnchorRightOffset.height), MouseCursor.ResizeHorizontal);
                    if(rectBoxAnchorRightOffset.Contains(e.mousePosition) && (mouseOverElement == (int)ElementType.None || mouseOverElement == (int)ElementType.TimelineAction)) {
                        mouseOverElement = (int)ElementType.ResizeAction;
                        if(dragType == (int)DragType.None) {
                            startResizeActionFrame = action_startFrame;
                            resizeActionFrame = action_endFrame;
                            endResizeActionFrame = -1;
                            mouseOverTrack = t;
                            arrKeyRatiosLeft = _track.getKeyFrameRatiosInBetween(startResizeActionFrame, resizeActionFrame);
                            arrKeyRatiosRight = _track.getKeyFrameRatiosInBetween(resizeActionFrame, endResizeActionFrame);
                            arrKeysLeft = _track.getKeyFramesInBetween(startResizeActionFrame, resizeActionFrame);
                            arrKeysRight = _track.getKeyFramesInBetween(resizeActionFrame, endResizeActionFrame);
                        }
                    }
                }
                #endregion
                last_action_startFrame = action_startFrame;
            }
            if(!drawEachAction) {
                // timeline action button
                if(GUI.Button(rectTimelineActions,/*new GUIContent("",tooltip)*/"", "label") && dragType == (int)DragType.None) {
                    int _frame_num_action = (int)aData.getCurrentTake().startFrame + Mathf.CeilToInt(e.mousePosition.x / current_width_frame) - 1;
                    AMKey _action = _track.getKeyContainingFrame(_frame_num_action);
                    int prevFrame = aData.getCurrentTake().selectedFrame;
                    // timeline select
                    timelineSelectFrame(t, _action.getStartFrame());
                    // clear and add frame to context selection
                    contextSelectFrame(_action.getStartFrame(), prevFrame);
                }
            }

            #endregion
        }
        GUI.EndGroup();
    }