Blob Blame Raw


#include "filmstrip.h"

// Tnz6 includes
#include "tapp.h"
#include "filmstripcommand.h"
#include "frameheadgadget.h"
#include "floatingpanelcommand.h"
#include "menubarcommandids.h"
#include "filmstripselection.h"
#include "onionskinmaskgui.h"
#include "comboviewerpane.h"

// TnzQt includes
#include "toonzqt/icongenerator.h"
#include "toonzqt/trepetitionguard.h"
#include "toonzqt/gutil.h"
#include "toonzqt/tselectionhandle.h"

// TnzLib includes
#include "toonz/txshlevelhandle.h"
#include "toonz/tcolumnhandle.h"
#include "toonz/txsheethandle.h"
#include "toonz/tframehandle.h"
#include "toonz/tonionskinmaskhandle.h"
#include "toonz/tobjecthandle.h"
#include "toonz/txshleveltypes.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/stage2.h"
#include "toonz/levelproperties.h"
#include "toonz/palettecontroller.h"
#include "toonz/tpalettehandle.h"
#include "toonz/tscenehandle.h"
#include "toonz/toonzscene.h"
#include "toonz/levelset.h"

// TnzCore includes
#include "tpalette.h"

// Qt includes
#include <QPainter>
#include <QScrollBar>
#include <QComboBox>
#include <QPushButton>
#include <QSettings>
#include <QApplication>
#include <QMainWindow>
#include <QMimeData>
#include <QDrag>

//=============================================================================
// Filmstrip
//-----------------------------------------------------------------------------

#if QT_VERSION >= 0x050500
FilmstripFrames::FilmstripFrames(QScrollArea *parent, Qt::WindowFlags flags)
#else
FilmstripFrames::FilmstripFrames(QScrollArea *parent, Qt::WFlags flags)
#endif
	: QFrame(parent, flags), m_scrollArea(parent), m_selection(new TFilmstripSelection()), m_frameHeadGadget(0), m_inbetweenDialog(0), m_pos(0, 0), m_iconSize(dimension2QSize(IconGenerator::instance()->getIconSize())), m_frameLabelWidth(11), m_selectingRange(0, 0), m_scrollSpeed(0), m_dragSelectionStartIndex(-1), m_dragSelectionEndIndex(-1), m_timerId(0), m_selecting(false), m_dragDropArmed(false), m_readOnly(false)
{
	setObjectName("filmStripFrames");
	setFrameStyle(QFrame::StyledPanel);

	setFocusPolicy(Qt::StrongFocus);
	setFixedWidth(m_iconSize.width() + fs_leftMargin + fs_rightMargin + fs_iconMarginLR * 2);
	setFixedHeight(parentWidget()->height());

	// la testa mobile che indica il frame corrente (e gestisce la GUI dell'onion skin)
	m_frameHeadGadget = new FilmstripFrameHeadGadget(this);
	installEventFilter(m_frameHeadGadget);

	m_selection->setView(this);
	setMouseTracking(true);
}

//-----------------------------------------------------------------------------

FilmstripFrames::~FilmstripFrames()
{
	delete m_selection;
	delete m_frameHeadGadget;
}

//-----------------------------------------------------------------------------

TXshSimpleLevel *FilmstripFrames::getLevel() const
{
	TXshLevel *xl = TApp::instance()->getCurrentLevel()->getLevel();
	return xl ? xl->getSimpleLevel() : 0;
}

//-----------------------------------------------------------------------------

int FilmstripFrames::y2index(int y) const
{
	const int dy = getIconSize().height() +
				   fs_frameSpacing +
				   fs_iconMarginTop +
				   fs_iconMarginBottom;
	return y / dy;
}

//-----------------------------------------------------------------------------

int FilmstripFrames::index2y(int index) const
{
	const int dy = getIconSize().height() +
				   fs_frameSpacing +
				   fs_iconMarginTop +
				   fs_iconMarginBottom;
	return index * dy;
}

//-----------------------------------------------------------------------------

TFrameId FilmstripFrames::index2fid(int index) const
{
	TXshSimpleLevel *sl = getLevel();
	if (!sl || index < 0)
		return TFrameId();
	return sl->index2fid(index);
}

//-----------------------------------------------------------------------------

int FilmstripFrames::fid2index(const TFrameId &fid) const
{
	TXshSimpleLevel *sl = getLevel();
	if (!sl)
		return -1;
	return sl->guessIndex(fid); // ATTENZIONE: dovrebbe usare fid2index()
}

//-----------------------------------------------------------------------------

int FilmstripFrames::getFramesHeight() const
{
	TXshSimpleLevel *level = getLevel();
	int frameCount = level ? level->getFrameCount() : 1;
	int frameHeight = m_iconSize.height() +
					  fs_frameSpacing +
					  fs_iconMarginTop +
					  fs_iconMarginBottom;
	return frameHeight * (frameCount + 1);
}

//-----------------------------------------------------------------------------

int FilmstripFrames::getOneFrameHeight()
{
	return m_iconSize.height() +
		   fs_frameSpacing +
		   fs_iconMarginTop +
		   fs_iconMarginBottom;
}

//-----------------------------------------------------------------------------
void FilmstripFrames::updateContentHeight(int minimumHeight)
{
	if (minimumHeight < 0)
		minimumHeight = visibleRegion().boundingRect().bottom();
	int contentHeight = getFramesHeight();
	if (contentHeight < minimumHeight)
		contentHeight = minimumHeight;
	int parentHeight = parentWidget()->height();
	if (contentHeight < parentHeight)
		contentHeight = parentHeight;
	if (contentHeight != height())
		setFixedHeight(contentHeight);
}

//-----------------------------------------------------------------------------

void FilmstripFrames::exponeFrame(int index)
{
	int y0 = index2y(index);
	int y1 = y0 + m_iconSize.height() +
			 fs_frameSpacing +
			 fs_iconMarginTop +
			 fs_iconMarginBottom;
	if (y1 > height())
		setFixedHeight(y1);
	// else if(height()-y1>=m_iconSize.height()) updateContentHeight();
	m_scrollArea->ensureVisible(0, (y0 + y1) / 2, 50, (y1 - y0) / 2);
}

//-----------------------------------------------------------------------------

void FilmstripFrames::scroll(int dy)
{
	QScrollBar *sb = m_scrollArea->verticalScrollBar();
	int sbValue = sb->value();
	int y = visibleRegion().boundingRect().bottom() + dy + 1;
	if (y < 0)
		y = 0;
	updateContentHeight(y);
	sb->setValue(sbValue + dy);
}

//---------------------------------------------------------------------------

void FilmstripFrames::mouseDoubleClickEvent(QMouseEvent *event)
{
	int index = y2index(event->pos().y());
	select(index, ONLY_SELECT); //ONLY_SELECT
}

//-----------------------------------------------------------------------------

void FilmstripFrames::select(int index, SelectionMode mode)
{
	TXshSimpleLevel *sl = getLevel();

	bool outOfRange = !sl || index < 0 || index >= sl->getFrameCount();

	TFrameId fid;
	if (!outOfRange)
		fid = index2fid(index);

	switch (mode) {
	//select one frame only
	case ONLY_SELECT:
		m_selection->selectNone();
		if (!outOfRange)
			m_selection->select(fid);
		break;
	case SIMPLE_SELECT: {
		// Bail out if fid is already selected
		if (!outOfRange && m_selection->isSelected(fid))
			return;

		m_selection->selectNone();
		if (!outOfRange)
			m_selection->select(fid);
	}

		CASE SHIFT_SELECT:
		{
			if (outOfRange)
				return;

			// Bail out if fid is already selected
			if (m_selection->isSelected(fid))
				return;

			if (m_selection->isEmpty())
				m_selection->select(fid);
			else {
				TXshSimpleLevel *sl = getLevel();
				if (!sl)
					return;

				// seleziono il range da fid al piu' vicino frame selezionato (in entrambe le direzioni)
				int frameCount = sl->getFrameCount();

				// calcolo il limite inferiore della selezione
				int ia = index;
				while (ia > 0 && !m_selection->isSelected(sl->index2fid(ia - 1)))
					ia--;
				if (ia == 0)
					ia = index;

				// calcolo il limite superiore della selezione
				int ib = index;
				while (ib < frameCount - 1 && !m_selection->isSelected(sl->index2fid(ib + 1)))
					ib++;
				if (ib == frameCount - 1)
					ib = index;

				// seleziono
				for (int i = ia; i <= ib; i++)
					m_selection->select(sl->index2fid(i));
			}
		}

		CASE CTRL_SELECT:
		{
			if (outOfRange)
				return;

			m_selection->select(fid, !m_selection->isSelected(fid));
		}

		CASE START_DRAG_SELECT:
		{
			m_selection->selectNone();

			if (outOfRange) {
				m_dragSelectionStartIndex =
					m_dragSelectionEndIndex = -1;

				break;
			}

			m_selection->select(fid);
			m_dragSelectionStartIndex = index;
		}

		CASE DRAG_SELECT:
		{
			if (outOfRange ||
				m_dragSelectionStartIndex < 0 ||
				m_dragSelectionEndIndex == index)
				return;

			m_dragSelectionEndIndex = index;

			m_selection->selectNone();

			int ia = m_dragSelectionStartIndex;
			int ib = index;

			if (ia > ib)
				std::swap(ia, ib);

			for (int i = ia; i <= ib; ++i)
				m_selection->select(index2fid(i));
		}
	}

	TObjectHandle *objectHandle = TApp::instance()->getCurrentObject();
	if (objectHandle->isSpline())
		objectHandle->setIsSpline(false);

	// Update current selection
	m_selection->makeCurrent();

	TSelectionHandle *selHandle = TApp::instance()->getCurrentSelection();
	selHandle->notifySelectionChanged();
}

//-----------------------------------------------------------------------------

void FilmstripFrames::showEvent(QShowEvent *)
{
	TApp *app = TApp::instance();

	// cambiamenti al livello
	TXshLevelHandle *levelHandle = app->getCurrentLevel();
	connect(levelHandle, SIGNAL(xshLevelSwitched(TXshLevel *)), this, SLOT(onLevelSwitched(TXshLevel *)));
	connect(levelHandle, SIGNAL(xshLevelChanged()), this, SLOT(onLevelChanged()));
	connect(levelHandle, SIGNAL(xshLevelViewChanged()), this, SLOT(onLevelChanged()));

	// al frame corrente
	connect(app->getCurrentFrame(), SIGNAL(frameSwitched()), this, SLOT(onFrameSwitched()));
	connect(app->getCurrentFrame(), SIGNAL(frameTypeChanged()), this, SLOT(update()));

	// iconcine
	connect(IconGenerator::instance(), SIGNAL(iconGenerated()), this, SLOT(update()));

	// onion skin
	connect(app->getCurrentOnionSkin(), SIGNAL(onionSkinMaskChanged()),
			this, SLOT(update()));

	// enable navigator link with the Viewer in the InknPaint page
	ComboViewerPanel *inknPaintViewerPanel = app->getInknPaintViewerPanel();
	if (inknPaintViewerPanel) {
		SceneViewer *viewer = inknPaintViewerPanel->getSceneViewer();
		if (viewer) {
			connect(viewer, SIGNAL(onZoomChanged()), this, SLOT(update()));
			connect(viewer, SIGNAL(refreshNavi()), this, SLOT(update()));
		}
	}
}

//-----------------------------------------------------------------------------

void FilmstripFrames::hideEvent(QHideEvent *)
{
	TApp *app = TApp::instance();

	// cambiamenti al livello
	disconnect(app->getCurrentLevel());

	// al frame corrente
	disconnect(app->getCurrentFrame(), SIGNAL(frameSwitched()), this, SLOT(onFrameSwitched()));
	disconnect(app->getCurrentFrame(), SIGNAL(frameTypeChanged()), this, SLOT(update()));

	// iconcine
	disconnect(IconGenerator::instance(), SIGNAL(iconGenerated()), this, SLOT(update()));

	// onion skin
	disconnect(app->getCurrentOnionSkin(), SIGNAL(onionSkinMaskChanged()), this, SLOT(update()));

	ComboViewerPanel *inknPaintViewerPanel = app->getInknPaintViewerPanel();
	if (inknPaintViewerPanel) {
		SceneViewer *viewer = inknPaintViewerPanel->getSceneViewer();
		if (viewer) {
			disconnect(viewer, SIGNAL(onZoomChanged()), this, SLOT(update()));
			disconnect(viewer, SIGNAL(refreshNavi()), this, SLOT(update()));
		}
	}
}

//-----------------------------------------------------------------------------

void FilmstripFrames::paintEvent(QPaintEvent *evt)
{
	QPainter p(this);

	// p.setRenderHint(QPainter::Antialiasing, true);

	QRect clipRect = evt->rect();

	p.fillRect(clipRect, Qt::black);
	// thumbnail rect, including offsets
	QRect iconImgRect = QRect(QPoint(fs_leftMargin + fs_iconMarginLR, fs_frameSpacing / 2 + fs_iconMarginTop), m_iconSize);
	// frame size with margins
	QSize frameSize = m_iconSize + QSize(fs_iconMarginLR * 2, fs_iconMarginTop + fs_iconMarginBottom);
	//  .. and with offset
	QRect frameRect = QRect(QPoint(fs_leftMargin, fs_frameSpacing / 2), frameSize);

	int oneFrameHeight = frameSize.height() + fs_frameSpacing;

	// visible frame index range
	int i0 = y2index(clipRect.top());
	int i1 = y2index(clipRect.bottom());

	// fids, frameCount <- frames del livello
	std::vector<TFrameId> fids;
	TXshSimpleLevel *sl = getLevel();
	if (sl)
		sl->getFids(fids);
	else {
		for (int i = i0; i <= i1; i++) {
			// draw white rectangles if obtaining the level is failed
			QRect iconRect = frameRect.translated(QPoint(0, oneFrameHeight * i));
			p.setBrush(QColor(192, 192, 192));
			p.setPen(Qt::NoPen);
			p.drawRect(iconRect);
		}
		return;
	}

	//--- compute navigator rect ---

	QRect naviRect;
	ComboViewerPanel *inknPaintViewerPanel = TApp::instance()->getInknPaintViewerPanel();
	if (sl->getType() == TZP_XSHLEVEL && inknPaintViewerPanel) {
		// show navigator only if the inknpaint viewer is visible
		if (inknPaintViewerPanel->isVisible()) {
			SceneViewer *viewer = inknPaintViewerPanel->getSceneViewer();
			//imgSize: image's pixel size
			QSize imgSize(sl->getProperties()->getImageRes().lx, sl->getProperties()->getImageRes().ly);
			//Viewer affine
			TAffine viewerAff = inknPaintViewerPanel->getSceneViewer()->getViewMatrix();
			// pixel size which will be displayed with 100% scale in Viewer Stage
			TFrameId currentId = TApp::instance()->getCurrentFrame()->getFid();
			double imgPixelWidth = (double)(imgSize.width()) / sl->getDpi(currentId).x * Stage::inch;
			double imgPixelHeight = (double)(imgSize.height()) / sl->getDpi(currentId).y * Stage::inch;

			//get the image's corner positions in viewer matrix (with current zoom scale)
			TPointD imgTopRight = viewerAff * TPointD(imgPixelWidth / 2.0f, imgPixelHeight / 2.0f);
			TPointD imgBottomLeft = viewerAff * TPointD(-imgPixelWidth / 2.0f, -imgPixelHeight / 2.0f);

			//pixel size in viewer matrix ( with current zoom scale )
			QSizeF imgSizeInViewer(imgTopRight.x - imgBottomLeft.x, imgTopRight.y - imgBottomLeft.y);

			//ratio of the Viewer frame's position and size
			QRectF naviRatio((-(float)viewer->width() * 0.5f - (float)imgBottomLeft.x) / imgSizeInViewer.width(),
							 1.0f - ((float)viewer->height() * 0.5f - (float)imgBottomLeft.y) / imgSizeInViewer.height(),
							 (float)viewer->width() / imgSizeInViewer.width(),
							 (float)viewer->height() / imgSizeInViewer.height());

			naviRect = QRect(iconImgRect.left() + (int)(naviRatio.left() * (float)iconImgRect.width()),
							 iconImgRect.top() + (int)(naviRatio.top() * (float)iconImgRect.height()),
							 (int)((float)iconImgRect.width() * naviRatio.width()),
							 (int)((float)iconImgRect.height() * naviRatio.height()));
			//for drag move
			m_naviRectPos = naviRect.center();

			naviRect = naviRect.intersected(frameRect);

			m_icon2ViewerRatio.setX(imgSizeInViewer.width() / (float)iconImgRect.width());
			m_icon2ViewerRatio.setY(imgSizeInViewer.height() / (float)iconImgRect.height());
		}
	}

	//--- compute navigator rect end ---

	int frameCount = (int)fids.size();

	std::set<TFrameId> editableFrameRange;

	if (sl)
		editableFrameRange = sl->getEditableRange();

	bool isReadOnly = false;
	if (sl)
		isReadOnly = sl->isReadOnly();

	int i;
	int iconWidth = m_iconSize.width();
	int x0 = m_frameLabelWidth;
	int x1 = x0 + iconWidth;
	int frameHeight = m_iconSize.height();

	// linee orizzontali che separano i frames
	p.setPen(getLightLineColor());
	for (i = i0; i <= i1; i++) {
		int y = index2y(i) + frameHeight;
		p.drawLine(0, y, x1, y);
	}

	TFilmstripSelection::InbetweenRange range = m_selection->getInbetweenRange();

	// draw for each frames
	for (i = i0; i <= i1; i++) {
		QRect tmp_iconImgRect = iconImgRect.translated(QPoint(0, oneFrameHeight * i));
		QRect tmp_frameRect = frameRect.translated(QPoint(0, oneFrameHeight * i));

		bool isCurrentFrame = (i == sl->fid2index(TApp::instance()->getCurrentFrame()->getFid()));
		bool isSelected = (0 <= i && i < frameCount && m_selection->isSelected(fids[i]));

		if (0 <= i && i < frameCount) {
			TFrameId fid = fids[i];

			//normal or inbetween (for vector levels)
			int flags = (sl->getType() == PLI_XSHLEVEL && range.first < fid && fid < range.second)
							? F_INBETWEEN_RANGE
							: F_NORMAL;

			// draw icons
			drawFrameIcon(p, tmp_iconImgRect, i, fid, flags);

			p.setPen(Qt::NoPen);
			p.setBrush(Qt::NoBrush);
			p.drawRect(tmp_iconImgRect);

			//Frame number
			if (m_selection->isSelected(fids[i])) {
				if (TApp::instance()->getCurrentFrame()->isEditingLevel() && isCurrentFrame)
					p.setPen(Qt::red);
				else
					p.setPen(Qt::white);
			} else
				p.setPen(QColor(192, 192, 192));

			p.setBrush(Qt::NoBrush);
			//for single frame
			if (fid.getNumber() == TFrameId::EMPTY_FRAME || fid.getNumber() == TFrameId::NO_FRAME) {
				p.drawText(tmp_frameRect.adjusted(0, 0, -3, 2),
						   "Single Frame",
						   QTextOption(Qt::AlignRight | Qt::AlignBottom));
			}
			//for sequencial frame
			else {
				p.drawText(tmp_frameRect.adjusted(0, 0, -3, 2),
						   QString().setNum(fid.getNumber()).rightJustified(4, '0'),
						   QTextOption(Qt::AlignRight | Qt::AlignBottom));
			}
			p.setPen(Qt::NoPen);

			// Read-only frames (lock)
			if (0 <= i && i < frameCount) {
				if ((editableFrameRange.empty() && isReadOnly) || (isReadOnly &&
																   (!editableFrameRange.empty() && editableFrameRange.count(fids[i]) == 0))) {
					static QPixmap lockPixmap(":Resources/forbidden.png");
					p.drawPixmap(tmp_frameRect.bottomLeft() + QPoint(3, -13), lockPixmap);
				}
			}
		}

		//navigator rect
		if (naviRect.isValid() && isCurrentFrame) {
			p.setPen(QPen(Qt::red, 1));
			p.drawRect(naviRect.translated(0, oneFrameHeight * i));
			p.setPen(Qt::NoPen);
		}

		//red frame for the current frame
		if (TApp::instance()->getCurrentFrame()->isEditingLevel() && (isCurrentFrame || isSelected)) {
			QPen pen;
			pen.setColor(Qt::red);
			pen.setWidth(2);
			pen.setJoinStyle(Qt::RoundJoin);
			p.setPen(pen);

			p.drawRect(tmp_frameRect.adjusted(-1, -1, 2, 2));
			p.setPen(Qt::NoPen);
		}
	}

	// se sono in modalita' level edit faccio vedere la freccia che indica il frame corrente
	if (TApp::instance()->getCurrentFrame()->isEditingLevel())
		m_frameHeadGadget->draw(p, QColor(Qt::white), QColor(Qt::black));
}

//-----------------------------------------------------------------------------

void FilmstripFrames::drawFrameIcon(
	QPainter &p,
	const QRect &r,
	int index,
	const TFrameId &fid,
	int flags)
{
	QPixmap pm;
	TXshSimpleLevel *sl = getLevel();
	if (sl) {
		pm = IconGenerator::instance()->getIcon(sl, fid);
	}
	if (!pm.isNull()) {
		p.drawPixmap(r.left(), r.top(), pm);

		if (sl && sl->getType() == PLI_XSHLEVEL && flags & F_INBETWEEN_RANGE) {
			int x1 = r.right();
			int x0 = x1 - 12;
			int y0 = r.top();
			int y1 = r.bottom();
			p.fillRect(x0, y0, x1 - x0 + 1, y1 - y0 + 1, QColor(180, 180, 180, 255));
			p.setPen(Qt::black);
			p.drawLine(x0 - 1, y0, x0 - 1, y1);

			QPixmap inbetweenPixmap(":Resources/filmstrip_inbetween.png");
			p.drawPixmap(x0 + 2, y1 - inbetweenPixmap.height() - 3, inbetweenPixmap);
		}
	} else {
		// non riesco (per qualche ragione) a visualizzare l'icona
		p.fillRect(r, QColor(255, 200, 200));
		p.setPen(Qt::black);
		p.drawText(r, tr("no icon"), QTextOption(Qt::AlignCenter));
	}
}

//-----------------------------------------------------------------------------

void FilmstripFrames::mousePressEvent(QMouseEvent *event)
{
	m_selecting = false;
	int index = y2index(event->pos().y());
	TFrameId fid = index2fid(index);

	TXshSimpleLevel *sl = getLevel();
	int i0 = y2index(0);
	int frameHeight = m_iconSize.height() + fs_frameSpacing + fs_iconMarginTop + fs_iconMarginBottom;
	QPoint clickedPos = event->pos() - QPoint(0, (index - i0) * frameHeight);

	if (event->button() == Qt::LeftButton) {
		if (fid == TFrameId())
			return;

		ComboViewerPanel *inknPaintViewerPanel = TApp::instance()->getInknPaintViewerPanel();

		// navigator pan
		if (fid == TApp::instance()->getCurrentFrame()->getFid() && sl->getType() == TZP_XSHLEVEL && inknPaintViewerPanel && inknPaintViewerPanel->isVisible() && QRect(QPoint(fs_leftMargin + fs_iconMarginLR,
																																											   fs_frameSpacing / 2 + fs_iconMarginTop) //<- top-left position of the icon
																																										,
																																										m_iconSize)
																																									  .contains(clickedPos)) {
			m_isNavigatorPanning = true;
			execNavigatorPan(event->pos());
			QApplication::setOverrideCursor(Qt::ClosedHandCursor);
		} else
			m_isNavigatorPanning = false;

		if (event->modifiers() & Qt::ShiftModifier) {
			select(index, SHIFT_SELECT);
			if (m_selection->isSelected(fid)) {
				// click su di un frame gia' selezionato. Puo' essere l'inizio di un drag'n'drop
				m_dragDropArmed = true;
				m_pos = event->pos();
			}
		} else if (event->modifiers() & Qt::ControlModifier)
			select(index, CTRL_SELECT);
		//inbetween
		else if (sl->getType() == PLI_XSHLEVEL && m_selection->isInInbetweenRange(fid) && event->pos().x() > width() - 12 - fs_rightMargin) {
			inbetween();
		} else {
			//move current frame when clicked without modifier
			TApp *tapp = TApp::instance();
			std::vector<TFrameId> fids;
			TXshLevel *level = tapp->getCurrentLevel()->getLevel();
			level->getFids(fids);

			tapp->getCurrentFrame()->setFrameIds(fids);
			tapp->getCurrentFrame()->setFid(fid);

			if (!m_selection->isSelected(fid)) // selezione semplice
			{
				// click su un frame non selezionato
				m_selecting = true; // posso estendere la selezione con il drag
				select(index, START_DRAG_SELECT);
			} else if (m_selection->isSelected(fid)) {
				// click su di un frame gia' selezionato. Puo' essere l'inizio di un drag'n'drop
				m_dragDropArmed = true;
				m_pos = event->pos();
			}
		}
		update();
	} else if (event->button() == Qt::MidButton) {
		m_pos = event->globalPos();
		return;
	} else if (event->button() == Qt::RightButton) {
		select(index);
	}
}

//-----------------------------------------------------------------------------

void FilmstripFrames::execNavigatorPan(const QPoint &point)
{
	int index = y2index(point.y());
	TFrameId fid = index2fid(index);
	int i0 = y2index(0);

	int frameHeight = m_iconSize.height() + fs_frameSpacing + fs_iconMarginTop + fs_iconMarginBottom;
	QPoint clickedPos = point - QPoint(0, (index - i0) * frameHeight);

	if (fid != TApp::instance()->getCurrentFrame()->getFid())
		return;

	QRect iconRect = QRect(QPoint(fs_leftMargin + fs_iconMarginLR,
								  fs_frameSpacing / 2 + fs_iconMarginTop) //<- top-left position of the icon
						   ,
						   m_iconSize);

	QPointF delta = m_naviRectPos - clickedPos;

	if (iconRect.left() > clickedPos.x() || iconRect.right() < clickedPos.x())
		delta.setX(0.0);
	if (iconRect.top() > clickedPos.y() || iconRect.bottom() < clickedPos.y())
		delta.setY(0.0);
	if (delta.x() == 0.0 && delta.y() == 0.0)
		return;

	delta.setX(delta.x() * m_icon2ViewerRatio.x());
	delta.setY(delta.y() * m_icon2ViewerRatio.y());

	if (TApp::instance()->getInknPaintViewerPanel()) {
		SceneViewer *viewer = TApp::instance()->getInknPaintViewerPanel()->getSceneViewer();
		if (viewer)
			viewer->navigatorPan(delta.toPoint());
	}
}

//-----------------------------------------------------------------------------

void FilmstripFrames::mouseReleaseEvent(QMouseEvent *)
{
	stopAutoPanning();
	m_selecting = false;
	m_dragDropArmed = false;
	m_isNavigatorPanning = false;

	QApplication::restoreOverrideCursor();
}

//-----------------------------------------------------------------------------

void FilmstripFrames::mouseMoveEvent(QMouseEvent *e)
{
	QPoint pos = e->pos();
	int index = y2index(e->pos().y());
	if (e->buttons() & Qt::LeftButton) {
		//navigator pan
		if (m_isNavigatorPanning) {
			execNavigatorPan(e->pos());
			e->accept();
			return;
		}

		if (m_dragDropArmed) {
			if ((m_pos - e->pos()).manhattanLength() > 10) {
				startDragDrop();
				m_dragDropArmed = false;
			}
		} else if (m_selecting) {
			m_pos = e->globalPos();
			select(index, DRAG_SELECT);
		}

		// autopan
		QRect visibleRect = visibleRegion().boundingRect();
		if (pos.y() < visibleRect.top())
			m_scrollSpeed = -(5 + (visibleRect.top() - pos.y()) / 4);
		else if (pos.y() > visibleRect.bottom())
			m_scrollSpeed = (5 + (pos.y() - visibleRect.bottom()) / 4);
		else
			m_scrollSpeed = 0;
		if (m_scrollSpeed != 0)
			startAutoPanning();
		else
			stopAutoPanning();
		update();
	} else if (e->buttons() & Qt::MidButton) {
		// scroll con il tasto centrale
		pos = e->globalPos();
		scroll(m_pos.y() - pos.y());
		m_pos = pos;
	}
}

//-----------------------------------------------------------------------------

void FilmstripFrames::keyPressEvent(QKeyEvent *event)
{
	TFrameHandle *fh = TApp::instance()->getCurrentFrame();
	TXshSimpleLevel *level = getLevel();
	if (!level)
		return;
	std::vector<TFrameId> fids;
	level->getFids(fids);
	if (fids.empty())
		return;

	fh->setFrameIds(fids);
	if (event->key() == Qt::Key_Up)
		fh->prevFrame();
	else if (event->key() == Qt::Key_Down)
		fh->nextFrame();
	else if (event->key() == Qt::Key_Home)
		fh->firstFrame();
	else if (event->key() == Qt::Key_End)
		fh->lastFrame();
	else if (event->key() == Qt::Key_PageDown) {
		int frameHeight = m_iconSize.height();
		int visibleHeight = visibleRegion().rects()[0].height();
		int visibleFrames = double(visibleHeight) / double(frameHeight);
		scroll(visibleFrames * frameHeight);
		return;
	} else if (event->key() == Qt::Key_PageUp) {
		int frameHeight = m_iconSize.height();
		int visibleHeight = visibleRegion().rects()[0].height();
		int visibleFrames = double(visibleHeight) / double(frameHeight);
		scroll(-visibleFrames * frameHeight);
		return;
	} else
		return;

	m_selection->selectNone();
	m_selection->select(fh->getFid());
	int index = fid2index(fh->getFid());
	if (index >= 0)
		exponeFrame(index);
}

//-----------------------------------------------------------------------------

void FilmstripFrames::wheelEvent(QWheelEvent *event)
{
	scroll(-event->delta());
}

//-----------------------------------------------------------------------------

void FilmstripFrames::startAutoPanning()
{
	if (m_timerId == 0)
		m_timerId = startTimer(40);
}

//-----------------------------------------------------------------------------

void FilmstripFrames::stopAutoPanning()
{
	if (m_timerId != 0) {
		killTimer(m_timerId);
		m_timerId = 0;
		m_scrollSpeed = 0;
	}
}

//-----------------------------------------------------------------------------

void FilmstripFrames::timerEvent(QTimerEvent *)
{
	scroll(m_scrollSpeed);

	if (m_selecting) {
		QPoint pos = mapFromGlobal(m_pos);
		int index = y2index(pos.y());
		select(index, DRAG_SELECT);
		update();
	}
}

//-----------------------------------------------------------------------------

void FilmstripFrames::contextMenuEvent(QContextMenuEvent *event)
{
	QMenu *menu = new QMenu();
	TXshSimpleLevel *sl = getLevel();
	bool isSubsequenceLevel = (sl && sl->isSubsequence());
	bool isReadOnly = (sl && sl->isReadOnly());
	CommandManager *cm = CommandManager::instance();

	menu->addAction(cm->getAction(MI_SelectAll));
	menu->addAction(cm->getAction(MI_InvertSelection));
	menu->addSeparator();
	if (!isSubsequenceLevel && !isReadOnly) {
		menu->addAction(cm->getAction(MI_Cut));
	}
	menu->addAction(cm->getAction(MI_Copy));

	if (!isSubsequenceLevel && !isReadOnly) {
		menu->addAction(cm->getAction(MI_Paste));
		menu->addAction(cm->getAction(MI_PasteInto));
		menu->addAction(cm->getAction(MI_Insert));
		menu->addAction(cm->getAction(MI_Clear));
		menu->addSeparator();
		menu->addAction(cm->getAction(MI_Reverse));
		menu->addAction(cm->getAction(MI_Swing));
		menu->addAction(cm->getAction(MI_Step2));
		menu->addAction(cm->getAction(MI_Step3));
		menu->addAction(cm->getAction(MI_Step4));
		menu->addAction(cm->getAction(MI_Each2));
		menu->addAction(cm->getAction(MI_Each3));
		menu->addAction(cm->getAction(MI_Each4));
		menu->addSeparator();
		menu->addAction(cm->getAction(MI_Duplicate));
		menu->addAction(cm->getAction(MI_MergeFrames));
	}
	menu->addAction(cm->getAction(MI_ExposeResource));
	if (!isSubsequenceLevel && !isReadOnly) {
#ifndef STUDENT
		menu->addAction(cm->getAction(MI_AddFrames));
#endif
		menu->addAction(cm->getAction(MI_Renumber));
		if (sl && sl->getType() == TZP_XSHLEVEL)
			menu->addAction(cm->getAction(MI_RevertToCleanedUp));
		if (sl && (sl->getType() == TZP_XSHLEVEL || sl->getType() == PLI_XSHLEVEL ||
				   (sl->getType() == OVL_XSHLEVEL && sl->getPath().getType() != "psd")))
			menu->addAction(cm->getAction(MI_RevertToLastSaved));
	}

	menu->exec(event->globalPos());
}

//-----------------------------------------------------------------------------

void FilmstripFrames::onLevelChanged()
{
	updateContentHeight();
	update();
}

//-----------------------------------------------------------------------------

void FilmstripFrames::onLevelSwitched(TXshLevel *)
{
	updateContentHeight(0);
	onFrameSwitched(); //deve visualizzare il frame corrente nella levelstrip
}

//-----------------------------------------------------------------------------

void FilmstripFrames::onFrameSwitched()
{
	// no. interferische con lo shift-click per la selezione. m_selection->selectNone();
	TApp *app = TApp::instance();
	TFrameHandle *fh = app->getCurrentFrame();
	TFrameId fid;
	if (fh->isEditingLevel())
		fid = fh->getFid();
	else {
		TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
		int col = app->getCurrentColumn()->getColumnIndex();
		int row = fh->getFrame();
		if (row < 0 || col < 0)
			return;
		TXshCell cell = xsh->getCell(row, col);
		if (cell.isEmpty())
			return;
		fid = cell.getFrameId();
	}
	int index = fid2index(fid);
	if (index >= 0) {
		exponeFrame(index);
		//clear selection and select only the destination frame
		select(index, ONLY_SELECT);
	}
	update();
}

//-----------------------------------------------------------------------------

void FilmstripFrames::startDragDrop()
{
	TRepetitionGuard guard;
	if (!guard.hasLock())
		return;

	TXshSimpleLevel *sl = getLevel();
	if (!sl)
		return;
	const std::set<TFrameId> &fids = m_selection->getSelectedFids();
	if (fids.empty())
		return;
	QByteArray byteArray;

	QMimeData *mimeData = new QMimeData;
	mimeData->setData("application/vnd.toonz.drawings", byteArray);
	QDrag *drag = new QDrag(this);
	QPixmap dropThumbnail = IconGenerator::instance()->getIcon(sl, *fids.begin());
	if (!dropThumbnail.isNull())
		drag->setPixmap(dropThumbnail);
	drag->setMimeData(mimeData);
	Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
}

//-----------------------------------------------------------------------------

void FilmstripFrames::inbetween()
{

	TFilmstripSelection::InbetweenRange range = m_selection->getInbetweenRange();
	if (range.first >= range.second || !getLevel())
		return;

	QSettings settings;
	QString keyName("InbetweenInterpolation");
	QString currentItem = settings.value(keyName, tr("Linear")).toString();
	int index;

	{
		if (!m_inbetweenDialog)
			m_inbetweenDialog = new InbetweenDialog(this);

		// Default -> l'ultimo valore usato

		m_inbetweenDialog->setValue(currentItem);

		int ret = m_inbetweenDialog->exec();
		if (!ret)
			return;

		currentItem = m_inbetweenDialog->getValue();
		if (currentItem.isEmpty())
			return;
		index = m_inbetweenDialog->getIndex(currentItem);
		if (index < 0)
			return;

		// registro il nuovo valore
	}
	settings.setValue(keyName, currentItem);

	// lo converto nella notazione di FilmstripCmd
	const FilmstripCmd::InbetweenInterpolation codes[] =
		{FilmstripCmd::II_Linear, FilmstripCmd::II_EaseIn,
		 FilmstripCmd::II_EaseOut, FilmstripCmd::II_EaseInOut};

	FilmstripCmd::InbetweenInterpolation interpolation = codes[index];

	// inbetween
	FilmstripCmd::inbetween(getLevel(), range.first, range.second, interpolation);
}

//=============================================================================
// Filmstrip
//-----------------------------------------------------------------------------

#if QT_VERSION >= 0x050500
Filmstrip::Filmstrip(QWidget *parent, Qt::WindowFlags flags)
#else
Filmstrip::Filmstrip(QWidget *parent, Qt::WFlags flags)
#endif
	: QWidget(parent)
{
	setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);

	m_frameArea = new QScrollArea(this);
	m_chooseLevelCombo = new QComboBox(this);
	m_frames = new FilmstripFrames(m_frameArea);

	//----

	m_frameArea->setObjectName("filmScrollArea");
	m_frameArea->setFrameStyle(QFrame::StyledPanel);
	m_frameArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	m_frameArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

	m_frameArea->verticalScrollBar()->setObjectName("LevelStripScrollBar");
	m_frameArea->setWidget(m_frames);

	m_chooseLevelCombo->setMaxVisibleItems(50);

	//layout
	QVBoxLayout *mainLayout = new QVBoxLayout();
	mainLayout->setMargin(0);
	mainLayout->setSpacing(3);
	{
		mainLayout->addWidget(m_chooseLevelCombo, 0);
		mainLayout->addWidget(m_frameArea, 1);
	}
	setLayout(mainLayout);

	setFocusProxy(m_frames);
	setMinimumWidth(m_frames->width() + 16); // 16 is the scroll bar's width

	onLevelSwitched(0);

	// signal-slot connections
	// switch the current level when the current index of m_chooseLevelCombo is changed
	connect(m_chooseLevelCombo, SIGNAL(activated(int)), this, SLOT(onChooseLevelComboChanged(int)));
}

//-----------------------------------------------------------------------------
/*! switch the current level when the current index of m_chooseLevelCombo is changed
*/
void Filmstrip::onChooseLevelComboChanged(int index)
{
	TApp *tapp = TApp::instance();
	//empty level
	if (index == m_chooseLevelCombo->findText(tr("- No Current Level -")))
		tapp->getCurrentLevel()->setLevel(0);
	else {
		std::vector<TFrameId> fids;
		m_levels[index]->getFids(fids);
		tapp->getCurrentFrame()->setFrameIds(fids);

		// retrieve to the current working frame of the level
		TFrameId WF;
		std::map<TXshSimpleLevel *, TFrameId>::iterator WFit;
		WFit = m_workingFrames.find(m_levels[index]);
		if (WFit != m_workingFrames.end())
			WF = WFit->second;
		else
			WF = fids[0];

		//this function emits xshLevelSwitched() signal and eventually calls FlipConsole::UpdateRange
		//it may move the current frame so we need to keep the current frameId before calling setLevel.
		tapp->getCurrentLevel()->setLevel(m_levels[index]);

		if (tapp->getCurrentSelection()->getSelection())
			tapp->getCurrentSelection()->getSelection()->selectNone();

		//move to the current working frame
		tapp->getCurrentFrame()->setFid(WF);

		QApplication::setOverrideCursor(Qt::WaitCursor);

		invalidateIcons(m_levels[index], fids);

		QApplication::restoreOverrideCursor();
	}
}

//-----------------------------------------------------------------------------
/*! update combo items when the contents of scene cast are changed
*/
void Filmstrip::updateChooseLevelComboItems()
{
	//clear items
	m_chooseLevelCombo->clear();
	m_levels.clear();

	std::map<TXshSimpleLevel *, TFrameId> new_workingFrames;

	//correct and register items
	ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
	if (scene) {
		std::vector<TXshLevel *> levels;
		scene->getLevelSet()->listLevels(levels);
		std::vector<TXshLevel *>::iterator it;

		for (it = levels.begin(); it != levels.end(); ++it) {
			//register only TLV and PLI
			TXshSimpleLevel *sl = (*it)->getSimpleLevel();
			if (sl) {
				//register only used level in xsheet
				if (!scene->getTopXsheet()->isLevelUsed(sl))
					continue;

				m_levels.push_back(sl);

				// create new m_workingFrames map with the new levelset
				TFrameId fId;
				std::map<TXshSimpleLevel *, TFrameId>::iterator WFit = m_workingFrames.find(sl);

				if (WFit != m_workingFrames.end())
					fId = WFit->second;
				else
					fId = sl->getFirstFid();

				new_workingFrames.insert(std::make_pair(sl, fId));

				QString levelName = QString::fromStdWString(sl->getName());
				if (sl->getProperties()->getDirtyFlag())
					levelName += " *";

				// append the current working frame number to the item name
				if (fId != sl->getFirstFid() && fId.getNumber() >= 0)
					levelName += QString("  [#") + QString::number(fId.getNumber()) + QString("]");

				m_chooseLevelCombo->addItem(levelName);
			}
		}
	}

	m_chooseLevelCombo->addItem(tr("- No Current Level -"));

	//swap the list
	m_workingFrames.clear();
	m_workingFrames = new_workingFrames;

	//synchronize the current index of combo to the current level
	updateCurrentLevelComboItem();
}

//-----------------------------------------------------------------------------
/*! synchronize the current index of combo to the current level
*/
void Filmstrip::updateCurrentLevelComboItem()
{
	if (m_chooseLevelCombo->count() == 1) {
		m_chooseLevelCombo->setCurrentIndex(0);
		return;
	}

	TXshSimpleLevel *currentLevel = TApp::instance()->getCurrentLevel()->getSimpleLevel();
	if (!currentLevel) {
		int noLevelIndex = m_chooseLevelCombo->findText(tr("- No Current Level -"));
		m_chooseLevelCombo->setCurrentIndex(noLevelIndex);
		return;
	}

	for (int i = 0; i < m_levels.size(); i++) {
		if (currentLevel->getName() == m_levels[i]->getName()) {
			m_chooseLevelCombo->setCurrentIndex(i);
			return;
		}
	}

	int noLevelIndex = m_chooseLevelCombo->findText(tr("- No Current Level -"));
	m_chooseLevelCombo->setCurrentIndex(noLevelIndex);
}

//-----------------------------------------------------------------------------

Filmstrip::~Filmstrip()
{
}

//-----------------------------------------------------------------------------

void Filmstrip::showEvent(QShowEvent *)
{
	TApp *app = TApp::instance();
	TXshLevelHandle *levelHandle = app->getCurrentLevel();
	bool ret = connect(levelHandle, SIGNAL(xshLevelSwitched(TXshLevel *)), SLOT(onLevelSwitched(TXshLevel *)));
	ret = ret && connect(levelHandle, SIGNAL(xshLevelChanged()), SLOT(onLevelChanged()));

	//updateWindowTitle is called in the onLevelChanged
	ret = ret && connect(app->getPaletteController()->getCurrentLevelPalette(),
						 SIGNAL(colorStyleChangedOnMouseRelease()), SLOT(onLevelChanged()));
	ret = ret && connect(levelHandle, SIGNAL(xshLevelTitleChanged()), SLOT(onLevelChanged()));

	ret = ret && connect(m_frameArea->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onSliderMoved(int)));

	TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene();
	ret = ret && connect(sceneHandle, SIGNAL(sceneSwitched()), this, SLOT(updateChooseLevelComboItems()));
	ret = ret && connect(sceneHandle, SIGNAL(castChanged()), this, SLOT(updateChooseLevelComboItems()));

	ret = ret && connect(TApp::instance()->getCurrentXsheet(), SIGNAL(xsheetChanged()), this, SLOT(updateChooseLevelComboItems()));

	ret = ret && connect(app->getCurrentFrame(), SIGNAL(frameSwitched()), this, SLOT(onFrameSwitched()));

	assert(ret);

	updateChooseLevelComboItems();
	onFrameSwitched();
	onLevelSwitched(0);
}

//-----------------------------------------------------------------------------

void Filmstrip::hideEvent(QHideEvent *)
{
	TApp *app = TApp::instance();
	TXshLevelHandle *levelHandle = app->getCurrentLevel();
	disconnect(levelHandle, SIGNAL(xshLevelSwitched(TXshLevel *)), this, SLOT(onLevelSwitched(TXshLevel *)));
	disconnect(levelHandle, SIGNAL(xshLevelChanged()), this, SLOT(onLevelChanged()));
	disconnect(TApp::instance()->getPaletteController()->getCurrentLevelPalette(),
			   SIGNAL(colorStyleChangedOnMouseRelease()), this, SLOT(onLevelChanged()));

	disconnect(levelHandle, SIGNAL(xshLevelTitleChanged()), this, SLOT(onLevelChanged()));

	disconnect(m_frameArea->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onSliderMoved(int)));

	TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene();
	disconnect(sceneHandle, SIGNAL(sceneSwitched()), this, SLOT(updateChooseLevelComboItems()));
	disconnect(sceneHandle, SIGNAL(castChanged()), this, SLOT(updateChooseLevelComboItems()));

	disconnect(TApp::instance()->getCurrentXsheet(), SIGNAL(xsheetChanged()), this, SLOT(updateChooseLevelComboItems()));

	disconnect(TApp::instance()->getCurrentFrame(), SIGNAL(frameSwitched()), this, SLOT(onFrameSwitched()));
}

//-----------------------------------------------------------------------------

void Filmstrip::resizeEvent(QResizeEvent *e)
{
	m_frames->updateContentHeight();
	m_frameArea->verticalScrollBar()->setSingleStep(m_frames->getOneFrameHeight());
}

//-----------------------------------------------------------------------------

void Filmstrip::onLevelChanged()
{
	updateWindowTitle();
}

//-----------------------------------------------------------------------------

void Filmstrip::updateWindowTitle()
{
	updateCurrentLevelComboItem();

	TXshSimpleLevel *level = m_frames->getLevel();

	QString levelName;

	if (!level) {
		parentWidget()->setWindowTitle(tr("Level Strip"));
		return;
	} else {
		levelName = QString::fromStdWString(level->getName());
		if (level->getProperties()->getDirtyFlag())
			levelName += " *";
	}

	//parentWidget() is TPanel
	parentWidget()->setWindowTitle(tr("Level:  ") + levelName);

	TFrameHandle *fh = TApp::instance()->getCurrentFrame();
	if (fh->isEditingLevel() && fh->getFid().getNumber() >= 0)
		levelName += QString("  [#") + QString::number(fh->getFid().getNumber()) + QString("]");

	m_chooseLevelCombo->setItemText(m_chooseLevelCombo->currentIndex(), levelName);
}

//-----------------------------------------------------------------------------

void Filmstrip::onLevelSwitched(TXshLevel *oldLevel)
{
	updateWindowTitle();

	int tc = ToonzCheck::instance()->getChecks();
	if (tc & (ToonzCheck::eInk | ToonzCheck::ePaint)) {
		TXshLevel *sl = TApp::instance()->getCurrentLevel()->getLevel();
		if (!sl)
			return;
		std::vector<TFrameId> fids;
		sl->getFids(fids);
		removeIcons(sl, fids, true);
	}
	update();
}

//-----------------------------------------------------------------------------

void Filmstrip::onSliderMoved(int val)
{
	int oneFrameHeight = m_frames->getIconSize().height() + fs_frameSpacing + fs_iconMarginTop + fs_iconMarginBottom;
	int tmpVal = (int)((float)val / (float)oneFrameHeight + 0.5f) * oneFrameHeight;
	m_frameArea->verticalScrollBar()->setValue(tmpVal);
}

//-----------------------------------------------------------------------------

void Filmstrip::onFrameSwitched()
{
	TFrameHandle *fh = TApp::instance()->getCurrentFrame();
	if (!fh->isEditingLevel())
		return;

	TXshSimpleLevel *level = m_frames->getLevel();

	std::map<TXshSimpleLevel *, TFrameId>::iterator WFit;
	WFit = m_workingFrames.find(level);
	if (WFit == m_workingFrames.end())
		return;

	WFit->second = fh->getFid();

	QString levelName = QString::fromStdWString(level->getName());
	if (level->getProperties()->getDirtyFlag())
		levelName += " *";
	if (fh->getFid().getNumber() >= 0)
		levelName += QString("  [#") + QString::number(fh->getFid().getNumber()) + QString("]");

	m_chooseLevelCombo->setItemText(m_chooseLevelCombo->currentIndex(), levelName);
}

//=============================================================================
// inbetweenDialog
//-----------------------------------------------------------------------------

InbetweenDialog::InbetweenDialog(QWidget *parent)
	: Dialog(TApp::instance()->getMainWindow(), true, "InBeetween")
{
	setWindowTitle(tr("Inbetween"));

	QString linear(tr("Linear"));
	QString easeIn(tr("Ease In"));
	QString easeOut(tr("Ease Out"));
	QString easeInOut(tr("Ease In / Ease Out"));
	QStringList items;
	items << linear << easeIn << easeOut << easeInOut;

	beginHLayout();
	m_comboBox = new QComboBox(this);
	m_comboBox->addItems(items);
	addWidget(tr("Interpolation:"), m_comboBox);
	endHLayout();

	QPushButton *okBtn = new QPushButton(tr("Inbetween"), this);
	QPushButton *cancelBtn = new QPushButton(tr("Cancel"), this);
	connect(okBtn, SIGNAL(clicked()), this, SLOT(accept()));
	connect(cancelBtn, SIGNAL(clicked()), this, SLOT(reject()));

	addButtonBarWidget(okBtn, cancelBtn);
}

//-----------------------------------------------------------------------------

QString InbetweenDialog::getValue()
{
	return m_comboBox->currentText();
}

//-----------------------------------------------------------------------------

void InbetweenDialog::setValue(const QString &value)
{
	int currentIndex = m_comboBox->findText(value);
	if (currentIndex < 0)
		currentIndex = 0;
	m_comboBox->setCurrentIndex(currentIndex);
}

//-----------------------------------------------------------------------------

int InbetweenDialog::getIndex(const QString &text)
{
	return m_comboBox->findText(text);
}

//=============================================================================

OpenFloatingPanel openFilmstripCommand(MI_OpenFilmStrip, "FilmStrip", QObject::tr("Level: "));