Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "toonzqt/functionpanel.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzQt includes
Toshihiro Shimizu 890ddd
#include "toonzqt/functionselection.h"
Toshihiro Shimizu 890ddd
#include "toonzqt/functionsegmentviewer.h"
Toshihiro Shimizu 890ddd
#include "toonzqt/imageutils.h"
Toshihiro Shimizu 890ddd
#include "functionpaneltools.h"
Toshihiro Shimizu 890ddd
#include "toonzqt/gutil.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzLib includes
Toshihiro Shimizu 890ddd
#include "toonz/tframehandle.h"
Toshihiro Shimizu 890ddd
#include "toonz/doubleparamcmd.h"
Toshihiro Shimizu 890ddd
#include "toonz/toonzfolders.h"
Jeremy Bullock 807052
#include "toonz/preferences.h"
Toshihiro Shimizu 890ddd
// TnzBase includes
Toshihiro Shimizu 890ddd
#include "tdoubleparam.h"
Toshihiro Shimizu 890ddd
#include "tdoublekeyframe.h"
Toshihiro Shimizu 890ddd
#include "tunit.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzCore includes
Toshihiro Shimizu 890ddd
#include "tcommon.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// Qt includes
Toshihiro Shimizu 890ddd
#include <qpainter></qpainter>
Christophe Giboudeaux 3ccd50
#include <qpainterpath></qpainterpath>
Toshihiro Shimizu 890ddd
#include <qmouseevent></qmouseevent>
Toshihiro Shimizu 890ddd
#include <qwheelevent></qwheelevent>
Toshihiro Shimizu 890ddd
#include <qmenu></qmenu>
Toshihiro Shimizu 890ddd
#include <qsettings></qsettings>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 61dd76
#include <cmath></cmath>
Shinya Kitaoka 61dd76
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void drawCircle(QPainter &painter, double x, double y, double r) {
Shinya Kitaoka 120a6e
  painter.drawEllipse(x - r, y - r, 2 * r, 2 * r);
Toshihiro Shimizu 890ddd
}
Shinya Kitaoka 120a6e
void drawCircle(QPainter &painter, const QPointF &p, double r) {
Shinya Kitaoka 120a6e
  painter.drawEllipse(p.x() - r, p.y() - r, 2 * r, 2 * r);
Toshihiro Shimizu 890ddd
}
Shinya Kitaoka 120a6e
void drawSquare(QPainter &painter, double x, double y, double r) {
Shinya Kitaoka 120a6e
  painter.drawRect(x - r, y - r, 2 * r, 2 * r);
Toshihiro Shimizu 890ddd
}
Shinya Kitaoka 120a6e
void drawSquare(QPainter &painter, const QPointF &p, double r) {
Shinya Kitaoka 120a6e
  drawSquare(painter, p.x(), p.y(), r);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void drawRoundedSquare(QPainter &painter, const QPointF &p, double r) {
Shinya Kitaoka 120a6e
  painter.drawRoundRect(p.x() - r, p.y() - r, 2 * r, 2 * r, 99, 99);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
double norm2(const QPointF &p) { return p.x() * p.x() + p.y() * p.y(); }
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class FunctionPanelZoomer final : public ImageUtils::ShortcutZoomer {
Shinya Kitaoka 120a6e
  FunctionPanel *m_panel;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  FunctionPanelZoomer(FunctionPanel *panel)
Shinya Kitaoka 120a6e
      : ShortcutZoomer(panel), m_panel(panel) {}
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  bool zoom(bool zoomin, bool resetZoom) override {
Shinya Kitaoka 120a6e
    if (resetZoom)
Shinya Kitaoka 120a6e
      m_panel->fitGraphToWindow();
Shinya Kitaoka 120a6e
    else {
Shinya Kitaoka 120a6e
      double f  = 1.25;
Shinya Kitaoka 120a6e
      double sc = zoomin ? f : 1.0 / f;
Shinya Kitaoka 120a6e
      QPoint center(m_panel->width() / 2, m_panel->height() / 2);
Shinya Kitaoka 120a6e
      m_panel->zoom(sc, sc, center);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    return true;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=============================================================================
Toshihiro Shimizu 890ddd
//
Toshihiro Shimizu 890ddd
// Ruler
Toshihiro Shimizu 890ddd
//
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class Ruler {
Shinya Kitaoka 120a6e
  double m_minValue, m_step;
Shinya Kitaoka 120a6e
  int m_labelPeriod, m_labelOffset, m_tickCount;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double m_unit, m_pan, m_vOrigin;
Shinya Kitaoka 120a6e
  int m_x0, m_x1;
Shinya Kitaoka 120a6e
  int m_minLabelDistance, m_minDistance;
Shinya Kitaoka 120a6e
  double m_minStep;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  Ruler();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // unit,pan define the world to viewport transformation: pixel = value * unit
Shinya Kitaoka 120a6e
  // + pan
Shinya Kitaoka 120a6e
  // note: unit can be <0 (e.g. in the vertical rulers)
Shinya Kitaoka 120a6e
  // use vOrigin!=0 when there is a offset between values and labels
Shinya Kitaoka 120a6e
  // (e.g. frame=0 is visualized as 1)
Shinya Kitaoka 120a6e
  void setTransform(double unit, double pan, double vOrigin = 0) {
Shinya Kitaoka 120a6e
    m_unit    = unit;
Shinya Kitaoka 120a6e
    m_pan     = pan;
Shinya Kitaoka 120a6e
    m_vOrigin = vOrigin;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void setRange(int x0, int x1) {
Shinya Kitaoka 120a6e
    m_x0 = x0;
Shinya Kitaoka 120a6e
    m_x1 = x1;
Shinya Kitaoka 120a6e
  }  // [x0,x1] is the pixel range
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // set minimum distance (pixel) between two consecutive labels
Shinya Kitaoka 120a6e
  void setMinLabelDistance(int distance) { m_minLabelDistance = distance; }
Shinya Kitaoka 120a6e
  // set minimum distance (pixel) between two consecutive ticks
Shinya Kitaoka 120a6e
  void setMinDistance(int distance) { m_minDistance = distance; }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // use setMinStep to define a minimum tick (e.g. for integer rulers as 'frame'
Shinya Kitaoka 120a6e
  // call setMinStep(1);)
Shinya Kitaoka 120a6e
  void setMinStep(double step) { m_minStep = step; }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void compute();  // call compute() once, before calling the following methods
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int getTickCount() const { return m_tickCount; }
Shinya Kitaoka 120a6e
  double getTick(int index) const { return m_minValue + index * m_step; }
Shinya Kitaoka 120a6e
  bool isLabel(int index) const {
Shinya Kitaoka 120a6e
    return ((m_labelOffset + index) % m_labelPeriod) == 0;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Ruler::Ruler()
Shinya Kitaoka 120a6e
    : m_minValue(0)
Shinya Kitaoka 120a6e
    , m_step(1)
Shinya Kitaoka 120a6e
    , m_labelPeriod(2)
Shinya Kitaoka 120a6e
    , m_labelOffset(0)
Shinya Kitaoka 120a6e
    , m_tickCount(0)
Shinya Kitaoka 120a6e
    , m_unit(1)
Shinya Kitaoka 120a6e
    , m_pan(0)
Shinya Kitaoka 120a6e
    , m_x0(0)
Shinya Kitaoka 120a6e
    , m_x1(100)
Shinya Kitaoka 120a6e
    , m_minLabelDistance(20)
Shinya Kitaoka 120a6e
    , m_minDistance(5)
Shinya Kitaoka 120a6e
    , m_minStep(0) {}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void Ruler::compute() {
Shinya Kitaoka 120a6e
  assert(m_x0 < m_x1);
Shinya Kitaoka 120a6e
  assert(m_unit != 0.0);
Shinya Kitaoka 120a6e
  assert(m_minLabelDistance > 0);
Shinya Kitaoka 120a6e
  assert(m_minDistance >= 0);
Shinya Kitaoka 120a6e
  // compute m_step (world distance between two adjacent ticks)
Shinya Kitaoka 120a6e
  // and m_labelPeriod (number of ticks between two adjacent labels)
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // the distance (world units) between two labels must be >=
Shinya Kitaoka 120a6e
  // minLabelWorldDistance
Shinya Kitaoka 120a6e
  const double absUnit               = std::abs(m_unit);
Shinya Kitaoka 120a6e
  const double minLabelWorldDistance = m_minLabelDistance / absUnit;
Shinya Kitaoka 120a6e
  const double minWorldDistance      = m_minDistance / absUnit;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // we want the minimum step with:
Shinya Kitaoka 120a6e
  //    step*labelPeriod (i.e. label distance) >=  minLabelWorldDistance
Shinya Kitaoka 120a6e
  //    step (i.e. tick distance) >= minWorldDistance
Shinya Kitaoka 120a6e
  // Note: labelPeriod alternates between 5 and 2 => labelPeriod' =
Shinya Kitaoka 120a6e
  // 7-labelPeriod
Shinya Kitaoka 120a6e
  m_step        = 1;
Shinya Kitaoka 120a6e
  m_labelPeriod = 5;
Shinya Kitaoka 120a6e
  if (m_step * m_labelPeriod >= minLabelWorldDistance &&
Shinya Kitaoka 120a6e
      m_step >= minWorldDistance) {
Shinya Kitaoka 120a6e
    while (m_step >= minLabelWorldDistance &&
Shinya Kitaoka 120a6e
           m_step / (7 - m_labelPeriod) >= minWorldDistance) {
Shinya Kitaoka 120a6e
      m_labelPeriod = 7 - m_labelPeriod;
Shinya Kitaoka 120a6e
      m_step /= m_labelPeriod;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    do {
Shinya Kitaoka 120a6e
      m_step *= m_labelPeriod;
Shinya Kitaoka 120a6e
      m_labelPeriod =
Shinya Kitaoka 120a6e
          7 - m_labelPeriod;  // m_labelPeriod alternates between 5 and 2
Shinya Kitaoka 120a6e
    } while (m_step * m_labelPeriod < minLabelWorldDistance ||
Shinya Kitaoka 120a6e
             m_step < minWorldDistance);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (m_step >= minLabelWorldDistance) {
Shinya Kitaoka 120a6e
    m_labelPeriod = 1;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (m_step * m_labelPeriod < m_minStep) {
Shinya Kitaoka 120a6e
    m_step        = m_minStep;
Shinya Kitaoka 120a6e
    m_labelPeriod = 1;
Shinya Kitaoka 120a6e
  } else if (m_step < m_minStep) {
Shinya Kitaoka 120a6e
    m_step *= m_labelPeriod;
Shinya Kitaoka 120a6e
    m_labelPeriod = 1;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // compute range
Shinya Kitaoka 120a6e
  double v0 = (m_x0 - m_pan) / m_unit;  // left margin (world units)
Shinya Kitaoka 120a6e
  double v1 = (m_x1 - m_pan) / m_unit;  // right margin (world units)
otakuto ed7dcd
  if (m_unit < 0) std::swap(v0, v1);
Shinya Kitaoka 120a6e
  int i0 =
Shinya Kitaoka 120a6e
      tfloor((v0 - m_vOrigin) / m_step);  // largest tick <=v0 is i0 * m_step
Shinya Kitaoka 120a6e
  int i1 =
Shinya Kitaoka 120a6e
      tceil((v1 - m_vOrigin) / m_step);  // smallest tick >=v1 is i1 * m_step
Shinya Kitaoka 120a6e
  m_minValue  = i0 * m_step + m_vOrigin;
Shinya Kitaoka 120a6e
  m_tickCount = i1 - i0 + 1;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_labelOffset = i0 >= 0 ? (i0 % m_labelPeriod)
Shinya Kitaoka 120a6e
                          : (m_labelPeriod - ((-i0) % m_labelPeriod));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=============================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=============================================================================
Toshihiro Shimizu 890ddd
//
Toshihiro Shimizu 890ddd
// FunctionPanel::Gadget
Toshihiro Shimizu 890ddd
//
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
FunctionPanel::Gadget::Gadget(FunctionPanel::Handle handle, int kIndex,
Shinya Kitaoka 120a6e
                              const QPointF &p, int rx, int ry,
Shinya Kitaoka 120a6e
                              const QPointF &pointPos)
Shinya Kitaoka 120a6e
    : m_handle(handle)
Shinya Kitaoka 120a6e
    , m_kIndex(kIndex)
Shinya Kitaoka 120a6e
    , m_hitRegion(QRect((int)p.x() - rx, (int)p.y() - ry, 2 * rx, 2 * ry))
Shinya Kitaoka 120a6e
    , m_pos(p)
Shinya Kitaoka 120a6e
    , m_pointPos(pointPos)
Shinya Kitaoka 120a6e
    , m_channel(0)
Shinya Kitaoka 120a6e
    , m_keyframePosition(0) {}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=============================================================================
Toshihiro Shimizu 890ddd
//
Toshihiro Shimizu 890ddd
// FunctionPanel
Toshihiro Shimizu 890ddd
//
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Jeremy Bullock 4ce953
FunctionPanel::FunctionPanel(QWidget *parent, bool isFloating)
Shinya Kitaoka 120a6e
    : QDialog(parent)
Shinya Kitaoka 120a6e
    , m_functionTreeModel(0)
Shinya Kitaoka 120a6e
    , m_viewTransform()
Shinya Kitaoka 120a6e
    , m_valueAxisX(50)
Shinya Kitaoka 120a6e
    , m_frameAxisY(50)
Shinya Kitaoka 120a6e
    , m_graphViewportY(50)
Shinya Kitaoka 120a6e
    , m_frameHandle(0)
Shinya Kitaoka 120a6e
    , m_dragTool(0)
Shinya Kitaoka 120a6e
    , m_currentFrameStatus(0)
Shinya Kitaoka 120a6e
    , m_selection(0)
Jeremy Bullock 4ce953
    , m_curveShape(SMOOTH)
Jeremy Bullock 4ce953
    , m_isFloating(isFloating) {
Shinya Kitaoka 120a6e
  setWindowTitle(tr("Function Curves"));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_viewTransform.translate(50, 200);
Shinya Kitaoka 120a6e
  m_viewTransform.scale(5, -1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  setFocusPolicy(Qt::ClickFocus);
Shinya Kitaoka 120a6e
  setMouseTracking(true);
Shinya Kitaoka 120a6e
  m_highlighted.handle = None;
Shinya Kitaoka 120a6e
  m_highlighted.gIndex = -1;
Shinya Kitaoka 120a6e
  m_cursor.visible     = false;
Shinya Kitaoka 120a6e
  m_cursor.frame = m_cursor.value = 0;
Shinya Kitaoka 120a6e
  m_curveLabel.text               = "";
Shinya Kitaoka 120a6e
  m_curveLabel.curve              = 0;
Shinya Kitaoka 120a6e
Jeremy Bullock 4ce953
  if (m_isFloating) {
Jeremy Bullock 4ce953
    // load the dialog size
shun-iwasawa 784640
    TFilePath fp(ToonzFolder::getMyModuleDir() + TFilePath("popups.ini"));
shun-iwasawa 784640
    QSettings settings(toQString(fp), QSettings::IniFormat);
Shinya Kitaoka 120a6e
Jeremy Bullock 4ce953
    setGeometry(
shun-iwasawa 784640
        settings.value("FunctionCurves", QRect(500, 500, 400, 300)).toRect());
Jeremy Bullock 4ce953
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
FunctionPanel::~FunctionPanel() {
Jeremy Bullock 4ce953
  if (m_isFloating) {
Jeremy Bullock 4ce953
    // save the dialog size
shun-iwasawa 784640
    TFilePath fp(ToonzFolder::getMyModuleDir() + TFilePath("popups.ini"));
shun-iwasawa 784640
    QSettings settings(toQString(fp), QSettings::IniFormat);
Jeremy Bullock 4ce953
shun-iwasawa 784640
    settings.setValue("FunctionCurves", geometry());
Jeremy Bullock 4ce953
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  delete m_dragTool;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
double FunctionPanel::getPixelRatio(TDoubleParam *curve) const {
Shinya Kitaoka 120a6e
  double framePixelSize = xToFrame(1) - xToFrame(0);
Shinya Kitaoka 120a6e
  assert(framePixelSize > 0);
Shinya Kitaoka 120a6e
  double valuePixelSize = fabs(yToValue(curve, 1) - yToValue(curve, 0));
Shinya Kitaoka 120a6e
  assert(valuePixelSize > 0);
Shinya Kitaoka 120a6e
  return framePixelSize / valuePixelSize;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
double FunctionPanel::frameToX(double f) const {
Shinya Kitaoka 120a6e
  return m_viewTransform.m11() * f + m_viewTransform.dx();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
double FunctionPanel::xToFrame(double x) const {
Shinya Kitaoka 120a6e
  return (x - m_viewTransform.dx()) / m_viewTransform.m11();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
double FunctionPanel::valueToY(TDoubleParam *curve, double v) const {
Shinya Kitaoka 120a6e
  const double bigNumber = 1.0e9;
Shinya Kitaoka 120a6e
  TMeasure *m            = curve->getMeasure();
Shinya Kitaoka 120a6e
  if (m) {
Shinya Kitaoka 120a6e
    const TUnit *unit = m->getCurrentUnit();
Shinya Kitaoka 120a6e
    v                 = unit->convertTo(v);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  return tcrop(m_viewTransform.m22() * v + m_viewTransform.dy(), -bigNumber,
Shinya Kitaoka 120a6e
               bigNumber);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
double FunctionPanel::yToValue(TDoubleParam *curve, double y) const {
Shinya Kitaoka 120a6e
  double v    = (y - m_viewTransform.dy()) / m_viewTransform.m22();
Shinya Kitaoka 120a6e
  TMeasure *m = curve->getMeasure();
Shinya Kitaoka 120a6e
  if (m) {
Shinya Kitaoka 120a6e
    const TUnit *unit = m->getCurrentUnit();
Shinya Kitaoka 120a6e
    v                 = unit->convertFrom(v);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  return v;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::pan(int dx, int dy) {
Shinya Kitaoka 120a6e
  QTransform m;
Shinya Kitaoka 120a6e
  m.translate(dx, dy);
Shinya Kitaoka 120a6e
  m_viewTransform *= m;
Shinya Kitaoka 120a6e
  update();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::zoom(double sx, double sy, const QPoint ¢er) {
Shinya Kitaoka 120a6e
  QTransform m;
Shinya Kitaoka 120a6e
  m.translate(center.x(), center.y());
Shinya Kitaoka 120a6e
  m.scale(sx, sy);
Shinya Kitaoka 120a6e
  m.translate(-center.x(), -center.y());
Shinya Kitaoka 120a6e
  m_viewTransform *= m;
Shinya Kitaoka 120a6e
  update();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
QPointF FunctionPanel::getWinPos(TDoubleParam *curve, double frame,
Shinya Kitaoka 120a6e
                                 double value) const {
Shinya Kitaoka 120a6e
  return QPointF(frameToX(frame), valueToY(curve, value));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
QPointF FunctionPanel::getWinPos(TDoubleParam *curve, double frame) const {
Shinya Kitaoka 120a6e
  return getWinPos(curve, frame, curve->getValue(frame));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
QPointF FunctionPanel::getWinPos(TDoubleParam *curve,
Shinya Kitaoka 120a6e
                                 const TDoubleKeyframe &kf) const {
Shinya Kitaoka 120a6e
  return getWinPos(curve, kf.m_frame, kf.m_value);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
int FunctionPanel::getCurveDistance(TDoubleParam *curve, const QPoint &winPos) {
Shinya Kitaoka 120a6e
  double frame  = xToFrame(winPos.x());
Shinya Kitaoka 120a6e
  double value  = curve->getValue(frame);
Shinya Kitaoka 120a6e
  double curveY = valueToY(curve, value);
Shinya Kitaoka 120a6e
  return std::abs(curveY - winPos.y());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
FunctionTreeModel::Channel *FunctionPanel::findClosestChannel(
Shinya Kitaoka 120a6e
    const QPoint &winPos, int maxWinDistance) {
Shinya Kitaoka 120a6e
  FunctionTreeModel::Channel *closestChannel = 0;
Shinya Kitaoka 120a6e
  int minDistance                            = maxWinDistance;
Shinya Kitaoka 120a6e
  int i;
Shinya Kitaoka 120a6e
  for (i = 0; i < m_functionTreeModel->getActiveChannelCount(); i++) {
Shinya Kitaoka 120a6e
    FunctionTreeModel::Channel *channel =
Shinya Kitaoka 120a6e
        m_functionTreeModel->getActiveChannel(i);
Shinya Kitaoka 120a6e
    TDoubleParam *curve = channel->getParam();
Shinya Kitaoka 120a6e
    int distance        = getCurveDistance(curve, winPos);
Shinya Kitaoka 120a6e
    if (distance < minDistance) {
Shinya Kitaoka 120a6e
      closestChannel = channel;
Shinya Kitaoka 120a6e
      minDistance    = distance;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  return closestChannel;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TDoubleParam *FunctionPanel::findClosestCurve(const QPoint &winPos,
Shinya Kitaoka 120a6e
                                              int maxWinDistance) {
Shinya Kitaoka 120a6e
  FunctionTreeModel::Channel *closestChannel =
Shinya Kitaoka 120a6e
      findClosestChannel(winPos, maxWinDistance);
Shinya Kitaoka 120a6e
  return closestChannel ? closestChannel->getParam() : 0;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// return the gadget index (-1 if no gadget is close enough)
Shinya Kitaoka 120a6e
int FunctionPanel::findClosestGadget(const QPoint &winPos, Handle &handle,
Shinya Kitaoka 120a6e
                                     int maxDistance) {
Shinya Kitaoka 120a6e
  // search only handles close enough (i.e. distance
Shinya Kitaoka 120a6e
  int minDistance = maxDistance;
Shinya Kitaoka 120a6e
  int k           = -1;
Shinya Kitaoka 120a6e
  for (int i = 0; i < m_gadgets.size(); i++) {
Shinya Kitaoka 120a6e
    if (m_gadgets[i].m_hitRegion.contains(winPos)) {
Shinya Kitaoka 120a6e
      QPoint p;
Shinya Kitaoka 120a6e
      double d = (m_gadgets[i].m_hitRegion.center() - winPos).manhattanLength();
Shinya Kitaoka 120a6e
      if (d < minDistance) {
Shinya Kitaoka 120a6e
        k           = i;
Shinya Kitaoka 120a6e
        minDistance = d;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  if (k >= 0) {
Shinya Kitaoka 120a6e
    handle = m_gadgets[k].m_handle;
Shinya Kitaoka 120a6e
    return k;  // m_gadgets[k].m_kIndex;
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    handle = None;
Shinya Kitaoka 120a6e
    return -1;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TDoubleParam *FunctionPanel::getCurrentCurve() const {
Shinya Kitaoka 120a6e
  FunctionTreeModel::Channel *currentChannel =
Shinya Kitaoka 120a6e
      m_functionTreeModel ? m_functionTreeModel->getCurrentChannel() : 0;
Shinya Kitaoka 120a6e
  if (!currentChannel)
Shinya Kitaoka 120a6e
    return 0;
Shinya Kitaoka 120a6e
  else
Shinya Kitaoka 120a6e
    return currentChannel->getParam();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
QPainterPath FunctionPanel::getSegmentPainterPath(TDoubleParam *curve,
Shinya Kitaoka 120a6e
                                                  int segmentIndex, int x0,
Shinya Kitaoka 120a6e
                                                  int x1) {
Shinya Kitaoka 120a6e
  double frame0 = xToFrame(x0), frame1 = xToFrame(x1);
Shinya Kitaoka 120a6e
  int kCount = curve->getKeyframeCount();
Shinya Kitaoka 120a6e
  int step   = 1;
Shinya Kitaoka 120a6e
  if (kCount > 0) {
Shinya Kitaoka 120a6e
    if (segmentIndex < 0)
Shinya Kitaoka 120a6e
      frame1 = std::min(
Shinya Kitaoka 120a6e
          frame1, curve->keyframeIndexToFrame(0));  // before first keyframe
Shinya Kitaoka 120a6e
    else if (segmentIndex >= kCount - 1)
Shinya Kitaoka 120a6e
      frame0 = std::max(frame0, curve->keyframeIndexToFrame(
Shinya Kitaoka 120a6e
                                    kCount - 1));  // after last keyframe
Shinya Kitaoka 120a6e
    else {
Shinya Kitaoka 120a6e
      // between keyframes
Shinya Kitaoka 120a6e
      TDoubleKeyframe kf = curve->getKeyframe(segmentIndex);
Shinya Kitaoka 120a6e
      frame0             = std::max(frame0, kf.m_frame);
Shinya Kitaoka 120a6e
      double f           = curve->keyframeIndexToFrame(segmentIndex + 1);
Shinya Kitaoka 120a6e
      frame1             = std::min(frame1, f);
Shinya Kitaoka 120a6e
      step               = kf.m_step;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  if (frame0 >= frame1) return QPainterPath();
Shinya Kitaoka 120a6e
  double frame;
Shinya Kitaoka 120a6e
  double df = xToFrame(3) - xToFrame(0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (m_curveShape == SMOOTH) {
Shinya Kitaoka 120a6e
    frame = frame0;
Shinya Kitaoka 120a6e
  } else  // FRAME_BASED
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    frame = (double)tfloor(frame0);
Shinya Kitaoka 120a6e
    df    = std::max(df, 1.0);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  QPainterPath path;
Shinya Kitaoka 120a6e
  if (0 <= segmentIndex && segmentIndex < kCount && step > 1) {
Shinya Kitaoka 120a6e
    // step>1
Shinya Kitaoka 120a6e
    path.moveTo(getWinPos(curve, frame));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    int f0        = curve->keyframeIndexToFrame(segmentIndex);
Shinya Kitaoka 120a6e
    int vFrame    = f0 + tfloor(tfloor(frame - f0), step);
Shinya Kitaoka 120a6e
    double vValue = curve->getValue(vFrame);
Shinya Kitaoka 120a6e
    assert(vFrame <= frame);
Shinya Kitaoka 120a6e
    assert(vFrame + step > frame);
Shinya Kitaoka 120a6e
    while (vFrame + step < frame1) {
Shinya Kitaoka 120a6e
      vValue = curve->getValue(vFrame);
Shinya Kitaoka 120a6e
      path.lineTo(getWinPos(curve, vFrame, vValue));
Shinya Kitaoka 120a6e
      vFrame += step;
Shinya Kitaoka 120a6e
      path.lineTo(getWinPos(curve, vFrame, vValue));
Shinya Kitaoka 120a6e
      vValue = curve->getValue(vFrame);
Shinya Kitaoka 120a6e
      path.lineTo(getWinPos(curve, vFrame, vValue));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    path.lineTo(getWinPos(curve, frame1, vValue));
Shinya Kitaoka 120a6e
    path.lineTo(getWinPos(curve, frame1, curve->getValue(frame1, true)));
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    // step = 1
Shinya Kitaoka 120a6e
    path.moveTo(getWinPos(curve, frame));
Shinya Kitaoka 120a6e
    while (frame + df < frame1) {
Shinya Kitaoka 120a6e
      frame += df;
Shinya Kitaoka 120a6e
      path.lineTo(getWinPos(curve, frame));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    path.lineTo(getWinPos(curve, frame1, curve->getValue(frame1, true)));
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  return path;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::drawCurrentFrame(QPainter &painter) {
Shinya Kitaoka 120a6e
  int currentframe                = 0;
Shinya Kitaoka 120a6e
  if (m_frameHandle) currentframe = m_frameHandle->getFrame();
Shinya Kitaoka 120a6e
  int x                           = frameToX(currentframe);
Shinya Kitaoka 120a6e
  if (m_currentFrameStatus == 0)
Shinya Kitaoka 120a6e
    painter.setPen(Qt::magenta);
Shinya Kitaoka 120a6e
  else if (m_currentFrameStatus == 1)
Shinya Kitaoka 120a6e
    painter.setPen(Qt::white);
Shinya Kitaoka 120a6e
  else
Shinya Kitaoka 120a6e
    painter.setPen(m_selectedColor);
Shinya Kitaoka 120a6e
  int y = m_graphViewportY + 1;
Shinya Kitaoka 120a6e
  painter.drawLine(x - 1, y, x - 1, height());
Shinya Kitaoka 120a6e
  painter.drawLine(x + 1, y, x + 1, height());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::drawFrameGrid(QPainter &painter) {
Shinya Kitaoka 120a6e
  QFontMetrics fm(painter.font());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // ruler background
Shinya Kitaoka 120a6e
  painter.setPen(Qt::NoPen);
Shinya Kitaoka 120a6e
  painter.setBrush(getRulerBackground());
Shinya Kitaoka 120a6e
  painter.drawRect(0, 0, width(), m_frameAxisY);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // draw ticks and labels
Shinya Kitaoka 120a6e
  Ruler ruler;
Shinya Kitaoka 120a6e
  ruler.setTransform(m_viewTransform.m11(), m_viewTransform.dx(), -1);
Shinya Kitaoka 120a6e
  ruler.setRange(m_valueAxisX, width());
Shinya Kitaoka 120a6e
  ruler.setMinLabelDistance(fm.width("-8888") + 2);
Shinya Kitaoka 120a6e
  ruler.setMinDistance(5);
Shinya Kitaoka 120a6e
  ruler.setMinStep(1);
Shinya Kitaoka 120a6e
  ruler.compute();
Shinya Kitaoka 120a6e
  for (int i = 0; i < ruler.getTickCount(); i++) {
Shinya Kitaoka 120a6e
    double f     = ruler.getTick(i);
Shinya Kitaoka 120a6e
    bool isLabel = ruler.isLabel(i);
Shinya Kitaoka 120a6e
    int x        = frameToX(f);
Shinya Kitaoka 120a6e
    painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
    int y = m_frameAxisY;
Shinya Kitaoka 120a6e
    painter.drawLine(x, y - (isLabel ? 4 : 2), x, y);
Shinya Kitaoka 120a6e
    painter.setPen(getFrameLineColor());
Shinya Kitaoka 120a6e
    painter.drawLine(x, m_graphViewportY, x, height());
Shinya Kitaoka 120a6e
    if (isLabel) {
Shinya Kitaoka 120a6e
      painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
      QString labelText = QString::number(f + 1);
Shinya Kitaoka 120a6e
      painter.drawText(x - fm.width(labelText) / 2, y - 6, labelText);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::drawValueGrid(QPainter &painter) {
Shinya Kitaoka 120a6e
  TDoubleParam *curve = getCurrentCurve();
Shinya Kitaoka 120a6e
  if (!curve) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  QFontMetrics fm(painter.font());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // ruler background
Shinya Kitaoka 120a6e
  painter.setPen(Qt::NoPen);
Shinya Kitaoka 120a6e
  painter.setBrush(getRulerBackground());
Shinya Kitaoka 120a6e
  painter.drawRect(0, 0, m_valueAxisX, height());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  Ruler ruler;
Shinya Kitaoka 120a6e
  ruler.setTransform(m_viewTransform.m22(), m_viewTransform.dy());
Shinya Kitaoka 120a6e
  ruler.setRange(m_graphViewportY, height());
Shinya Kitaoka 120a6e
  ruler.setMinLabelDistance(fm.height() + 2);
Shinya Kitaoka 120a6e
  ruler.setMinDistance(5);
Shinya Kitaoka 120a6e
  ruler.compute();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  painter.setBrush(Qt::NoBrush);
Shinya Kitaoka 120a6e
  for (int i = 0; i < ruler.getTickCount(); i++) {
Shinya Kitaoka 120a6e
    double v     = ruler.getTick(i);
Shinya Kitaoka 120a6e
    bool isLabel = ruler.isLabel(i);
Shinya Kitaoka 120a6e
    int y        = tround(m_viewTransform.m22() * v +
Shinya Kitaoka 120a6e
                   m_viewTransform.dy());  // valueToY(curve, v);
Shinya Kitaoka 120a6e
    painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
    int x = m_valueAxisX;
Shinya Kitaoka 120a6e
    painter.drawLine(x - (isLabel ? 5 : 2), y, x, y);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    painter.setPen(getValueLineColor());
Shinya Kitaoka 120a6e
    painter.drawLine(x, y, width(), y);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (isLabel) {
Shinya Kitaoka 120a6e
      painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
      QString labelText = QString::number(v);
Shinya Kitaoka 120a6e
      painter.drawText(std::max(0, x - 5 - fm.width(labelText)),
Shinya Kitaoka 120a6e
                       y + fm.height() / 2, labelText);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  if (false && ruler.getTickCount() > 10) {
Shinya Kitaoka 120a6e
    double value = ruler.getTick(9);
Shinya Kitaoka 120a6e
    double dv    = fabs(ruler.getTick(10));
Shinya Kitaoka 120a6e
    double frame = 10;
Shinya Kitaoka 120a6e
    double df    = dv * getPixelRatio(curve);
Shinya Kitaoka 120a6e
    QPointF p0   = getWinPos(curve, frame, value);
Shinya Kitaoka 120a6e
    QPointF p1   = getWinPos(curve, frame + df, value + dv);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    painter.setPen(Qt::magenta);
Shinya Kitaoka 120a6e
    painter.drawRect(p0.x(), p0.y(), (p1 - p0).x(), (p1 - p0).y());
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::drawOtherCurves(QPainter &painter) {
Shinya Kitaoka 120a6e
  painter.setRenderHint(QPainter::Antialiasing, false);
Shinya Kitaoka 120a6e
  painter.setBrush(Qt::NoBrush);
Shinya Kitaoka 120a6e
  int x0 = m_valueAxisX;
Shinya Kitaoka 120a6e
  int x1 = width();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  QPen solidPen;
Shinya Kitaoka 120a6e
  QPen dashedPen;
Shinya Kitaoka 120a6e
  QVector<qreal> dashes;</qreal>
Shinya Kitaoka 120a6e
  dashes << 4 << 4;
Shinya Kitaoka 120a6e
  dashedPen.setDashPattern(dashes);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  for (int i = 0; i < m_functionTreeModel->getActiveChannelCount(); i++) {
Shinya Kitaoka 120a6e
    FunctionTreeModel::Channel *channel =
Shinya Kitaoka 120a6e
        m_functionTreeModel->getActiveChannel(i);
Shinya Kitaoka 120a6e
    if (channel->isCurrent()) continue;
Shinya Kitaoka 120a6e
    TDoubleParam *curve = channel->getParam();
Shinya Kitaoka 120a6e
    QColor color =
Shinya Kitaoka 120a6e
        curve == m_curveLabel.curve ? m_selectedColor : getOtherCurvesColor();
Shinya Kitaoka 120a6e
    solidPen.setColor(color);
Shinya Kitaoka 120a6e
    dashedPen.setColor(color);
Shinya Kitaoka 120a6e
    painter.setBrush(Qt::NoBrush);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    int kCount = curve->getKeyframeCount();
Shinya Kitaoka 120a6e
    if (kCount == 0) {
Shinya Kitaoka 120a6e
      // no control points
Shinya Kitaoka 120a6e
      painter.setPen(dashedPen);
Shinya Kitaoka 120a6e
      painter.drawPath(getSegmentPainterPath(curve, 0, x0, x1));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    // draw control points and handles
Shinya Kitaoka 120a6e
    else {
Shinya Kitaoka 120a6e
      for (int k = -1; k < kCount; k++) {
Shinya Kitaoka 120a6e
        painter.setPen((k < 0 || k >= kCount - 1) ? dashedPen : solidPen);
Shinya Kitaoka 120a6e
        painter.drawPath(getSegmentPainterPath(curve, k, x0, x1));
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
      painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
      painter.setBrush(m_subColor);
Shinya Kitaoka 120a6e
      for (int k = 0; k < kCount; k++) {
Shinya Kitaoka 120a6e
        double frame = curve->keyframeIndexToFrame(k);
Shinya Kitaoka 120a6e
        QPointF p    = getWinPos(curve, frame, curve->getValue(frame));
Shinya Kitaoka 120a6e
        painter.drawRect(p.x() - 1, p.y() - 1, 3, 3);
Shinya Kitaoka 120a6e
        QPointF p2 = getWinPos(curve, frame, curve->getValue(frame, true));
Shinya Kitaoka 120a6e
        if (p2.y() != p.y()) {
Shinya Kitaoka 120a6e
          painter.drawRect(p2.x() - 1, p2.y() - 1, 3, 3);
Shinya Kitaoka 120a6e
          painter.setPen(solidPen);
Shinya Kitaoka 120a6e
          painter.drawLine(p, p2);
Shinya Kitaoka 120a6e
          painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  painter.setBrush(Qt::NoBrush);
Shinya Kitaoka 120a6e
  painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
  painter.setRenderHint(QPainter::Antialiasing, false);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::updateGadgets(TDoubleParam *curve) {
Shinya Kitaoka 120a6e
  m_gadgets.clear();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TDoubleKeyframe oldKf;
Shinya Kitaoka 120a6e
  oldKf.m_type = TDoubleKeyframe::None;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int keyframeCount = curve->getKeyframeCount();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  for (int i = 0; i != keyframeCount; ++i) {
Shinya Kitaoka 120a6e
    const int pointHitRadius = 10, handleHitRadius = 6;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TDoubleKeyframe kf = curve->getKeyframe(i);
Shinya Kitaoka 120a6e
    kf.m_value         = curve->getValue(
Shinya Kitaoka 120a6e
        kf.m_frame);  // Some keyframe values do NOT correspond to the
Shinya Kitaoka 120a6e
                      // actual displayed curve value (eg with expressions)
Shinya Kitaoka 120a6e
    // Build keyframe positions
Shinya Kitaoka 120a6e
    QPointF p     = getWinPos(curve, kf.m_frame);
Shinya Kitaoka 120a6e
    QPointF pLeft = p;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (i == keyframeCount - 1 &&
Shinya Kitaoka 120a6e
        curve
Shinya Kitaoka 120a6e
            ->isCycleEnabled())  // This is probably OBSOLETE. I don't think the
Shinya Kitaoka 120a6e
      p = getWinPos(curve, kf.m_frame,
Shinya Kitaoka 120a6e
                    curve->getValue(
Shinya Kitaoka 120a6e
                        kf.m_frame,
Shinya Kitaoka 120a6e
                        true));  // GUI allows cycling single curves nowadays...
Shinya Kitaoka 120a6e
                                 // However, is the assignment correct?
Shinya Kitaoka 120a6e
    // Add keyframe gadget(s)
Shinya Kitaoka 120a6e
    m_gadgets.push_back(Gadget(Point, i, p, pointHitRadius, pointHitRadius));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TPointD currentPointRight(kf.m_frame, kf.m_value);
Shinya Kitaoka 120a6e
    TPointD currentPointLeft(currentPointRight);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // If the previous segment or the current segment are not keyframe based,
Shinya Kitaoka 120a6e
    // the curve can have two different values in kf.m_frame
Shinya Kitaoka 120a6e
    if (i > 0 &&
Shinya Kitaoka 120a6e
        (!TDoubleKeyframe::isKeyframeBased(
Shinya Kitaoka 120a6e
             kf.m_type) ||  // Keyframe-based are the above mentioned curves
Shinya Kitaoka 120a6e
         !TDoubleKeyframe::isKeyframeBased(
Shinya Kitaoka 120a6e
             curve->getKeyframe(i - 1)
Shinya Kitaoka 120a6e
                 .m_type)))  // where values stored in keyframes are not used
Shinya Kitaoka 120a6e
    {                        // to calculate the actual curve values.
Shinya Kitaoka 120a6e
      currentPointLeft.y = curve->getValue(kf.m_frame, true);
Shinya Kitaoka 120a6e
      pLeft              = getWinPos(curve, currentPointLeft);
Shinya Kitaoka 120a6e
      m_gadgets.push_back(
Shinya Kitaoka 120a6e
          Gadget(Point, i, pLeft, pointHitRadius, pointHitRadius));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Add handle gadgets (eg the speed or ease handles)
Shinya Kitaoka 120a6e
    if (getSelection()->isSelected(curve, i)) {
Shinya Kitaoka 120a6e
      // Left handle
Shinya Kitaoka 120a6e
      switch (oldKf.m_type) {
Shinya Kitaoka 120a6e
      case TDoubleKeyframe::SpeedInOut: {
Shinya Kitaoka 120a6e
        TPointD speedIn = curve->getSpeedIn(i);
Shinya Kitaoka 120a6e
        if (norm2(speedIn) > 0) {
Shinya Kitaoka 120a6e
          QPointF q = getWinPos(curve, currentPointLeft + speedIn);
Shinya Kitaoka 120a6e
          m_gadgets.push_back(
Shinya Kitaoka 120a6e
              Gadget(SpeedIn, i, q, handleHitRadius, handleHitRadius, pLeft));
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
        break;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      case TDoubleKeyframe::EaseInOut: {
Shinya Kitaoka 120a6e
        QPointF q = getWinPos(curve, kf.m_frame + kf.m_speedIn.x);
Shinya Kitaoka 120a6e
        m_gadgets.push_back(Gadget(EaseIn, i, q, 6, 15));
Shinya Kitaoka 120a6e
        break;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      case TDoubleKeyframe::EaseInOutPercentage: {
Shinya Kitaoka 120a6e
        double easeIn = kf.m_speedIn.x * (kf.m_frame - oldKf.m_frame) * 0.01;
Shinya Kitaoka 120a6e
        QPointF q     = getWinPos(curve, kf.m_frame + easeIn);
Shinya Kitaoka 120a6e
        m_gadgets.push_back(Gadget(EaseInPercentage, i, q, 6, 15));
Shinya Kitaoka 120a6e
        break;
Shinya Kitaoka 120a6e
      }
Rozhuk Ivan 823a31
      default:
Rozhuk Ivan 823a31
        break;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Right handle
Shinya Kitaoka 120a6e
      if (i != keyframeCount - 1) {
Shinya Kitaoka 120a6e
        switch (kf.m_type) {
Shinya Kitaoka 120a6e
        case TDoubleKeyframe::SpeedInOut: {
Shinya Kitaoka 120a6e
          TPointD speedOut = curve->getSpeedOut(i);
Shinya Kitaoka 120a6e
          if (norm2(speedOut) > 0) {
Shinya Kitaoka 120a6e
            QPointF q = getWinPos(curve, currentPointRight + speedOut);
Shinya Kitaoka 120a6e
            m_gadgets.push_back(
Shinya Kitaoka 120a6e
                Gadget(SpeedOut, i, q, handleHitRadius, handleHitRadius, p));
Shinya Kitaoka 120a6e
          }
Shinya Kitaoka 120a6e
          break;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        case TDoubleKeyframe::EaseInOut: {
Shinya Kitaoka 120a6e
          QPointF q = getWinPos(curve, kf.m_frame + kf.m_speedOut.x);
Shinya Kitaoka 120a6e
          m_gadgets.push_back(Gadget(EaseOut, i, q, 6, 15));
Shinya Kitaoka 120a6e
          break;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        case TDoubleKeyframe::EaseInOutPercentage: {
Shinya Kitaoka 120a6e
          double segmentWidth = curve->keyframeIndexToFrame(i + 1) - kf.m_frame;
Shinya Kitaoka 120a6e
          double easeOut      = segmentWidth * kf.m_speedOut.x * 0.01;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          QPointF q = getWinPos(curve, kf.m_frame + easeOut);
Shinya Kitaoka 120a6e
          m_gadgets.push_back(Gadget(EaseOutPercentage, i, q, 6, 15));
Shinya Kitaoka 120a6e
          break;
Shinya Kitaoka 120a6e
        }
Rozhuk Ivan 823a31
        default:
Rozhuk Ivan 823a31
          break;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    oldKf = kf;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Add group gadgets (ie those that can be added when multiple channels share
Shinya Kitaoka 120a6e
  // the same keyframe data)
Shinya Kitaoka 120a6e
  int channelCount = m_functionTreeModel->getActiveChannelCount();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Using a map of vectors. Yes, really. The *ideal* way would be that of
Shinya Kitaoka 120a6e
  // copying the first keyframes
Shinya Kitaoka 120a6e
  // vector, and then comparing it with the others from each channel - keeping
Shinya Kitaoka 120a6e
  // the common data only...
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  typedef std::map<double, std::vector<tdoublekeyframe="">></double,>
Shinya Kitaoka 120a6e
      KeyframeTable;  // frame -> { keyframes }
Shinya Kitaoka 120a6e
  KeyframeTable keyframes;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  for (int i = 0; i != channelCount; ++i) {
Shinya Kitaoka 120a6e
    FunctionTreeModel::Channel *channel =
Shinya Kitaoka 120a6e
        m_functionTreeModel->getActiveChannel(i);
Shinya Kitaoka 120a6e
    if (!channel) continue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TDoubleParam *curve = channel->getParam();
Shinya Kitaoka 120a6e
    for (int j = 0; j != curve->getKeyframeCount(); ++j) {
Shinya Kitaoka 120a6e
      TDoubleKeyframe kf = curve->getKeyframe(
Shinya Kitaoka 120a6e
          j);  // Well... this stuff gets called upon *painting*  o_o'
Shinya Kitaoka 120a6e
      keyframes[kf.m_frame].push_back(
Shinya Kitaoka 120a6e
          kf);  // It's bound to be slow. Do we really need it?
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int groupHandleY = m_graphViewportY - 6;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  KeyframeTable::iterator it, iEnd(keyframes.end()),
Shinya Kitaoka 120a6e
      iLast(keyframes.empty() ? iEnd : --iEnd);
Shinya Kitaoka 120a6e
  for (KeyframeTable::iterator it = keyframes.begin(); it != keyframes.end();
Shinya Kitaoka 120a6e
       ++it) {
Shinya Kitaoka 120a6e
    assert(!it->second.empty());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    double frame = it->first;  // redundant, already in the key... oh well
Shinya Kitaoka 120a6e
    QPointF p(frameToX(frame), groupHandleY);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    Gadget gadget((FunctionPanel::Handle)100, -1, p, 6,
Shinya Kitaoka 120a6e
                  6);  // No idea what the '100' type value mean...
Shinya Kitaoka 120a6e
    gadget.m_keyframePosition = frame;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    m_gadgets.push_back(gadget);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TDoubleKeyframe kf = it->second[0];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if ((int)it->second.size() < channelCount) continue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // All channels had this keyframe - so, add further gadgets about stuff...
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (int i = 1; i < channelCount; ++i) {
Shinya Kitaoka 120a6e
      // Find out if keyframes data differs
Shinya Kitaoka 120a6e
      const TDoubleKeyframe &kf2 = it->second[i];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (kf.m_type != kf2.m_type || kf.m_speedOut.x != kf2.m_speedOut.x)
Shinya Kitaoka 120a6e
        kf.m_type = TDoubleKeyframe::None;
Shinya Kitaoka 120a6e
      if (kf.m_prevType != kf2.m_prevType || kf.m_speedIn.x != kf2.m_speedIn.x)
Shinya Kitaoka 120a6e
        kf.m_prevType = TDoubleKeyframe::None;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // NOTE: EaseInOutPercentage are currently NOT SUPPORTED - they would be
Shinya Kitaoka 120a6e
    // harder to code and
Shinya Kitaoka 120a6e
    //       controversial, since the handle position depends on the *segment
Shinya Kitaoka 120a6e
    //       size* too.
Shinya Kitaoka 120a6e
    //       So, keyframe data could be shared, but adjacent segment lengths
Shinya Kitaoka 120a6e
    //       could not...
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (it != iLast && (kf.m_type == TDoubleKeyframe::SpeedInOut ||
Shinya Kitaoka 120a6e
                        kf.m_type == TDoubleKeyframe::EaseInOut) &&
Shinya Kitaoka 120a6e
        kf.m_speedOut.x != 0) {
Shinya Kitaoka 120a6e
      QPointF p(frameToX(frame + kf.m_speedOut.x), groupHandleY);
Shinya Kitaoka 120a6e
      Gadget gadget((FunctionPanel::Handle)101, -1, p, 6, 15);  // type value...
Shinya Kitaoka 120a6e
      gadget.m_keyframePosition = frame;
Shinya Kitaoka 120a6e
      m_gadgets.push_back(gadget);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if ((kf.m_prevType == TDoubleKeyframe::SpeedInOut ||
Shinya Kitaoka 120a6e
         kf.m_prevType == TDoubleKeyframe::EaseInOut) &&
Shinya Kitaoka 120a6e
        kf.m_speedIn.x != 0) {
Shinya Kitaoka 120a6e
      QPointF p(frameToX(frame + kf.m_speedIn.x), groupHandleY);
Shinya Kitaoka 120a6e
      Gadget gadget((FunctionPanel::Handle)102, -1, p, 6, 15);  // type value...
Shinya Kitaoka 120a6e
      gadget.m_keyframePosition = frame;
Shinya Kitaoka 120a6e
      m_gadgets.push_back(gadget);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::drawCurrentCurve(QPainter &painter) {
Shinya Kitaoka 120a6e
  FunctionTreeModel::Channel *channel =
Shinya Kitaoka 120a6e
      m_functionTreeModel ? m_functionTreeModel->getCurrentChannel() : 0;
Shinya Kitaoka 120a6e
  if (!channel) return;
Shinya Kitaoka 120a6e
  TDoubleParam *curve = channel->getParam();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  painter.setRenderHint(QPainter::Antialiasing, true);
Shinya Kitaoka 120a6e
  QColor color = Qt::red;
Shinya Kitaoka 120a6e
  QPen solidPen(color);
Shinya Kitaoka 120a6e
  QPen dashedPen(color);
Shinya Kitaoka 120a6e
  QVector<qreal> dashes;</qreal>
Shinya Kitaoka 120a6e
  dashes << 4 << 4;
Shinya Kitaoka 120a6e
  dashedPen.setDashPattern(dashes);
Shinya Kitaoka 120a6e
  painter.setBrush(Qt::NoBrush);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int x0 = m_valueAxisX;
Shinya Kitaoka 120a6e
  int x1 = width();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // draw curve
Shinya Kitaoka 120a6e
  int kCount = curve->getKeyframeCount();
Shinya Kitaoka 120a6e
  if (kCount == 0) {
Shinya Kitaoka 120a6e
    // no control points
Shinya Kitaoka 120a6e
    painter.setPen(dashedPen);
Shinya Kitaoka 120a6e
    painter.drawPath(getSegmentPainterPath(curve, 0, x0, x1));
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    for (int k = -1; k < kCount; k++) {
Shinya Kitaoka 120a6e
      if (k < 0 || k >= kCount - 1) {
Shinya Kitaoka 120a6e
        painter.setPen(dashedPen);
Shinya Kitaoka 120a6e
        painter.drawPath(getSegmentPainterPath(curve, k, x0, x1));
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        TDoubleKeyframe::Type segmentType = curve->getKeyframe(k).m_type;
Shinya Kitaoka 120a6e
        QColor color                      = Qt::red;
Shinya Kitaoka 120a6e
        if (segmentType == TDoubleKeyframe::Expression ||
Shinya Kitaoka 120a6e
            segmentType == TDoubleKeyframe::SimilarShape ||
Shinya Kitaoka 120a6e
            segmentType == TDoubleKeyframe::File)
Shinya Kitaoka 120a6e
          color = QColor(185, 0, 0);
Shinya Kitaoka 120a6e
        if (getSelection()->isSegmentSelected(curve, k))
Shinya Kitaoka 120a6e
          solidPen.setWidth(2);
Shinya Kitaoka 120a6e
        else
Shinya Kitaoka 120a6e
          solidPen.setWidth(0);
Shinya Kitaoka 120a6e
        solidPen.setColor(color);
Shinya Kitaoka 120a6e
        painter.setPen(solidPen);
Shinya Kitaoka 120a6e
        painter.drawPath(getSegmentPainterPath(curve, k, x0, x1));
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  painter.setPen(QPen(m_textColor, 0));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // draw control points
Shinya Kitaoka 120a6e
  updateGadgets(curve);
Shinya Kitaoka 120a6e
  painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
  for (int j = 0; j < (int)m_gadgets.size(); j++) {
Shinya Kitaoka 120a6e
    const Gadget &g = m_gadgets[j];
Shinya Kitaoka 120a6e
    if (g.m_handle == SpeedIn || g.m_handle == SpeedOut)
Shinya Kitaoka 120a6e
      painter.drawLine(g.m_pointPos, g.m_pos);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  solidPen.setWidth(0);
Shinya Kitaoka 120a6e
  solidPen.setColor(Qt::red);
Shinya Kitaoka 120a6e
  painter.setPen(solidPen);
Shinya Kitaoka 120a6e
  for (int j = 0; j < (int)m_gadgets.size() - 1; j++)
Shinya Kitaoka 120a6e
    if (m_gadgets[j].m_handle == Point && m_gadgets[j + 1].m_handle &&
Shinya Kitaoka 120a6e
        m_gadgets[j + 1].m_handle != 100 &&
Shinya Kitaoka 120a6e
        m_gadgets[j].m_pos.x() == m_gadgets[j + 1].m_pos.x())
Shinya Kitaoka 120a6e
      painter.drawLine(m_gadgets[j].m_pos, m_gadgets[j + 1].m_pos);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  painter.setRenderHint(QPainter::Antialiasing, false);
Shinya Kitaoka 120a6e
  for (int j = 0; j < (int)m_gadgets.size(); j++) {
Shinya Kitaoka 120a6e
    const Gadget &g = m_gadgets[j];
Shinya Kitaoka 120a6e
    int i           = g.m_kIndex;
Shinya Kitaoka 120a6e
    int r           = 1;
Shinya Kitaoka 120a6e
    QPointF p       = g.m_pos;
Shinya Kitaoka 120a6e
    double easeDx = 0, easeHeight = 15, easeTick = 2;
Shinya Kitaoka 120a6e
    bool isSelected = getSelection()->isSelected(curve, i);
Shinya Kitaoka 120a6e
    bool isHighlighted =
Shinya Kitaoka 120a6e
        m_highlighted.handle == g.m_handle && m_highlighted.gIndex == j;
Shinya Kitaoka 120a6e
    switch (g.m_handle) {
Shinya Kitaoka 120a6e
    case Point:
Shinya Kitaoka 120a6e
      painter.setBrush(isSelected ? QColor(255, 126, 0) : m_subColor);
Shinya Kitaoka 120a6e
      painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
      r = isHighlighted ? 3 : 2;
Shinya Kitaoka 120a6e
      drawSquare(painter, p, r);
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    case SpeedIn:
Shinya Kitaoka 120a6e
    case SpeedOut:
Shinya Kitaoka 120a6e
      painter.setBrush(m_subColor);
Shinya Kitaoka 120a6e
      painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
      r = isHighlighted ? 3 : 2;
Shinya Kitaoka 120a6e
      drawRoundedSquare(painter, p, r);
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    case EaseIn:
Shinya Kitaoka 120a6e
    case EaseOut:
Shinya Kitaoka 120a6e
    case EaseInPercentage:
Shinya Kitaoka 120a6e
    case EaseOutPercentage:
Shinya Kitaoka 120a6e
      painter.setBrush(Qt::NoBrush);
Shinya Kitaoka 120a6e
      painter.setPen(isHighlighted ? QColor(255, 126, 0) : m_textColor);
Shinya Kitaoka 120a6e
      painter.drawLine(p.x(), p.y() - easeHeight, p.x(), p.y() + easeHeight);
Shinya Kitaoka 120a6e
      if (g.m_handle == EaseIn || g.m_handle == EaseInPercentage)
Shinya Kitaoka 120a6e
        easeDx = easeTick;
Shinya Kitaoka 120a6e
      else
Shinya Kitaoka 120a6e
        easeDx = -easeTick;
Shinya Kitaoka 120a6e
      painter.drawLine(p.x(), p.y() - easeHeight, p.x() + easeDx,
Shinya Kitaoka 120a6e
                       p.y() - easeHeight - easeTick);
Shinya Kitaoka 120a6e
      painter.drawLine(p.x(), p.y() + easeHeight, p.x() + easeDx,
Shinya Kitaoka 120a6e
                       p.y() + easeHeight + easeTick);
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      painter.setBrush(
Shinya Kitaoka 120a6e
          Qt::NoBrush);  // isSelected ? QColor(255,126,0) : Qt::white);
Shinya Kitaoka 120a6e
      painter.setPen(isHighlighted ? QColor(255, 126, 0) : m_selectedColor);
Shinya Kitaoka 120a6e
      painter.drawLine(p.x(), p.y() - 15, p.x(), p.y() + 15);
Shinya Kitaoka 120a6e
      break;
Rozhuk Ivan 823a31
    default:
Rozhuk Ivan 823a31
      break;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  painter.setRenderHint(QPainter::Antialiasing, false);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::drawGroupKeyframes(QPainter &painter) {
Shinya Kitaoka 120a6e
  FunctionTreeModel::Channel *channel =
Shinya Kitaoka 120a6e
      m_functionTreeModel ? m_functionTreeModel->getCurrentChannel() : 0;
Shinya Kitaoka 120a6e
  if (!channel) return;
Shinya Kitaoka 120a6e
  QColor color = Qt::red;
Shinya Kitaoka 120a6e
  QPen solidPen(color);
Shinya Kitaoka 120a6e
  QPen dashedPen(color);
Shinya Kitaoka 120a6e
  QVector<qreal> dashes;</qreal>
Shinya Kitaoka 120a6e
  dashes << 4 << 4;
Shinya Kitaoka 120a6e
  dashedPen.setDashPattern(dashes);
Shinya Kitaoka 120a6e
  painter.setBrush(Qt::NoBrush);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int x0 = m_valueAxisX;
Shinya Kitaoka 120a6e
  int x1 = width();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  solidPen.setWidth(0);
Shinya Kitaoka 120a6e
  solidPen.setColor(Qt::red);
Shinya Kitaoka 120a6e
  painter.setPen(solidPen);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  std::vector<double> keyframes;</double>
Shinya Kitaoka 120a6e
  int y = 0;
Shinya Kitaoka 120a6e
  for (int j = 0; j < (int)m_gadgets.size(); j++) {
Shinya Kitaoka 120a6e
    const Gadget &g = m_gadgets[j];
Shinya Kitaoka 120a6e
    int i           = g.m_kIndex;
Shinya Kitaoka 120a6e
    int r           = 1;
Shinya Kitaoka 120a6e
    QPointF p       = g.m_pos;
Shinya Kitaoka 120a6e
    double easeDx = 0, easeHeight = 15, easeTick = 2;
Shinya Kitaoka 120a6e
    bool isSelected = false;  // getSelection()->isSelected(curve, i);
Shinya Kitaoka 120a6e
    bool isHighlighted =
Shinya Kitaoka 120a6e
        m_highlighted.handle == g.m_handle && m_highlighted.gIndex == j;
Shinya Kitaoka 120a6e
    painter.setBrush(isSelected ? QColor(255, 126, 0) : m_subColor);
Shinya Kitaoka 120a6e
    painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
    r = isHighlighted ? 3 : 2;
Shinya Kitaoka 120a6e
    QPainterPath pp;
Shinya Kitaoka 120a6e
    int d = 2;
Shinya Kitaoka 120a6e
    int h = 4;
Shinya Kitaoka 120a6e
    switch (g.m_handle) {
Shinya Kitaoka 120a6e
    case 100:
Shinya Kitaoka 120a6e
      drawSquare(painter, p, r);
Shinya Kitaoka 120a6e
      y = p.y();
Shinya Kitaoka 120a6e
      keyframes.push_back(p.x());
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
    case 101:
Shinya Kitaoka 120a6e
      d = -d;
Shinya Kitaoka 120a6e
    // Note: NO break!
Shinya Kitaoka 120a6e
    case 102:
Shinya Kitaoka 120a6e
      painter.setBrush(Qt::NoBrush);
Shinya Kitaoka 120a6e
      painter.setPen(isHighlighted ? QColor(255, 126, 0) : m_textColor);
Shinya Kitaoka 120a6e
      pp.moveTo(p + QPointF(d, -h));
Shinya Kitaoka 120a6e
      pp.lineTo(p + QPointF(0, -h));
Shinya Kitaoka 120a6e
      pp.lineTo(p + QPointF(0, h));
Shinya Kitaoka 120a6e
      pp.lineTo(p + QPointF(d, h));
Shinya Kitaoka 120a6e
      painter.drawPath(pp);
Shinya Kitaoka 120a6e
      break;
Rozhuk Ivan 823a31
    default:
Rozhuk Ivan 823a31
      break;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
  for (int i = 0; i + 1 < (int)keyframes.size(); i++) {
Shinya Kitaoka 120a6e
    painter.drawLine(keyframes[i] + 3, y, keyframes[i + 1] - 3, y);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::paintEvent(QPaintEvent *e) {
Shinya Kitaoka 120a6e
  m_gadgets.clear();
Shinya Kitaoka 120a6e
Jeremy Bullock 807052
  QString fontName = Preferences::instance()->getInterfaceFont();
Jeremy Bullock 807052
  if (fontName == "") {
Jeremy Bullock 807052
#ifdef _WIN32
Jeremy Bullock 807052
    fontName = "Arial";
Jeremy Bullock 807052
#else
Jeremy Bullock 807052
    fontName = "Helvetica";
Jeremy Bullock 807052
#endif
Jeremy Bullock 807052
  }
Jeremy Bullock 807052
Shinya Kitaoka 120a6e
  QPainter painter(this);
Jeremy Bullock 807052
  QFont font(fontName, 8);
Shinya Kitaoka 120a6e
  painter.setFont(font);
Shinya Kitaoka 120a6e
  QFontMetrics fm(font);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // define ruler sizes
Shinya Kitaoka 120a6e
  m_valueAxisX     = fm.width("-888.88") + 2;
Shinya Kitaoka 120a6e
  m_frameAxisY     = fm.height() + 2;
Shinya Kitaoka 120a6e
  m_graphViewportY = m_frameAxisY + 12;
Shinya Kitaoka 120a6e
  int ox           = m_valueAxisX;
Shinya Kitaoka 120a6e
  int oy0          = m_graphViewportY;
Shinya Kitaoka 120a6e
  int oy1          = m_frameAxisY;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // QRect bounds(0,0,width(),height());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // draw functions background
Shinya Kitaoka 120a6e
  painter.setBrush(getBGColor());
Shinya Kitaoka 120a6e
  painter.setPen(Qt::NoPen);
Shinya Kitaoka 120a6e
  painter.drawRect(ox, oy0, width() - ox, height() - oy0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  painter.setClipRect(ox, 0, width() - ox, height());
Shinya Kitaoka 120a6e
  drawCurrentFrame(painter);
Shinya Kitaoka 120a6e
  drawFrameGrid(painter);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  painter.setClipRect(0, oy0, width(), height() - oy0);
Shinya Kitaoka 120a6e
  drawValueGrid(painter);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // draw axes
Shinya Kitaoka 120a6e
  painter.setClipping(false);
Shinya Kitaoka 120a6e
  painter.setPen(m_textColor);
Shinya Kitaoka 120a6e
  painter.drawLine(0, oy0, width(), oy0);
Shinya Kitaoka 120a6e
  painter.drawLine(ox, oy1, width(), oy1);
Shinya Kitaoka 120a6e
  painter.drawLine(ox, 0, ox, height());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // draw curves
Shinya Kitaoka 120a6e
  painter.setClipRect(ox + 1, oy0 + 1, width() - ox - 1, height() - oy0 - 1);
Shinya Kitaoka 120a6e
  drawOtherCurves(painter);
Shinya Kitaoka 120a6e
  drawCurrentCurve(painter);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  painter.setClipping(false);
Shinya Kitaoka 120a6e
  painter.setClipRect(ox + 1, oy1 + 1, width() - ox - 1, oy0 - oy1 - 1);
Shinya Kitaoka 120a6e
  drawGroupKeyframes(painter);
Shinya Kitaoka 120a6e
  painter.setClipRect(ox + 1, oy0 + 1, width() - ox - 1, height() - oy0 - 1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // tool
Shinya Kitaoka 120a6e
  if (m_dragTool) m_dragTool->draw(painter);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // cursor
Shinya Kitaoka 120a6e
  if (m_cursor.visible) {
Shinya Kitaoka 120a6e
    painter.setClipRect(ox + 1, oy0 + 1, width() - ox - 1, height() - oy0 - 1);
Shinya Kitaoka 120a6e
    painter.setPen(getOtherCurvesColor());
Shinya Kitaoka 120a6e
    int x = frameToX(m_cursor.frame);
Shinya Kitaoka 120a6e
    painter.drawLine(x, oy0 + 1, x, oy0 + 10);
Shinya Kitaoka 120a6e
    QString text = QString::number(tround(m_cursor.frame) + 1);
Shinya Kitaoka 120a6e
    painter.drawText(x - fm.width(text) / 2, oy0 + 10 + fm.height(), text);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TDoubleParam *currentCurve = getCurrentCurve();
Shinya Kitaoka 120a6e
    if (currentCurve) {
Shinya Kitaoka 120a6e
      const TUnit *unit = 0;
Shinya Kitaoka 120a6e
      if (currentCurve->getMeasure())
Shinya Kitaoka 120a6e
        unit                 = currentCurve->getMeasure()->getCurrentUnit();
Shinya Kitaoka 120a6e
      double displayValue    = m_cursor.value;
Shinya Kitaoka 120a6e
      if (unit) displayValue = unit->convertTo(displayValue);
Shinya Kitaoka 120a6e
      // painter.setClipRect(0,oy0,height(),height()-oy0);
Shinya Kitaoka 120a6e
      int y = valueToY(currentCurve, m_cursor.value);
Shinya Kitaoka 120a6e
      painter.drawLine(ox, y, ox + 10, y);
Shinya Kitaoka 120a6e
      painter.drawText(m_origin.x() + 10, y + 4, QString::number(displayValue));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // curve name
Shinya Kitaoka 120a6e
  if (m_curveLabel.text != "") {
Shinya Kitaoka 120a6e
    painter.setClipRect(ox, oy0, width() - ox, height() - oy0);
Shinya Kitaoka 120a6e
    painter.setPen(m_selectedColor);
Shinya Kitaoka 120a6e
    painter.drawLine(m_curveLabel.curvePos, m_curveLabel.labelPos);
Shinya Kitaoka 120a6e
    painter.drawText(m_curveLabel.labelPos,
Shinya Kitaoka 120a6e
                     QString::fromStdString(m_curveLabel.text));
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // painter.setPen(Qt::black);
Shinya Kitaoka 120a6e
  // painter.drawText(QPointF(70,70),
Shinya Kitaoka 120a6e
  //  "f0=" + QString::number(xToFrame(ox)) +
Shinya Kitaoka 120a6e
  //  " f1=" + QString::number(xToFrame(width())));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // painter.setPen(Qt::black);
Shinya Kitaoka 120a6e
  // painter.setBrush(Qt::NoBrush);
Shinya Kitaoka 120a6e
  // painter.drawRect(ox+10,oy+10,width()-ox-20,height()-oy-20);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::mousePressEvent(QMouseEvent *e) {
Shinya Kitaoka 120a6e
  m_cursor.visible = false;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // m_dragTool should be 0. just in case...
Shinya Kitaoka 120a6e
  assert(m_dragTool == 0);
Shinya Kitaoka 120a6e
  m_dragTool = 0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (e->button() == Qt::MidButton) {
Shinya Kitaoka 120a6e
    // mid mouse click => panning
Shinya Kitaoka 120a6e
    bool xLocked = e->pos().x() <= m_valueAxisX;
Shinya Kitaoka 120a6e
    bool yLocked = e->pos().y() <= m_valueAxisX;
Shinya Kitaoka 120a6e
    m_dragTool   = new PanDragTool(this, xLocked, yLocked);
Shinya Kitaoka 120a6e
    m_dragTool->click(e);
Shinya Kitaoka 120a6e
    return;
Shinya Kitaoka 120a6e
  } else if (e->button() == Qt::RightButton) {
Shinya Kitaoka 120a6e
    // right mouse click => open context menu
Shinya Kitaoka 120a6e
    openContextMenu(e);
Shinya Kitaoka 120a6e
    return;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  QPoint winPos         = e->pos();
Shinya Kitaoka 120a6e
  Handle handle         = None;
Shinya Kitaoka 120a6e
  const int maxDistance = 20;
Shinya Kitaoka 120a6e
  int closestGadgetId   = findClosestGadget(e->pos(), handle, maxDistance);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (e->pos().x() > m_valueAxisX && e->pos().y() < m_frameAxisY &&
Shinya Kitaoka 120a6e
      closestGadgetId < 0 && (e->modifiers() & Qt::ControlModifier) == 0) {
Shinya Kitaoka 120a6e
    // click on topbar => frame zoom
Shinya Kitaoka 120a6e
    m_dragTool = new ZoomDragTool(this, ZoomDragTool::FrameZoom);
Shinya Kitaoka 120a6e
  } else if (e->pos().x() < m_valueAxisX && e->pos().y() > m_graphViewportY) {
Shinya Kitaoka 120a6e
    // click on topbar => value zoom
Shinya Kitaoka 120a6e
    m_dragTool = new ZoomDragTool(this, ZoomDragTool::ValueZoom);
Shinya Kitaoka 120a6e
  } else if (m_currentFrameStatus == 1 && m_frameHandle != 0 &&
Shinya Kitaoka 120a6e
             closestGadgetId < 0) {
Shinya Kitaoka 120a6e
    // click on current frame => move frame
Shinya Kitaoka 120a6e
    m_currentFrameStatus = 2;
Shinya Kitaoka 120a6e
    m_dragTool           = new MoveFrameDragTool(this, m_frameHandle);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (0 <= closestGadgetId && closestGadgetId < (int)m_gadgets.size()) {
Shinya Kitaoka 120a6e
    if (handle == 100)  // group move gadget
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      MovePointDragTool *dragTool = new MovePointDragTool(this, 0);
Shinya Kitaoka 120a6e
      dragTool->selectKeyframes(m_gadgets[closestGadgetId].m_keyframePosition);
Shinya Kitaoka 120a6e
      m_dragTool = dragTool;
Shinya Kitaoka 120a6e
    } else if (handle == 101 || handle == 102) {
Shinya Kitaoka 120a6e
      m_dragTool = new MoveGroupHandleDragTool(
Shinya Kitaoka 120a6e
          this, m_gadgets[closestGadgetId].m_keyframePosition, handle);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (m_dragTool) {
Shinya Kitaoka 120a6e
    m_dragTool->click(e);
Shinya Kitaoka 120a6e
    return;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  FunctionTreeModel::Channel *currentChannel =
Shinya Kitaoka 120a6e
      m_functionTreeModel ? m_functionTreeModel->getCurrentChannel() : 0;
Shinya Kitaoka 120a6e
  if (!currentChannel ||
Rozhuk Ivan 823a31
      (getCurveDistance(currentChannel->getParam(), winPos) > maxDistance &&
Rozhuk Ivan 823a31
          closestGadgetId < 0)) {
Shinya Kitaoka 120a6e
    // if current channel is undefined or its curve is too far from the clicked
Shinya Kitaoka 120a6e
    // point
Shinya Kitaoka 120a6e
    // the user is possibly trying to select a different curve
Shinya Kitaoka 120a6e
    FunctionTreeModel::Channel *channel =
Shinya Kitaoka 120a6e
        findClosestChannel(winPos, maxDistance);
Shinya Kitaoka 120a6e
    if (channel) {
Shinya Kitaoka 120a6e
      channel->setIsCurrent(true);
Shinya Kitaoka 120a6e
      // Open folder
Shinya Kitaoka 120a6e
      FunctionTreeModel::ChannelGroup *channelGroup =
Shinya Kitaoka 120a6e
          channel->getChannelGroup();
Shinya Kitaoka 120a6e
      if (!channelGroup->isOpen())
Shinya Kitaoka 120a6e
        channelGroup->getModel()->setExpandedItem(channelGroup->createIndex(),
Shinya Kitaoka 120a6e
                                                  true);
Shinya Kitaoka 120a6e
      currentChannel = channel;
Shinya Kitaoka 120a6e
      getSelection()->selectNone();
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (currentChannel) {
Shinya Kitaoka 120a6e
    TDoubleParam *currentCurve = currentChannel->getParam();
Shinya Kitaoka 120a6e
    if (currentCurve) {
Shinya Kitaoka 120a6e
      int kIndex =
Shinya Kitaoka 120a6e
          closestGadgetId >= 0 ? m_gadgets[closestGadgetId].m_kIndex : -1;
Shinya Kitaoka 120a6e
      if (kIndex >= 0) {
Shinya Kitaoka 120a6e
        // keyframe clicked
Shinya Kitaoka 120a6e
        if (handle == FunctionPanel::Point) {
Shinya Kitaoka 120a6e
          // select point (if needed)
Shinya Kitaoka 120a6e
          if (!getSelection()->isSelected(currentCurve, kIndex)) {
Shinya Kitaoka 120a6e
            // shift-click => add to selection
Shinya Kitaoka 120a6e
            if (0 == (e->modifiers() & Qt::ShiftModifier))
Shinya Kitaoka 120a6e
              getSelection()->deselectAllKeyframes();
Shinya Kitaoka 120a6e
            getSelection()->select(currentCurve, kIndex);
Shinya Kitaoka 120a6e
          }
Shinya Kitaoka 120a6e
          // move selected point(s)
Shinya Kitaoka 120a6e
          MovePointDragTool *dragTool =
Shinya Kitaoka 120a6e
              new MovePointDragTool(this, currentCurve);
Shinya Kitaoka 120a6e
          if (getSelection()->getSelectedSegment().first != 0) {
Shinya Kitaoka 120a6e
            // if a segment is selected then move only the clicked point
Shinya Kitaoka 120a6e
            dragTool->addKeyframe2(kIndex);
Shinya Kitaoka 120a6e
          } else {
Shinya Kitaoka 120a6e
            dragTool->setSelection(getSelection());
Shinya Kitaoka 120a6e
          }
Shinya Kitaoka 120a6e
          m_dragTool = dragTool;
Shinya Kitaoka 120a6e
        } else {
Shinya Kitaoka 120a6e
          m_dragTool =
Shinya Kitaoka 120a6e
              new MoveHandleDragTool(this, currentCurve, kIndex, handle);
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        // no keyframe clicked
Shinya Kitaoka 120a6e
        int curveDistance =
Shinya Kitaoka 120a6e
            getCurveDistance(currentChannel->getParam(), winPos);
Shinya Kitaoka 120a6e
        bool isKeyframeable = true;
Shinya Kitaoka 120a6e
        bool isGroup        = abs(winPos.y() - (m_graphViewportY - 5)) < 5;
Shinya Kitaoka 120a6e
        if (0 != (e->modifiers() & Qt::ControlModifier) &&
Shinya Kitaoka 120a6e
            (curveDistance < maxDistance || isGroup) && isKeyframeable) {
Shinya Kitaoka 120a6e
          // ctrl-clicked near curve => create a new keyframe
Shinya Kitaoka 120a6e
          double frame = tround(xToFrame(winPos.x()));
Shinya Kitaoka 120a6e
          MovePointDragTool *dragTool =
Shinya Kitaoka 120a6e
              new MovePointDragTool(this, isGroup ? 0 : currentCurve);
Shinya Kitaoka 120a6e
          //          if(curveDistance>=maxDistance)
Shinya Kitaoka 120a6e
          //            dragTool->m_channelGroup =
Shinya Kitaoka 120a6e
          //            currentChannel->getChannelGroup();
Shinya Kitaoka 120a6e
          dragTool->createKeyframe(frame);
Shinya Kitaoka 120a6e
          dragTool->selectKeyframes(frame);
Shinya Kitaoka 120a6e
          m_dragTool = dragTool;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          /*
Shinya Kitaoka 120a6e
int kIndex = dragTool->createKeyframe(frame);
Shinya Kitaoka 120a6e
          if(kIndex!=-1)
Shinya Kitaoka 120a6e
          {
Shinya Kitaoka 120a6e
                  getSelection()->deselectAllKeyframes();
Shinya Kitaoka 120a6e
                  getSelection()->select(currentCurve, kIndex);
Shinya Kitaoka 120a6e
                  m_dragTool = dragTool;
Shinya Kitaoka 120a6e
          }
Shinya Kitaoka 120a6e
*/
Shinya Kitaoka 120a6e
          // assert(0);
Shinya Kitaoka 120a6e
        } else if (curveDistance < maxDistance) {
Shinya Kitaoka 120a6e
          // clicked near curve (but far from keyframes)
Shinya Kitaoka 120a6e
          getSelection()->deselectAllKeyframes();
Shinya Kitaoka 120a6e
          double frame = xToFrame(winPos.x());
Shinya Kitaoka 120a6e
          int k0       = currentCurve->getPrevKeyframe(frame);
Shinya Kitaoka 120a6e
          int k1       = currentCurve->getNextKeyframe(frame);
Shinya Kitaoka 120a6e
          if (k0 >= 0 && k1 == k0 + 1) {
Shinya Kitaoka 120a6e
            // select and move the segment
Shinya Kitaoka 120a6e
            getSelection()->selectSegment(currentCurve, k0);
Shinya Kitaoka 120a6e
            MovePointDragTool *dragTool =
Shinya Kitaoka 120a6e
                new MovePointDragTool(this, currentCurve);
Shinya Kitaoka 120a6e
            dragTool->addKeyframe2(k0);
Shinya Kitaoka 120a6e
            dragTool->addKeyframe2(k1);
Shinya Kitaoka 120a6e
            m_dragTool = dragTool;
Shinya Kitaoka 120a6e
          } else {
Shinya Kitaoka 120a6e
            // start a rectangular selection
Shinya Kitaoka 120a6e
            m_dragTool = new RectSelectTool(this, currentCurve);
Shinya Kitaoka 120a6e
          }
Shinya Kitaoka 120a6e
        } else {
Shinya Kitaoka 120a6e
          // nothing clicked: start a rectangular selection
Shinya Kitaoka 120a6e
          getSelection()->deselectAllKeyframes();
Shinya Kitaoka 120a6e
          m_dragTool = new RectSelectTool(this, currentCurve);
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (m_dragTool) m_dragTool->click(e);
Shinya Kitaoka 120a6e
  update();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::mouseReleaseEvent(QMouseEvent *e) {
Shinya Kitaoka 120a6e
  if (m_dragTool) m_dragTool->release(e);
Shinya Kitaoka 120a6e
  delete m_dragTool;
Shinya Kitaoka 120a6e
  m_dragTool           = 0;
Shinya Kitaoka 120a6e
  m_cursor.visible     = true;
Shinya Kitaoka 120a6e
  m_currentFrameStatus = 0;
Shinya Kitaoka 120a6e
  update();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::mouseMoveEvent(QMouseEvent *e) {
Shinya Kitaoka 120a6e
  if (e->buttons()) {
Shinya Kitaoka 120a6e
    if (m_dragTool) m_dragTool->drag(e);
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    m_cursor.frame   = xToFrame(e->pos().x());
Shinya Kitaoka 120a6e
    m_cursor.value   = 0;
Shinya Kitaoka 120a6e
    m_cursor.visible = true;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TDoubleParam *currentCurve = getCurrentCurve();
Shinya Kitaoka 120a6e
    if (currentCurve) {
Shinya Kitaoka 120a6e
      Handle handle = None;
Shinya Kitaoka 120a6e
      int gIndex    = findClosestGadget(e->pos(), handle, 20);
Shinya Kitaoka 120a6e
      if (m_highlighted.handle != handle || m_highlighted.gIndex != gIndex) {
Shinya Kitaoka 120a6e
        m_highlighted.handle = handle;
Shinya Kitaoka 120a6e
        m_highlighted.gIndex = gIndex;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
      m_cursor.value = yToValue(currentCurve, e->pos().y());
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    double currentFrame = m_frameHandle ? m_frameHandle->getFrame() : 0;
Shinya Kitaoka 120a6e
    if (m_highlighted.handle == None &&
Shinya Kitaoka 120a6e
        std::abs(e->pos().x() - frameToX(currentFrame)) < 5)
Shinya Kitaoka 120a6e
      m_currentFrameStatus = 1;
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      m_currentFrameStatus = 0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    FunctionTreeModel::Channel *closestChannel =
Shinya Kitaoka 120a6e
        findClosestChannel(e->pos(), 20);
Shinya Kitaoka 120a6e
    if (closestChannel && m_highlighted.handle == None) {
Shinya Kitaoka 120a6e
      TDoubleParam *curve = closestChannel->getParam();
Shinya Kitaoka 120a6e
      if (m_functionTreeModel->getActiveChannelCount() <= 1)
Shinya Kitaoka 120a6e
        //|| closestChannel == m_functionTreeModel->getCurrentChannel())
Shinya Kitaoka 120a6e
        curve = 0;
Shinya Kitaoka 120a6e
      if (curve && m_curveLabel.curve != curve) {
Shinya Kitaoka 120a6e
        m_curveLabel.curve  = curve;
Shinya Kitaoka 120a6e
        QString channelName = closestChannel->data(Qt::DisplayRole).toString();
Shinya Kitaoka 120a6e
        QString parentChannelName =
Shinya Kitaoka 120a6e
            closestChannel->getChannelGroup()->data(Qt::DisplayRole).toString();
Shinya Kitaoka 120a6e
        QString name      = parentChannelName + QString(", ") + channelName;
Shinya Kitaoka 120a6e
        m_curveLabel.text = name.toStdString();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        // in order to avoid run off the right-end of visible area
Shinya Kitaoka 120a6e
        int textWidth = fontMetrics().width(name) + 30;
Shinya Kitaoka 120a6e
        double frame  = xToFrame(width() - textWidth);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        m_curveLabel.curvePos = getWinPos(curve, frame).toPoint();
Shinya Kitaoka 120a6e
        m_curveLabel.labelPos = m_curveLabel.curvePos + QPoint(20, -10);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      m_curveLabel.text  = "";
Shinya Kitaoka 120a6e
      m_curveLabel.curve = 0;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    update();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::keyPressEvent(QKeyEvent *e) {
Shinya Kitaoka 120a6e
  FunctionPanelZoomer(this).exec(e);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::enterEvent(QEvent *) {
Shinya Kitaoka 120a6e
  m_cursor.visible = true;
Shinya Kitaoka 120a6e
  update();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::leaveEvent(QEvent *) {
Shinya Kitaoka 120a6e
  m_cursor.visible = false;
Shinya Kitaoka 120a6e
  update();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::wheelEvent(QWheelEvent *e) {
Shinya Kitaoka 120a6e
  double factor = exp(0.002 * (double)e->delta());
Shinya Kitaoka 120a6e
  zoom(factor, factor, e->pos());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::fitGraphToWindow(bool currentCurveOnly) {
Shinya Kitaoka 120a6e
  double f0 = 0, f1 = -1;
Shinya Kitaoka 120a6e
  double v0 = 0, v1 = -1;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  for (int i = 0; i < m_functionTreeModel->getActiveChannelCount(); i++) {
Shinya Kitaoka 120a6e
    FunctionTreeModel::Channel *channel =
Shinya Kitaoka 120a6e
        m_functionTreeModel->getActiveChannel(i);
Shinya Kitaoka 120a6e
    TDoubleParam *curve = channel->getParam();
Shinya Kitaoka 120a6e
    if (currentCurveOnly && curve != getCurrentCurve()) continue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    const TUnit *unit             = 0;
Shinya Kitaoka 120a6e
    if (curve->getMeasure()) unit = curve->getMeasure()->getCurrentUnit();
Shinya Kitaoka 120a6e
    int n                         = curve->getKeyframeCount();
Shinya Kitaoka 120a6e
    if (n == 0) {
Shinya Kitaoka 120a6e
      double v    = curve->getDefaultValue();
Shinya Kitaoka 120a6e
      if (unit) v = unit->convertTo(v);
Shinya Kitaoka 120a6e
      if (v0 > v1)
Shinya Kitaoka 120a6e
        v0 = v1 = v;
Shinya Kitaoka 120a6e
      else if (v > v1)
Shinya Kitaoka 120a6e
        v1 = v;
Shinya Kitaoka 120a6e
      else if (v < v0)
Shinya Kitaoka 120a6e
        v0 = v;
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      TDoubleKeyframe k = curve->getKeyframe(0);
Shinya Kitaoka 120a6e
      double fa         = k.m_frame;
Shinya Kitaoka 120a6e
      k                 = curve->getKeyframe(n - 1);
Shinya Kitaoka 120a6e
      double fb         = k.m_frame;
Shinya Kitaoka 120a6e
      if (f0 > f1) {
Shinya Kitaoka 120a6e
        f0 = fa;
Shinya Kitaoka 120a6e
        f1 = fb;
Shinya Kitaoka 120a6e
      } else {
shun-iwasawa a0d489
        f0 = std::min(f0, fa);
shun-iwasawa a0d489
        f1 = std::max(f1, fb);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
      double v        = curve->getValue(fa);
Shinya Kitaoka 120a6e
      if (unit) v     = unit->convertTo(v);
Shinya Kitaoka 120a6e
      if (v0 > v1) v0 = v1 = v;
Shinya Kitaoka 120a6e
      int m                = 50;
Shinya Kitaoka 120a6e
      for (int j = 0; j < m; j++) {
Shinya Kitaoka 120a6e
        double t    = (double)j / (double)(m - 1);
Shinya Kitaoka 120a6e
        double v    = curve->getValue((1 - t) * fa + t * fb);
Shinya Kitaoka 120a6e
        if (unit) v = unit->convertTo(v);
shun-iwasawa a0d489
        v0          = std::min(v0, v);
shun-iwasawa a0d489
        v1          = std::max(v1, v);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  if (f0 >= f1 || v0 >= v1) {
Shinya Kitaoka 120a6e
    m_viewTransform = QTransform();
Shinya Kitaoka 120a6e
    m_viewTransform.translate(m_valueAxisX, 200);
Shinya Kitaoka 120a6e
    m_viewTransform.scale(5, -1);
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    double mx       = (width() - m_valueAxisX - 20) / (f1 - f0);
Shinya Kitaoka 120a6e
    double my       = -(height() - m_graphViewportY - 20) / (v1 - v0);
Shinya Kitaoka 120a6e
    double dx       = m_valueAxisX + 10 - f0 * mx;
Shinya Kitaoka 120a6e
    double dy       = m_graphViewportY + 10 - v1 * my;
Shinya Kitaoka 120a6e
    m_viewTransform = QTransform(mx, 0, 0, my, dx, dy);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  update();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::fitSelectedPoints() { fitGraphToWindow(true); }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::fitCurve() { fitGraphToWindow(); }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::fitRegion(double f0, double v0, double f1, double v1) {}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Campbell Barton ccd505
static void setSegmentType(FunctionSelection *selection, TDoubleParam *curve,
Campbell Barton ccd505
                           int segmentIndex, TDoubleKeyframe::Type type) {
Shinya Kitaoka 120a6e
  selection->selectSegment(curve, segmentIndex);
Shinya Kitaoka 120a6e
  KeyframeSetter setter(curve, segmentIndex);
Shinya Kitaoka 120a6e
  setter.setType(type);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::openContextMenu(QMouseEvent *e) {
Shinya Kitaoka 120a6e
  QAction linkHandlesAction(tr("Link Handles"), 0);
Shinya Kitaoka 120a6e
  QAction unlinkHandlesAction(tr("Unlink Handles"), 0);
Shinya Kitaoka 120a6e
  QAction resetHandlesAction(tr("Reset Handles"), 0);
Shinya Kitaoka 120a6e
  QAction deleteKeyframeAction(tr("Delete"), 0);
Shinya Kitaoka 120a6e
  QAction insertKeyframeAction(tr("Set Key"), 0);
Shinya Kitaoka 120a6e
  QAction activateCycleAction(tr("Activate Cycle"), 0);
Shinya Kitaoka 120a6e
  QAction deactivateCycleAction(tr("Deactivate Cycle"), 0);
Shinya Kitaoka 120a6e
  QAction setLinearAction(tr("Linear Interpolation"), 0);
Shinya Kitaoka 120a6e
  QAction setSpeedInOutAction(tr("Speed In / Speed Out Interpolation"), 0);
Shinya Kitaoka 120a6e
  QAction setEaseInOutAction(tr("Ease In / Ease Out Interpolation"), 0);
Shinya Kitaoka 120a6e
  QAction setEaseInOut2Action(tr("Ease In / Ease Out (%) Interpolation"), 0);
Shinya Kitaoka 120a6e
  QAction setExponentialAction(tr("Exponential Interpolation"), 0);
Shinya Kitaoka 120a6e
  QAction setExpressionAction(tr("Expression Interpolation"), 0);
Shinya Kitaoka 120a6e
  QAction setFileAction(tr("File Interpolation"), 0);
Shinya Kitaoka 120a6e
  QAction setConstantAction(tr("Constant Interpolation"), 0);
Shinya Kitaoka 120a6e
  QAction setSimilarShapeAction(tr("Similar Shape Interpolation"), 0);
Shinya Kitaoka 120a6e
  QAction fitSelectedAction(tr("Fit Selection"), 0);
Shinya Kitaoka 120a6e
  QAction fitAllAction(tr("Fit"), 0);
Shinya Kitaoka 120a6e
  QAction setStep1Action(tr("Step 1"), 0);
Shinya Kitaoka 120a6e
  QAction setStep2Action(tr("Step 2"), 0);
Shinya Kitaoka 120a6e
  QAction setStep3Action(tr("Step 3"), 0);
Shinya Kitaoka 120a6e
  QAction setStep4Action(tr("Step 4"), 0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TDoubleParam *curve = getCurrentCurve();
Shinya Kitaoka 120a6e
  int segmentIndex    = -1;
Shinya Kitaoka 120a6e
  if (!curve) return;
Shinya Kitaoka 120a6e
  TDoubleKeyframe kf;
Shinya Kitaoka 120a6e
  double frame = xToFrame(e->pos().x());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // build menu
Shinya Kitaoka 120a6e
  QMenu menu(0);
Shinya Kitaoka 120a6e
  if (m_highlighted.handle == Point && m_highlighted.gIndex >= 0 &&
Shinya Kitaoka 120a6e
      m_gadgets[m_highlighted.gIndex].m_handle != 100) {
Shinya Kitaoka 120a6e
    kf = curve->getKeyframe(m_gadgets[m_highlighted.gIndex].m_kIndex);
Shinya Kitaoka 120a6e
    if (kf.m_linkedHandles)
Shinya Kitaoka 120a6e
      menu.addAction(&unlinkHandlesAction);
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      menu.addAction(&linkHandlesAction);
Shinya Kitaoka 120a6e
    menu.addAction(&resetHandlesAction);
Shinya Kitaoka 120a6e
    menu.addAction(&deleteKeyframeAction);
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    int k0 = curve->getPrevKeyframe(frame);
Shinya Kitaoka 120a6e
    int k1 = curve->getNextKeyframe(frame);
Shinya Kitaoka 120a6e
    if (k0 == curve->getKeyframeCount() - 1)  // after last keyframe
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      if (curve->isCycleEnabled())
Shinya Kitaoka 120a6e
        menu.addAction(&deactivateCycleAction);
Shinya Kitaoka 120a6e
      else
Shinya Kitaoka 120a6e
        menu.addAction(&activateCycleAction);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    menu.addAction(&insertKeyframeAction);
Shinya Kitaoka 120a6e
    if (k0 >= 0 && k1 >= 0) {
Shinya Kitaoka 120a6e
      menu.addSeparator();
Shinya Kitaoka 120a6e
      segmentIndex = k0;
Shinya Kitaoka 120a6e
      kf           = curve->getKeyframe(k0);
Shinya Kitaoka 120a6e
      menu.addAction(&setLinearAction);
Shinya Kitaoka 120a6e
      if (kf.m_type == TDoubleKeyframe::Linear)
Shinya Kitaoka 120a6e
        setLinearAction.setEnabled(false);
Shinya Kitaoka 120a6e
      menu.addAction(&setSpeedInOutAction);
Shinya Kitaoka 120a6e
      if (kf.m_type == TDoubleKeyframe::SpeedInOut)
Shinya Kitaoka 120a6e
        setSpeedInOutAction.setEnabled(false);
Shinya Kitaoka 120a6e
      menu.addAction(&setEaseInOutAction);
Shinya Kitaoka 120a6e
      if (kf.m_type == TDoubleKeyframe::EaseInOut)
Shinya Kitaoka 120a6e
        setEaseInOutAction.setEnabled(false);
Shinya Kitaoka 120a6e
      menu.addAction(&setEaseInOut2Action);
Shinya Kitaoka 120a6e
      if (kf.m_type == TDoubleKeyframe::EaseInOutPercentage)
Shinya Kitaoka 120a6e
        setEaseInOut2Action.setEnabled(false);
Shinya Kitaoka 120a6e
      menu.addAction(&setExponentialAction);
Shinya Kitaoka 120a6e
      if (kf.m_type == TDoubleKeyframe::Exponential)
Shinya Kitaoka 120a6e
        setExponentialAction.setEnabled(false);
Shinya Kitaoka 120a6e
      menu.addAction(&setExpressionAction);
Shinya Kitaoka 120a6e
      if (kf.m_type == TDoubleKeyframe::Expression)
Shinya Kitaoka 120a6e
        setExpressionAction.setEnabled(false);
Shinya Kitaoka 120a6e
      menu.addAction(&setSimilarShapeAction);
Shinya Kitaoka 120a6e
      if (kf.m_type == TDoubleKeyframe::SimilarShape)
Shinya Kitaoka 120a6e
        setSimilarShapeAction.setEnabled(false);
Shinya Kitaoka 120a6e
      menu.addAction(&setFileAction);
Shinya Kitaoka 120a6e
      if (kf.m_type == TDoubleKeyframe::File) setFileAction.setEnabled(false);
Shinya Kitaoka 120a6e
      menu.addAction(&setConstantAction);
Shinya Kitaoka 120a6e
      if (kf.m_type == TDoubleKeyframe::Constant)
Shinya Kitaoka 120a6e
        setConstantAction.setEnabled(false);
Shinya Kitaoka 120a6e
      menu.addSeparator();
Shinya Kitaoka 120a6e
      if (kf.m_step != 1) menu.addAction(&setStep1Action);
Shinya Kitaoka 120a6e
      if (kf.m_step != 2) menu.addAction(&setStep2Action);
Shinya Kitaoka 120a6e
      if (kf.m_step != 3) menu.addAction(&setStep3Action);
Shinya Kitaoka 120a6e
      if (kf.m_step != 4) menu.addAction(&setStep4Action);
Shinya Kitaoka 120a6e
      menu.addSeparator();
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  if (!getSelection()->isEmpty()) menu.addAction(&fitSelectedAction);
Shinya Kitaoka 120a6e
  menu.addAction(&fitAllAction);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // curve shape type
Shinya Kitaoka 120a6e
  QAction curveShapeSmoothAction(tr("Smooth"), 0);
Shinya Kitaoka 120a6e
  QAction curveShapeFrameBasedAction(tr("Frame Based"), 0);
Shinya Kitaoka 120a6e
  QMenu curveShapeSubmenu(tr("Curve Shape"), 0);
Shinya Kitaoka 120a6e
  menu.addSeparator();
Shinya Kitaoka 120a6e
  curveShapeSubmenu.addAction(&curveShapeSmoothAction);
Shinya Kitaoka 120a6e
  curveShapeSubmenu.addAction(&curveShapeFrameBasedAction);
Shinya Kitaoka 120a6e
  menu.addMenu(&curveShapeSubmenu);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  curveShapeSmoothAction.setCheckable(true);
Shinya Kitaoka 120a6e
  curveShapeSmoothAction.setChecked(m_curveShape == SMOOTH);
Shinya Kitaoka 120a6e
  curveShapeFrameBasedAction.setCheckable(true);
Shinya Kitaoka 120a6e
  curveShapeFrameBasedAction.setChecked(m_curveShape == FRAME_BASED);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Store m_highlighted due to the following exec()
Shinya Kitaoka 120a6e
  Highlighted highlighted(m_highlighted);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // execute menu
Shinya Kitaoka 120a6e
  QAction *action = menu.exec(e->globalPos());  // Will process events, possibly
Shinya Kitaoka 120a6e
                                                // altering m_highlighted
Shinya Kitaoka 120a6e
                                                // (MAC-verified)
Shinya Kitaoka 120a6e
  if (action == &linkHandlesAction)  // Let's just *hope* that doesn't happen to
Shinya Kitaoka 120a6e
                                     // m_gadgets though...  :/
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    if (m_gadgets[highlighted.gIndex].m_handle != 100)
Shinya Kitaoka 120a6e
      KeyframeSetter(curve, m_gadgets[highlighted.gIndex].m_kIndex)
Shinya Kitaoka 120a6e
          .linkHandles();
Shinya Kitaoka 120a6e
  } else if (action == &unlinkHandlesAction) {
Shinya Kitaoka 120a6e
    if (m_gadgets[highlighted.gIndex].m_handle != 100)
Shinya Kitaoka 120a6e
      KeyframeSetter(curve, m_gadgets[highlighted.gIndex].m_kIndex)
Shinya Kitaoka 120a6e
          .unlinkHandles();
Shinya Kitaoka 120a6e
  } else if (action == &resetHandlesAction) {
Shinya Kitaoka 120a6e
    kf.m_speedIn  = TPointD(-5, 0);
Shinya Kitaoka 120a6e
    kf.m_speedOut = -kf.m_speedIn;
Shinya Kitaoka 120a6e
    curve->setKeyframe(kf);
Shinya Kitaoka 120a6e
  } else if (action == &deleteKeyframeAction) {
Shinya Kitaoka 120a6e
    KeyframeSetter::removeKeyframeAt(curve, kf.m_frame);
Shinya Kitaoka 120a6e
  } else if (action == &insertKeyframeAction) {
Shinya Kitaoka 120a6e
    KeyframeSetter(curve).createKeyframe(tround(frame));
Shinya Kitaoka 120a6e
  } else if (action == &activateCycleAction) {
Shinya Kitaoka 120a6e
    KeyframeSetter::enableCycle(curve, true);
Shinya Kitaoka 120a6e
  } else if (action == &deactivateCycleAction) {
Shinya Kitaoka 120a6e
    KeyframeSetter::enableCycle(curve, false);
Shinya Kitaoka 120a6e
  } else if (action == &setLinearAction)
Shinya Kitaoka 120a6e
    setSegmentType(getSelection(), curve, segmentIndex,
Shinya Kitaoka 120a6e
                   TDoubleKeyframe::Linear);
Shinya Kitaoka 120a6e
  else if (action == &setSpeedInOutAction)
Shinya Kitaoka 120a6e
    setSegmentType(getSelection(), curve, segmentIndex,
Shinya Kitaoka 120a6e
                   TDoubleKeyframe::SpeedInOut);
Shinya Kitaoka 120a6e
  else if (action == &setEaseInOutAction)
Shinya Kitaoka 120a6e
    setSegmentType(getSelection(), curve, segmentIndex,
Shinya Kitaoka 120a6e
                   TDoubleKeyframe::EaseInOut);
Shinya Kitaoka 120a6e
  else if (action == &setEaseInOut2Action)
Shinya Kitaoka 120a6e
    setSegmentType(getSelection(), curve, segmentIndex,
Shinya Kitaoka 120a6e
                   TDoubleKeyframe::EaseInOutPercentage);
Shinya Kitaoka 120a6e
  else if (action == &setExponentialAction)
Shinya Kitaoka 120a6e
    setSegmentType(getSelection(), curve, segmentIndex,
Shinya Kitaoka 120a6e
                   TDoubleKeyframe::Exponential);
Shinya Kitaoka 120a6e
  else if (action == &setExpressionAction)
Shinya Kitaoka 120a6e
    setSegmentType(getSelection(), curve, segmentIndex,
Shinya Kitaoka 120a6e
                   TDoubleKeyframe::Expression);
Shinya Kitaoka 120a6e
  else if (action == &setSimilarShapeAction)
Shinya Kitaoka 120a6e
    setSegmentType(getSelection(), curve, segmentIndex,
Shinya Kitaoka 120a6e
                   TDoubleKeyframe::SimilarShape);
Shinya Kitaoka 120a6e
  else if (action == &setFileAction)
Shinya Kitaoka 120a6e
    setSegmentType(getSelection(), curve, segmentIndex, TDoubleKeyframe::File);
Shinya Kitaoka 120a6e
  else if (action == &setConstantAction)
Shinya Kitaoka 120a6e
    setSegmentType(getSelection(), curve, segmentIndex,
Shinya Kitaoka 120a6e
                   TDoubleKeyframe::Constant);
Shinya Kitaoka 120a6e
  else if (action == &fitSelectedAction)
Shinya Kitaoka 120a6e
    fitSelectedPoints();
Shinya Kitaoka 120a6e
  else if (action == &fitAllAction)
Shinya Kitaoka 120a6e
    fitCurve();
Shinya Kitaoka 120a6e
  else if (action == &setStep1Action)
Shinya Kitaoka 120a6e
    KeyframeSetter(curve, segmentIndex).setStep(1);
Shinya Kitaoka 120a6e
  else if (action == &setStep2Action)
Shinya Kitaoka 120a6e
    KeyframeSetter(curve, segmentIndex).setStep(2);
Shinya Kitaoka 120a6e
  else if (action == &setStep3Action)
Shinya Kitaoka 120a6e
    KeyframeSetter(curve, segmentIndex).setStep(3);
Shinya Kitaoka 120a6e
  else if (action == &setStep4Action)
Shinya Kitaoka 120a6e
    KeyframeSetter(curve, segmentIndex).setStep(4);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  else if (action == &curveShapeSmoothAction)
Shinya Kitaoka 120a6e
    m_curveShape = SMOOTH;
Shinya Kitaoka 120a6e
  else if (action == &curveShapeFrameBasedAction)
Shinya Kitaoka 120a6e
    m_curveShape = FRAME_BASED;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  update();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::setFrameHandle(TFrameHandle *frameHandle) {
Shinya Kitaoka 120a6e
  if (m_frameHandle == frameHandle) return;
Shinya Kitaoka 120a6e
  if (m_frameHandle) m_frameHandle->disconnect(this);
Shinya Kitaoka 120a6e
  m_frameHandle = frameHandle;
Shinya Kitaoka 120a6e
  if (isVisible() && m_frameHandle) {
Shinya Kitaoka 120a6e
    connect(m_frameHandle, SIGNAL(frameSwitched()), this,
Shinya Kitaoka 120a6e
            SLOT(onFrameSwitched()));
Shinya Kitaoka 120a6e
    update();
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  assert(m_selection);
Shinya Kitaoka 120a6e
  m_selection->setFrameHandle(frameHandle);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::showEvent(QShowEvent *) {
Shinya Kitaoka 120a6e
  if (m_frameHandle)
Shinya Kitaoka 120a6e
    connect(m_frameHandle, SIGNAL(frameSwitched()), this,
Shinya Kitaoka 120a6e
            SLOT(onFrameSwitched()));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::hideEvent(QHideEvent *) {
Shinya Kitaoka 120a6e
  if (m_frameHandle) m_frameHandle->disconnect(this);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void FunctionPanel::onFrameSwitched() { update(); }