text: /*
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   *
   * The Original Code is Copyright (C) 2012 Blender Foundation.
   * All rights reserved.
   */
  
  /** \file
   * \ingroup spclip
   */
  
  #include "DNA_scene_types.h"
  
  #include "BLI_math.h"
  #include "BLI_rect.h"
  #include "BLI_utildefines.h"
  
  #include "BKE_context.h"
  #include "BKE_tracking.h"
  
  #include "WM_api.h"
  #include "WM_types.h"
  
  #include "ED_clip.h"
  #include "ED_screen.h"
  
  #include "UI_interface.h"
  
  #include "RNA_access.h"
  #include "RNA_define.h"
  
  #include "UI_view2d.h"
  
  #include "clip_intern.h"  // own include
  
  static bool space_clip_dopesheet_poll(bContext *C)
  {
    if (ED_space_clip_tracking_poll(C)) {
      SpaceClip *sc = CTX_wm_space_clip(C);
  
      if (sc->view == SC_VIEW_DOPESHEET) {
        ARegion *region = CTX_wm_region(C);
  
        return region->regiontype == RGN_TYPE_PREVIEW;
      }
    }
  
    return false;
  }
  
  /********************** select channel operator *********************/
  
  static bool dopesheet_select_channel_poll(bContext *C)
  {
    SpaceClip *sc = CTX_wm_space_clip(C);
  
    if (sc && sc->clip) {
      return sc->view == SC_VIEW_DOPESHEET;
    }
  
    return false;
  }
  
  static int dopesheet_select_channel_exec(bContext *C, wmOperator *op)
  {
    SpaceClip *sc = CTX_wm_space_clip(C);
    MovieClip *clip = ED_space_clip_get_clip(sc);
    MovieTracking *tracking = &clip->tracking;
    MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
    MovieTrackingDopesheet *dopesheet = &tracking->dopesheet;
    MovieTrackingDopesheetChannel *channel;
    ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, object);
    float location[2];
    const bool extend = RNA_boolean_get(op->ptr, "extend");
    int current_channel_index = 0, channel_index;
    const bool show_selected_only = (dopesheet->flag & TRACKING_DOPE_SELECTED_ONLY) != 0;
  
    RNA_float_get_array(op->ptr, "location", location);
    channel_index = -(location[1] - (CHANNEL_FIRST + CHANNEL_HEIGHT_HALF)) / CHANNEL_STEP;
  
    for (channel = dopesheet->channels.first; channel; channel = channel->next) {
      MovieTrackingTrack *track = channel->track;
  
      if (current_channel_index == channel_index) {
        if (extend) {
          track->flag ^= TRACK_DOPE_SEL;
        }
        else {
          track->flag |= TRACK_DOPE_SEL;
        }
  
        if (track->flag & TRACK_DOPE_SEL) {
          tracking->act_track = track;
          BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true);
        }
        else if (show_selected_only == false) {
          BKE_tracking_track_deselect(track, TRACK_AREA_ALL);
        }
      }
      else if (!extend) {
        track->flag &= ~TRACK_DOPE_SEL;
      }
  
      current_channel_index++;
    }
  
    WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
  
    return OPERATOR_FINISHED;
  }
  
  static int dopesheet_select_channel_invoke(bContext *C, wmOperator *op, const wmEvent *event)
  {
    ARegion *region = CTX_wm_region(C);
    float location[2];
  
    UI_view2d_region_to_view(
        &region->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
    RNA_float_set_array(op->ptr, "location", location);
  
    return dopesheet_select_channel_exec(C, op);
  }
  
  void CLIP_OT_dopesheet_select_channel(wmOperatorType *ot)
  {
    /* identifiers */
    ot->name = "Select Channel";
    ot->description = "Select movie tracking channel";
    ot->idname = "CLIP_OT_dopesheet_select_channel";
  
    /* api callbacks */
    ot->invoke = dopesheet_select_channel_invoke;
    ot->exec = dopesheet_select_channel_exec;
    ot->poll = dopesheet_select_channel_poll;
  
    /* flags */
    ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
  
    /* properties */
    RNA_def_float_vector(ot->srna,
                         "location",
                         2,
                         NULL,
                         -FLT_MAX,
                         FLT_MAX,
                         "Location",
                         "Mouse location to select channel",
                         -100.0f,
                         100.0f);
    RNA_def_boolean(ot->srna,
                    "extend",
                    0,
                    "Extend",
                    "Extend selection rather than clearing the existing selection");
  }
  
  /********************** View All operator *********************/
  
  static int dopesheet_view_all_exec(bContext *C, wmOperator *UNUSED(op))
  {
    SpaceClip *sc = CTX_wm_space_clip(C);
    ARegion *region = CTX_wm_region(C);
    View2D *v2d = &region->v2d;
    MovieClip *clip = ED_space_clip_get_clip(sc);
    MovieTracking *tracking = &clip->tracking;
    MovieTrackingDopesheet *dopesheet = &tracking->dopesheet;
    MovieTrackingDopesheetChannel *channel;
    int frame_min = INT_MAX, frame_max = INT_MIN;
  
    for (channel = dopesheet->channels.first; channel; channel = channel->next) {
      frame_min = min_ii(frame_min, channel->segments[0]);
      frame_max = max_ii(frame_max, channel->segments[channel->tot_segment]);
    }
  
    if (frame_min < frame_max) {
      float extra;
  
      v2d->cur.xmin = frame_min;
      v2d->cur.xmax = frame_max;
  
      /* we need an extra "buffer" factor on either side so that the endpoints are visible */
      extra = 0.01f * BLI_rctf_size_x(&v2d->cur);
      v2d->cur.xmin -= extra;
      v2d->cur.xmax += extra;
  
      ED_region_tag_redraw(region);
    }
  
    return OPERATOR_FINISHED;
  }
  
  void CLIP_OT_dopesheet_view_all(wmOperatorType *ot)
  {
    /* identifiers */
    ot->name = "View All";
    ot->description = "Reset viewable area to show full keyframe range";
    ot->idname = "CLIP_OT_dopesheet_view_all";
  
    /* api callbacks */
    ot->exec = dopesheet_view_all_exec;
    ot->poll = space_clip_dopesheet_poll;
  
    /* flags */
    ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
  }
  