übernahme Code Shortcut

This commit is contained in:
georg0480
2026-01-31 15:28:10 +01:00
parent 6f4d6b9301
commit ef46c21291
1787 changed files with 1126465 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2023-2025 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "colordialog.h"
#include "settings.h"
#include "util.h"
#include <QColorDialog>
ColorDialog::ColorDialog(QObject *parent)
: QObject{parent}
{}
QColor ColorDialog::getColor(const QColor &initial,
QWidget *parent,
const QString &title,
bool showAlpha)
{
auto flags = Util::getColorDialogOptions();
if (showAlpha) {
flags |= QColorDialog::ShowAlphaChannel;
}
auto color = initial;
auto newColor = QColorDialog::getColor(color, parent, title, flags);
// Save custom colors to settings after dialog closes
Settings.saveCustomColors();
if (newColor.isValid() && showAlpha) {
auto rgb = newColor;
auto transparent = QColor(0, 0, 0, 0);
rgb.setAlpha(color.alpha());
if (newColor.alpha() == 0
&& (rgb != color || (newColor == transparent && color == transparent))) {
newColor.setAlpha(255);
}
}
return newColor;
}
void ColorDialog::open()
{
auto newColor = getColor(m_color, nullptr, m_title, m_showAlpha);
if (newColor.isValid()) {
setSelectedColor(newColor);
emit accepted();
}
}
void ColorDialog::setSelectedColor(const QColor &color)
{
if (color != m_color) {
m_color = color;
emit selectedColorChanged(color);
}
}
void ColorDialog::setTitle(const QString &title)
{
if (title != m_title) {
m_title = title;
emit titleChanged();
}
}
void ColorDialog::setShowAlpha(bool show)
{
if (show != m_showAlpha) {
m_showAlpha = show;
emit showAlphaChanged();
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2023 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COLORDIALOG_H
#define COLORDIALOG_H
#include <QColor>
#include <QObject>
class ColorDialog : public QObject
{
Q_OBJECT
Q_PROPERTY(
QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(bool showAlpha READ showAlpha WRITE setShowAlpha NOTIFY showAlphaChanged)
public:
explicit ColorDialog(QObject *parent = nullptr);
Q_INVOKABLE void open();
// Static convenience method for non-QML usage
static QColor getColor(const QColor &initial = Qt::white,
QWidget *parent = nullptr,
const QString &title = QString(),
bool showAlpha = true);
signals:
void selectedColorChanged(const QColor &color);
void accepted();
void titleChanged();
void showAlphaChanged();
private:
QColor m_color;
QString m_title;
bool m_showAlpha = true;
QColor selectedColor() const { return m_color; }
void setSelectedColor(const QColor &color);
QString title() const { return m_title; }
void setTitle(const QString &title);
bool showAlpha() const { return m_showAlpha; }
void setShowAlpha(bool show);
};
#endif // COLORDIALOG_H

View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2014-2025 Meltytech, LLC
* Inspiration: KDENLIVE colorpickerwidget.cpp by Till Theato (root@ttill.de)
* Inspiration: QColorDialog.cpp
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "colorpickeritem.h"
#include "Logger.h"
#include <QApplication>
#include <QGuiApplication>
#include <QImage>
#include <QScreen>
#include <QTimer>
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusMetaType>
#include <QDBusObjectPath>
#include <QDBusPendingCall>
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
#endif
ColorPickerItem::ColorPickerItem(QObject *parent)
: QObject(parent)
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
qDBusRegisterMetaType<QColor>();
#endif
connect(this, &ColorPickerItem::pickColor, &m_selector, &ScreenSelector::startSelection);
connect(&m_selector, &ScreenSelector::screenSelected, this, &ColorPickerItem::screenSelected);
connect(&m_selector, &ScreenSelector::cancelled, this, &ColorPickerItem::cancelled);
}
void ColorPickerItem::screenSelected(const QRect &rect)
{
m_selectedRect = rect;
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
if (m_selector.useDBus())
QTimer::singleShot(0, this, &ColorPickerItem::grabColorDBus);
else
#endif
// Give the frame buffer time to clear the selector window before
// grabbing the color.
QTimer::singleShot(200, this, &ColorPickerItem::grabColor);
}
void ColorPickerItem::grabColor()
{
QScreen *screen = QGuiApplication::screenAt(m_selectedRect.topLeft());
QPixmap screenGrab = screen->grabWindow(0,
m_selectedRect.x() - screen->geometry().x(),
m_selectedRect.y() - screen->geometry().y(),
m_selectedRect.width(),
m_selectedRect.height());
QImage image = screenGrab.toImage();
int numPixel = qMax(image.width() * image.height(), 1);
int sumR = 0;
int sumG = 0;
int sumB = 0;
for (int x = 0; x < image.width(); ++x) {
for (int y = 0; y < image.height(); ++y) {
QColor color = image.pixel(x, y);
sumR += color.red();
sumG += color.green();
sumB += color.blue();
}
}
QColor avgColor(sumR / numPixel, sumG / numPixel, sumB / numPixel);
emit colorPicked(avgColor);
}
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
QDBusArgument &operator<<(QDBusArgument &arg, const QColor &color)
{
arg.beginStructure();
arg << color.redF() << color.greenF() << color.blueF();
arg.endStructure();
return arg;
}
const QDBusArgument &operator>>(const QDBusArgument &arg, QColor &color)
{
double red, green, blue;
arg.beginStructure();
arg >> red >> green >> blue;
color.setRedF(red);
color.setGreenF(green);
color.setBlueF(blue);
arg.endStructure();
return arg;
}
void ColorPickerItem::grabColorDBus()
{
QDBusMessage message
= QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"),
QLatin1String("/org/freedesktop/portal/desktop"),
QLatin1String("org.freedesktop.portal.Screenshot"),
QLatin1String("PickColor"));
message << QLatin1String("x11:") << QVariantMap{};
QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall);
connect(watcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *watcher) {
QDBusPendingReply<QDBusObjectPath> reply = *watcher;
if (reply.isError()) {
LOG_WARNING() << "Unable to get DBus reply: " << reply.error().message();
} else {
QDBusConnection::sessionBus().connect(QString(),
reply.value().path(),
QLatin1String("org.freedesktop.portal.Request"),
QLatin1String("Response"),
this,
SLOT(gotColorResponse(uint, QVariantMap)));
}
});
}
void ColorPickerItem::gotColorResponse(uint response, const QVariantMap &results)
{
if (!response) {
if (results.contains(QLatin1String("color"))) {
const QColor color = qdbus_cast<QColor>(results.value(QLatin1String("color")));
LOG_DEBUG() << "picked" << color;
emit colorPicked(color);
}
} else {
LOG_WARNING() << "Failed to grab screen" << response << results;
}
}
#endif

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2014-2025 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COLORPICKERITEM_H
#define COLORPICKERITEM_H
#include "widgets/screenselector.h"
#include <QColor>
#include <QObject>
class ColorPickerItem : public QObject
{
Q_OBJECT
public:
explicit ColorPickerItem(QObject *parent = 0);
signals:
void pickColor(QPoint initialPos = QPoint(-1, -1));
void colorPicked(const QColor &color);
void cancelled();
private slots:
void screenSelected(const QRect &rect);
void grabColor();
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
void grabColorDBus();
void gotColorResponse(uint response, const QVariantMap &results);
#endif
private:
ScreenSelector m_selector;
QRect m_selectedRect;
};
#endif // COLORPICKERITEM_H

View File

@@ -0,0 +1,381 @@
/*
* Copyright (c) 2013-2022 Meltytech, LLC
* Author: Dan Dennedy <dan@dennedy.org>
* Some ideas came from Qt-Plus: https://github.com/liuyanghejerry/Qt-Plus
* and Steinar Gunderson's Movit demo app.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "colorwheelitem.h"
#include "mainwindow.h"
#include <QCursor>
#include <QPainter>
#include <qmath.h>
#include <cstdio>
static const qreal WHEEL_SLIDER_RATIO = 10.0;
ColorWheelItem::ColorWheelItem(QQuickItem *parent)
: QQuickPaintedItem(parent)
, m_image()
, m_isMouseDown(false)
, m_lastPoint(0, 0)
, m_size(0, 0)
, m_margin(5)
, m_color(0, 0, 0, 0)
, m_isInWheel(false)
, m_isInSquare(false)
, m_step(1 / 256)
{
setAcceptedMouseButtons(Qt::LeftButton);
setAcceptHoverEvents(true);
}
QColor ColorWheelItem::color()
{
return m_color;
}
void ColorWheelItem::setColor(const QColor &color)
{
if (m_color != color) {
m_color = color;
update();
emit colorChanged(m_color);
}
}
int ColorWheelItem::red()
{
return m_color.red();
}
void ColorWheelItem::setRed(int red)
{
if (m_color.red() != red) {
m_color.setRed(red);
update();
emit colorChanged(m_color);
}
}
int ColorWheelItem::green()
{
return m_color.green();
}
void ColorWheelItem::setGreen(int green)
{
if (m_color.green() != green) {
m_color.setGreen(green);
update();
emit colorChanged(m_color);
}
}
int ColorWheelItem::blue()
{
return m_color.blue();
}
void ColorWheelItem::setBlue(int blue)
{
if (m_color.blue() != blue) {
m_color.setBlue(blue);
update();
emit colorChanged(m_color);
}
}
qreal ColorWheelItem::redF()
{
return m_color.redF();
}
void ColorWheelItem::setRedF(qreal red)
{
if (m_color.redF() != red) {
m_color.setRedF(red);
update();
emit colorChanged(m_color);
}
}
qreal ColorWheelItem::greenF()
{
return m_color.greenF();
}
void ColorWheelItem::setGreenF(qreal green)
{
if (m_color.greenF() != green) {
m_color.setGreenF(green);
update();
emit colorChanged(m_color);
}
}
qreal ColorWheelItem::blueF()
{
return m_color.blueF();
}
void ColorWheelItem::setBlueF(qreal blue)
{
if (m_color.blueF() != blue) {
m_color.setBlueF(blue);
update();
emit colorChanged(m_color);
}
}
qreal ColorWheelItem::step()
{
return m_step;
}
void ColorWheelItem::setStep(qreal step)
{
m_step = step;
}
int ColorWheelItem::wheelSize() const
{
qreal ws = (qreal) width() / (1.0 + 1.0 / WHEEL_SLIDER_RATIO);
return qMin(ws, height());
}
QColor ColorWheelItem::colorForPoint(const QPoint &point)
{
if (!m_image.valid(point))
return QColor();
if (m_isInWheel) {
qreal w = wheelSize() - m_margin * 2;
qreal xf = qreal(point.x() - m_margin) / w;
qreal yf = 1.0 - qreal(point.y() - m_margin) / w;
qreal xp = 2.0 * xf - 1.0;
qreal yp = 2.0 * yf - 1.0;
qreal rad = qMin(hypot(xp, yp), 1.0);
qreal theta = qAtan2(yp, xp);
theta -= 105.0 / 360.0 * 2.0 * M_PI;
if (theta < 0.0)
theta += 2.0 * M_PI;
qreal hue = (theta * 180.0 / M_PI) / 360.0;
return QColor::fromHsvF(hue, rad, m_color.valueF());
}
if (m_isInSquare) {
qreal value = 1.0 - qreal(point.y() - m_margin) / (wheelSize() - m_margin * 2);
return QColor::fromHsvF(m_color.hueF(), m_color.saturationF(), value);
}
return QColor();
}
void ColorWheelItem::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
m_lastPoint = event->pos();
if (m_wheelRegion.contains(m_lastPoint)) {
m_isInWheel = true;
m_isInSquare = false;
QColor color = colorForPoint(m_lastPoint);
setColor(color);
} else if (m_sliderRegion.contains(m_lastPoint)) {
m_isInWheel = false;
m_isInSquare = true;
QColor color = colorForPoint(m_lastPoint);
setColor(color);
}
m_isMouseDown = true;
}
}
void ColorWheelItem::mouseMoveEvent(QMouseEvent *event)
{
updateCursor(event->pos());
if (!m_isMouseDown)
return;
m_lastPoint = event->pos();
if (m_wheelRegion.contains(m_lastPoint) && m_isInWheel) {
QColor color = colorForPoint(m_lastPoint);
setColor(color);
} else if (m_sliderRegion.contains(m_lastPoint) && m_isInSquare) {
QColor color = colorForPoint(m_lastPoint);
setColor(color);
}
}
void ColorWheelItem::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
m_isMouseDown = false;
m_isInWheel = false;
m_isInSquare = false;
}
}
void ColorWheelItem::hoverMoveEvent(QHoverEvent *event)
{
updateCursor(event->position().toPoint());
}
void ColorWheelItem::wheelEvent(QWheelEvent *event)
{
QPoint steps = event->angleDelta() / 8 / 15;
qreal delta = (qreal) steps.y() * m_step;
QColor currentColor = color();
qreal c;
// Increment/decrement RGB values by delta
c = currentColor.redF();
c += delta;
if (c < 0)
c = 0;
if (c > 1)
c = 1;
currentColor.setRedF(c);
c = currentColor.greenF();
c += delta;
if (c < 0)
c = 0;
if (c > 1)
c = 1;
currentColor.setGreenF(c);
c = currentColor.blueF();
c += delta;
if (c < 0)
c = 0;
if (c > 1)
c = 1;
currentColor.setBlueF(c);
setColor(currentColor);
event->accept();
}
void ColorWheelItem::paint(QPainter *painter)
{
QSize size(width(), height());
if (m_size != size) {
m_image = QImage(QSize(width(), height()), QImage::Format_ARGB32_Premultiplied);
m_image.fill(qRgba(0, 0, 0, 0));
drawWheel();
drawSlider();
m_size = size;
}
painter->setRenderHint(QPainter::Antialiasing);
painter->drawImage(0, 0, m_image);
drawWheelDot(*painter);
drawSliderBar(*painter);
}
void ColorWheelItem::drawWheel()
{
int r = wheelSize();
QPainter painter(&m_image);
painter.setRenderHint(QPainter::Antialiasing);
m_image.fill(0); // transparent
QConicalGradient conicalGradient;
conicalGradient.setColorAt(0.0, Qt::red);
conicalGradient.setColorAt(60.0 / 360.0, Qt::yellow);
conicalGradient.setColorAt(135.0 / 360.0, Qt::green);
conicalGradient.setColorAt(180.0 / 360.0, Qt::cyan);
conicalGradient.setColorAt(240.0 / 360.0, Qt::blue);
conicalGradient.setColorAt(315.0 / 360.0, Qt::magenta);
conicalGradient.setColorAt(1.0, Qt::red);
QRadialGradient radialGradient(0.0, 0.0, r / 2);
radialGradient.setColorAt(0.0, Qt::white);
radialGradient.setColorAt(1.0, Qt::transparent);
painter.translate(r / 2, r / 2);
painter.rotate(-105);
QBrush hueBrush(conicalGradient);
painter.setPen(Qt::NoPen);
painter.setBrush(hueBrush);
painter.drawEllipse(QPoint(0, 0), r / 2 - m_margin, r / 2 - m_margin);
QBrush saturationBrush(radialGradient);
painter.setBrush(saturationBrush);
painter.drawEllipse(QPoint(0, 0), r / 2 - m_margin, r / 2 - m_margin);
m_wheelRegion = QRegion(r / 2, r / 2, r - 2 * m_margin, r - 2 * m_margin, QRegion::Ellipse);
m_wheelRegion.translate(-(r - 2 * m_margin) / 2, -(r - 2 * m_margin) / 2);
}
void ColorWheelItem::drawWheelDot(QPainter &painter)
{
int r = wheelSize() / 2;
QPen pen(Qt::white);
pen.setWidth(2);
painter.setPen(pen);
painter.setBrush(Qt::black);
painter.translate(r, r);
painter.rotate(360.0 - m_color.hue());
painter.rotate(-105);
painter.drawEllipse(QPointF(m_color.saturationF() * r - m_margin, 0.0), 4, 4);
painter.resetTransform();
}
void ColorWheelItem::drawSliderBar(QPainter &painter)
{
qreal value = 1.0 - m_color.valueF();
int ws = wheelSize() * MAIN.devicePixelRatioF();
int w = (qreal) ws / WHEEL_SLIDER_RATIO;
int h = ws - m_margin * 2;
QPen pen(Qt::white);
pen.setWidth(qRound(2 * MAIN.devicePixelRatioF()));
painter.setPen(pen);
painter.setBrush(Qt::black);
painter.translate(ws, m_margin + value * h);
painter.drawRect(0, 0, w, 4);
painter.resetTransform();
}
void ColorWheelItem::drawSlider()
{
QPainter painter(&m_image);
painter.setRenderHint(QPainter::Antialiasing);
int ws = wheelSize();
int w = (qreal) ws / WHEEL_SLIDER_RATIO;
int h = ws - m_margin * 2;
QLinearGradient gradient(0, 0, w, h);
gradient.setColorAt(0.0, Qt::white);
gradient.setColorAt(1.0, Qt::black);
QBrush brush(gradient);
painter.setPen(Qt::NoPen);
painter.setBrush(brush);
painter.translate(ws, m_margin);
painter.drawRect(0, 0, w, h);
m_sliderRegion = QRegion(ws, m_margin, w, h);
}
void ColorWheelItem::updateCursor(const QPoint &pos)
{
if (m_wheelRegion.contains(pos) || m_sliderRegion.contains(pos)) {
setCursor(QCursor(Qt::CrossCursor));
} else {
unsetCursor();
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2013-2018 Meltytech, LLC
* Author: Dan Dennedy <dan@dennedy.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COLORWHEELITEM_H
#define COLORWHEELITEM_H
#include <QImage>
#include <QQuickPaintedItem>
class ColorWheelItem : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(int red READ red WRITE setRed)
Q_PROPERTY(int green READ green WRITE setGreen)
Q_PROPERTY(int blue READ blue WRITE setBlue)
Q_PROPERTY(qreal redF READ redF WRITE setRedF)
Q_PROPERTY(qreal greenF READ greenF WRITE setGreenF)
Q_PROPERTY(qreal blueF READ blueF WRITE setBlueF)
Q_PROPERTY(qreal step READ step WRITE setStep)
public:
explicit ColorWheelItem(QQuickItem *parent = 0);
QColor color();
void setColor(const QColor &color);
int red();
void setRed(int red);
int green();
void setGreen(int green);
int blue();
void setBlue(int blue);
qreal redF();
void setRedF(qreal red);
qreal greenF();
void setGreenF(qreal green);
qreal blueF();
void setBlueF(qreal blue);
qreal step();
void setStep(qreal blue);
signals:
void colorChanged(const QColor &color);
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void hoverMoveEvent(QHoverEvent *event);
void wheelEvent(QWheelEvent *event);
void paint(QPainter *painter);
private:
QImage m_image;
bool m_isMouseDown;
QPoint m_lastPoint;
QSize m_size;
int m_margin;
QRegion m_wheelRegion;
QRegion m_sliderRegion;
QColor m_color;
bool m_isInWheel;
bool m_isInSquare;
qreal m_step;
int wheelSize() const;
QColor colorForPoint(const QPoint &point);
void drawWheel();
void drawWheelDot(QPainter &painter);
void drawSliderBar(QPainter &painter);
void drawSlider();
void updateCursor(const QPoint &pos);
};
#endif // COLORWHEELITEM_H

View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2023 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "filedialog.h"
#include "mainwindow.h"
#include "settings.h"
#include "util.h"
FileDialog::FileDialog(QObject *parent)
: QObject{parent}
{
m_fileDialog.reset(new QFileDialog(&MAIN));
connect(m_fileDialog.get(), &QDialog::accepted, this, &FileDialog::accepted);
connect(m_fileDialog.get(), &QDialog::rejected, this, &FileDialog::rejected);
connect(m_fileDialog.get(), &QFileDialog::fileSelected, this, &FileDialog::fileSelected);
connect(m_fileDialog.get(), &QFileDialog::filterSelected, this, &FileDialog::filterSelected);
}
void FileDialog::setFileMode(FileMode mode)
{
m_fileMode = mode;
}
QString FileDialog::title() const
{
return m_fileDialog->windowTitle();
}
void FileDialog::setTitle(const QString &title)
{
if (title != m_fileDialog->windowTitle()) {
m_fileDialog->setWindowTitle(title);
emit titleChanged();
}
}
QStringList FileDialog::nameFilters() const
{
return m_fileDialog->nameFilters();
}
void FileDialog::setNameFilters(const QStringList &filters)
{
if (filters != m_fileDialog->nameFilters()) {
m_fileDialog->setNameFilters(filters);
emit nameFiltersChanged();
}
}
QString FileDialog::selectedFile()
{
return m_fileDialog->selectedFiles().first();
}
void FileDialog::open()
{
if (m_fileMode == FileDialog::OpenFile) {
m_fileDialog->setAcceptMode(QFileDialog::AcceptOpen);
m_fileDialog->setDirectory(Settings.openPath());
} else {
m_fileDialog->setAcceptMode(QFileDialog::AcceptSave);
m_fileDialog->setDirectory(Settings.savePath());
}
#ifdef Q_OS_MAC
m_fileDialog->setWindowModality(Qt::NonModal);
#else
m_fileDialog->setWindowModality(Qt::ApplicationModal);
#endif
m_fileDialog->setOptions(Util::getFileDialogOptions());
m_fileDialog->open();
}

60
src/qmltypes/filedialog.h Normal file
View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2023 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FILEDIALOG_H
#define FILEDIALOG_H
#include <QFileDialog>
class FileDialog : public QObject
{
Q_OBJECT
Q_PROPERTY(FileDialog::FileMode fileMode READ fileMode WRITE setFileMode NOTIFY fileModeChanged)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(
QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
Q_PROPERTY(QString selectedFile READ selectedFile NOTIFY fileSelected)
public:
enum FileMode { OpenFile, SaveFile };
Q_ENUM(FileMode)
explicit FileDialog(QObject *parent = nullptr);
FileDialog::FileMode fileMode() const { return m_fileMode; }
void setFileMode(FileDialog::FileMode mode);
QString title() const;
void setTitle(const QString &title);
QStringList nameFilters() const;
void setNameFilters(const QStringList &filters);
QString selectedFile();
Q_INVOKABLE void open();
signals:
void fileModeChanged();
void titleChanged();
void nameFiltersChanged();
void fileSelected(const QString &file);
void filterSelected(const QString &filter);
void accepted();
void rejected();
private:
FileDialog::FileMode m_fileMode{FileDialog::OpenFile};
std::unique_ptr<QFileDialog> m_fileDialog;
};
#endif // FILEDIALOG_H

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2023-2024 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "fontdialog.h"
#include <QFontDialog>
FontDialog::FontDialog(QObject *parent)
: QObject{parent}
{}
void FontDialog::open()
{
QFontDialog dialog(m_font);
dialog.setModal(true);
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
dialog.setOption(QFontDialog::DontUseNativeDialog);
#endif
if (dialog.exec() == QDialog::Accepted) {
setSelectedFont(dialog.currentFont());
emit accepted();
} else {
emit rejected();
}
}
void FontDialog::setSelectedFont(const QFont &font)
{
if (font != m_font) {
m_font = font;
emit selectedFontChanged(font);
}
}

46
src/qmltypes/fontdialog.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2023 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FONTDIALOG_H
#define FONTDIALOG_H
#include <QFont>
#include <QObject>
class FontDialog : public QObject
{
Q_OBJECT
Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont NOTIFY selectedFontChanged)
public:
FontDialog(QObject *parent = nullptr);
Q_INVOKABLE void open();
signals:
void accepted();
void rejected();
void selectedFontChanged(const QFont &font);
private:
QFont m_font;
QFont selectedFont() const { return m_font; }
void setSelectedFont(const QFont &font);
};
#endif // FONTDIALOG_H

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2023 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "messagedialog.h"
#include "Logger.h"
#include "qmlapplication.h"
#include <QApplication>
MessageDialog::MessageDialog(QObject *parent)
: QObject{parent}
, m_buttons{0}
{}
void MessageDialog::open()
{
QMessageBox dialog;
if (m_buttons & QMessageBox::No) {
dialog.setIcon(QMessageBox::Question);
dialog.setStandardButtons(QMessageBox::StandardButtons(m_buttons));
dialog.setDefaultButton(QMessageBox::Yes);
dialog.setEscapeButton(QMessageBox::No);
} else if (!m_buttons) {
dialog.setIcon(QMessageBox::Information);
dialog.setDefaultButton(QMessageBox::Ok);
} else {
dialog.setStandardButtons(QMessageBox::StandardButtons(m_buttons));
}
if (!m_title.isEmpty()) {
dialog.setWindowTitle(m_title);
} else {
dialog.setWindowTitle(QApplication::applicationName());
}
dialog.setText(m_text);
dialog.setWindowModality(QmlApplication::dialogModality());
auto button = QMessageBox::StandardButton(dialog.exec());
if (QMessageBox::Ok == button || QMessageBox::Yes == button) {
emit accepted();
} else {
emit rejected();
}
}
void MessageDialog::setTitle(const QString &title)
{
if (title != m_title) {
m_title = title;
emit titleChanged(title);
}
}
void MessageDialog::setText(const QString &text)
{
if (text != m_text) {
m_text = text;
emit textChanged(text);
}
}
void MessageDialog::setButtons(int buttons)
{
if (buttons != m_buttons) {
m_buttons = buttons;
emit buttonsChanged(buttons);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2023 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MESSAGEDIALOG_H
#define MESSAGEDIALOG_H
#include <QMessageBox>
class MessageDialog : public QObject
{
Q_OBJECT
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(int buttons READ buttons WRITE setButtons NOTIFY buttonsChanged)
public:
enum StandardButtons {
Ok = QMessageBox::Ok,
Yes = QMessageBox::Yes,
No = QMessageBox::No,
Cancel = QMessageBox::Cancel
};
Q_ENUM(StandardButtons)
explicit MessageDialog(QObject *parent = nullptr);
Q_INVOKABLE void open();
signals:
void titleChanged(const QString &title);
void textChanged(const QString &text);
void buttonsChanged(int buttons);
void accepted();
void rejected();
private:
QString m_title;
QString m_text;
int m_buttons;
QString title() const { return m_title; }
void setTitle(const QString &title);
QString text() const { return m_text; }
void setText(const QString &text);
int buttons() const { return m_buttons; }
void setButtons(int buttons);
};
#endif // MESSAGEDIALOG_H

View File

@@ -0,0 +1,283 @@
/*
* Copyright (c) 2013-2024 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmlapplication.h"
#include "controllers/filtercontroller.h"
#include "mainwindow.h"
#include "mltcontroller.h"
#include "models/attachedfiltersmodel.h"
#include "settings.h"
#include "util.h"
#include "videowidget.h"
#include <QApplication>
#include <QCheckBox>
#include <QClipboard>
#include <QCursor>
#include <QFileInfo>
#include <QMessageBox>
#include <QPalette>
#include <QStyle>
#include <QSysInfo>
#ifdef Q_OS_WIN
#include <QLocale>
#else
#include <clocale>
#endif
#include <limits>
QmlApplication &QmlApplication::singleton()
{
static QmlApplication instance;
return instance;
}
QmlApplication::QmlApplication()
: QObject()
{}
Qt::WindowModality QmlApplication::dialogModality()
{
#ifdef Q_OS_MAC
return Qt::WindowModal;
#else
return Qt::ApplicationModal;
#endif
}
QPoint QmlApplication::mousePos()
{
return QCursor::pos();
}
QColor QmlApplication::toolTipBaseColor()
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
if ("gtk+" == QApplication::style()->objectName())
return QApplication::palette().highlight().color();
#endif
return QApplication::palette().toolTipBase().color();
}
QColor QmlApplication::toolTipTextColor()
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
if ("gtk+" == QApplication::style()->objectName())
return QApplication::palette().highlightedText().color();
#endif
return QApplication::palette().toolTipText().color();
}
QString QmlApplication::OS()
{
#if defined(Q_OS_MAC)
return "macOS";
#elif defined(Q_OS_LINUX)
return "Linux";
#elif defined(Q_OS_UNIX)
return "UNIX";
#elif defined(Q_OS_WIN)
return "Windows";
#else
return "";
#endif
}
QRect QmlApplication::mainWinRect()
{
return MAIN.geometry();
}
bool QmlApplication::hasFiltersOnClipboard()
{
return MLT.hasFiltersOnClipboard();
}
void QmlApplication::copyEnabledFilters()
{
QScopedPointer<Mlt::Producer> producer(
new Mlt::Producer(MAIN.filterController()->attachedModel()->producer()));
MLT.copyFilters(producer.data(), MLT.FILTER_INDEX_ENABLED);
QGuiApplication::clipboard()->setText(MLT.filtersClipboardXML());
emit QmlApplication::singleton().filtersCopied();
}
void QmlApplication::copyAllFilters()
{
QScopedPointer<Mlt::Producer> producer(
new Mlt::Producer(MAIN.filterController()->attachedModel()->producer()));
MLT.copyFilters(producer.data(), MLT.FILTER_INDEX_ALL);
QGuiApplication::clipboard()->setText(MLT.filtersClipboardXML());
emit QmlApplication::singleton().filtersCopied();
}
void QmlApplication::copyCurrentFilter()
{
int currentIndex = MAIN.filterController()->currentIndex();
if (currentIndex < 0) {
MAIN.showStatusMessage(tr("Select a filter to copy"));
return;
}
QScopedPointer<Mlt::Producer> producer(
new Mlt::Producer(MAIN.filterController()->attachedModel()->producer()));
MLT.copyFilters(producer.data(), currentIndex);
QGuiApplication::clipboard()->setText(MLT.filtersClipboardXML());
emit QmlApplication::singleton().filtersCopied();
}
QString QmlApplication::clockFromFrames(int frames)
{
if (MLT.producer()) {
return MLT.producer()->frames_to_time(frames, Settings.timeFormat());
}
return QString();
}
QString QmlApplication::timeFromFrames(int frames)
{
if (MLT.producer()) {
return MLT.producer()->frames_to_time(frames, Settings.timeFormat());
}
return QString();
}
int QmlApplication::audioChannels()
{
return MLT.audioChannels();
}
QString QmlApplication::getNextProjectFile(const QString &filename)
{
QDir dir(MLT.projectFolder());
if (!MLT.projectFolder().isEmpty() && dir.exists()) {
QFileInfo info(filename);
QString basename = info.completeBaseName();
QString extension = info.suffix();
if (extension.isEmpty()) {
extension = basename;
basename = QString();
}
for (unsigned i = 1; i < std::numeric_limits<unsigned>::max(); i++) {
QString filename = QString::fromLatin1("%1%2.%3").arg(basename).arg(i).arg(extension);
if (!dir.exists(filename))
return dir.filePath(filename);
}
}
return QString();
}
bool QmlApplication::isProjectFolder()
{
QDir dir(MLT.projectFolder());
return (!MLT.projectFolder().isEmpty() && dir.exists());
}
qreal QmlApplication::devicePixelRatio()
{
return MAIN.devicePixelRatioF();
}
void QmlApplication::showStatusMessage(const QString &message, int timeoutSeconds)
{
MAIN.showStatusMessage(message, timeoutSeconds);
}
int QmlApplication::maxTextureSize()
{
auto *videoWidget = qobject_cast<Mlt::VideoWidget *>(MLT.videoWidget());
return videoWidget ? videoWidget->maxTextureSize() : 0;
}
bool QmlApplication::confirmOutputFilter()
{
bool result = true;
if (MAIN.filterController()->isOutputTrackSelected() && Settings.askOutputFilter()) {
QMessageBox dialog(QMessageBox::Warning,
qApp->applicationName(),
tr("<p>Do you really want to add filters to <b>Output</b>?</p>"
"<p><b>Timeline > Output</b> is currently selected. "
"Adding filters to <b>Output</b> affects ALL clips in the "
"timeline including new ones that will be added.</p>"),
QMessageBox::No | QMessageBox::Yes,
&MAIN);
dialog.setWindowModality(dialogModality());
dialog.setDefaultButton(QMessageBox::No);
dialog.setEscapeButton(QMessageBox::Yes);
dialog.setCheckBox(
new QCheckBox(tr("Do not show this anymore.", "confirm output filters dialog")));
result = dialog.exec() == QMessageBox::Yes;
if (dialog.checkBox()->isChecked()) {
Settings.setAskOutputFilter(false);
}
}
return result;
}
QDir QmlApplication::dataDir()
{
QDir dir(qApp->applicationDirPath());
#if defined(Q_OS_MAC)
dir.cdUp();
dir.cd("Resources");
#else
#if defined(Q_OS_UNIX) || (defined(Q_OS_WIN) && defined(NODEPLOY))
dir.cdUp();
#endif
dir.cd("share");
#endif
return dir;
}
QColor QmlApplication::contrastingColor(QString color)
{
return Util::textColor(color);
}
QStringList QmlApplication::wipes()
{
QStringList result;
const auto transitions = QString::fromLatin1("transitions");
QDir dir(Settings.appDataLocation());
if (!dir.exists(transitions)) {
dir.mkdir(transitions);
}
if (dir.cd(transitions)) {
for (auto &s : dir.entryList(QDir::Files | QDir::Readable)) {
result << dir.filePath(s);
}
}
return result;
}
bool QmlApplication::addWipe(const QString &filePath)
{
const auto transitions = QString::fromLatin1("transitions");
QDir dir(Settings.appDataLocation());
if (!dir.exists(transitions)) {
dir.mkdir(transitions);
}
if (dir.cd(transitions)) {
return QFile::copy(filePath, dir.filePath(QFileInfo(filePath).fileName()));
}
return false;
}
bool QmlApplication::intersects(const QRectF &a, const QRectF &b)
{
return a.intersects(b);
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2014-2024 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QMLAPPLICATION_H
#define QMLAPPLICATION_H
#include <QColor>
#include <QDir>
#include <QObject>
#include <QPoint>
#include <QRect>
namespace Mlt {
class Producer;
}
class QmlApplication : public QObject
{
Q_OBJECT
Q_PROPERTY(Qt::WindowModality dialogModality READ dialogModality CONSTANT);
Q_PROPERTY(QPoint mousePos READ mousePos);
Q_PROPERTY(QColor toolTipBaseColor READ toolTipBaseColor NOTIFY paletteChanged)
Q_PROPERTY(QColor toolTipTextColor READ toolTipTextColor NOTIFY paletteChanged)
Q_PROPERTY(QString OS READ OS CONSTANT)
Q_PROPERTY(QRect mainWinRect READ mainWinRect);
Q_PROPERTY(bool hasFiltersOnClipboard READ hasFiltersOnClipboard NOTIFY filtersCopied)
Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio CONSTANT)
Q_PROPERTY(int maxTextureSize READ maxTextureSize CONSTANT)
Q_PROPERTY(QStringList wipes READ wipes CONSTANT)
public:
static QmlApplication &singleton();
static Qt::WindowModality dialogModality();
static QPoint mousePos();
static QColor toolTipBaseColor();
static QColor toolTipTextColor();
static QString OS();
static QRect mainWinRect();
static bool hasFiltersOnClipboard();
Q_INVOKABLE static void copyAllFilters();
Q_INVOKABLE static void copyEnabledFilters();
Q_INVOKABLE static void copyCurrentFilter();
Q_INVOKABLE static QString clockFromFrames(int frames);
Q_INVOKABLE static QString timeFromFrames(int frames);
Q_INVOKABLE static int audioChannels();
Q_INVOKABLE static QString getNextProjectFile(const QString &filename);
Q_INVOKABLE static bool isProjectFolder();
static qreal devicePixelRatio();
Q_INVOKABLE void showStatusMessage(const QString &message, int timeoutSeconds = 15);
static int maxTextureSize();
Q_INVOKABLE static bool confirmOutputFilter();
static QDir dataDir();
Q_INVOKABLE static QColor contrastingColor(QString color);
static QStringList wipes();
Q_INVOKABLE static bool addWipe(const QString &filePath);
Q_INVOKABLE static bool intersects(const QRectF &a, const QRectF &b);
signals:
void paletteChanged();
void filtersCopied();
void filtersPasted(Mlt::Producer *);
private:
explicit QmlApplication();
QmlApplication(QmlApplication const &);
void operator=(QmlApplication const &);
};
#endif // QMLAPPLICATION_H

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2022 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmleditmenu.h"
#include <QMenu>
QmlEditMenu::QmlEditMenu(QObject *parent)
: QObject(parent)
, m_showPastePlain(false)
, m_readOnly(false)
{}
void QmlEditMenu::popup()
{
QMenu menu;
QAction undoAction(tr("Undo"));
undoAction.setShortcut(QKeySequence::Undo);
connect(&undoAction, &QAction::triggered, this, &QmlEditMenu::undoTriggered);
if (!m_readOnly)
menu.addAction(&undoAction);
QAction redoAction(tr("Redo"));
redoAction.setShortcut(QKeySequence::Redo);
connect(&redoAction, &QAction::triggered, this, &QmlEditMenu::redoTriggered);
if (!m_readOnly)
menu.addAction(&redoAction);
if (!m_readOnly)
menu.addSeparator();
QAction cutAction(tr("Cut"));
cutAction.setShortcut(QKeySequence::Cut);
connect(&cutAction, &QAction::triggered, this, &QmlEditMenu::cutTriggered);
if (!m_readOnly)
menu.addAction(&cutAction);
QAction copyAction(tr("Copy"));
copyAction.setShortcut(QKeySequence::Copy);
connect(&copyAction, &QAction::triggered, this, &QmlEditMenu::copyTriggered);
menu.addAction(&copyAction);
QAction pasteAction(tr("Paste"));
pasteAction.setShortcut(QKeySequence::Paste);
connect(&pasteAction, &QAction::triggered, this, &QmlEditMenu::pasteTriggered);
if (!m_readOnly)
menu.addAction(&pasteAction);
QAction pastePlainAction(tr("Paste Text Only"));
pastePlainAction.setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_V));
connect(&pastePlainAction, &QAction::triggered, this, &QmlEditMenu::pastePlainTriggered);
if (m_showPastePlain && !m_readOnly)
menu.addAction(&pastePlainAction);
QAction deleteAction(tr("Delete"));
deleteAction.setShortcut(QKeySequence::Delete);
connect(&deleteAction, &QAction::triggered, this, &QmlEditMenu::deleteTriggered);
if (!m_readOnly)
menu.addAction(&deleteAction);
QAction clearAction(tr("Clear"));
connect(&clearAction, &QAction::triggered, this, &QmlEditMenu::clearTriggered);
if (!m_readOnly)
menu.addAction(&clearAction);
if (!m_readOnly)
menu.addSeparator();
QAction selectAllAction(tr("Select All"));
selectAllAction.setShortcut(QKeySequence::SelectAll);
connect(&selectAllAction, &QAction::triggered, this, &QmlEditMenu::selectAllTriggered);
menu.addAction(&selectAllAction);
menu.exec(QCursor::pos());
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2022 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QMLEDITMENU_H
#define QMLEDITMENU_H
#include <QObject>
class QmlEditMenu : public QObject
{
Q_OBJECT
Q_PROPERTY(bool showPastePlain MEMBER m_showPastePlain NOTIFY showPastePlainChanged)
Q_PROPERTY(bool readOnly MEMBER m_readOnly NOTIFY readOnlyChanged)
public:
explicit QmlEditMenu(QObject *parent = 0);
signals:
void showPastePlainChanged();
void readOnlyChanged();
void undoTriggered();
void redoTriggered();
void cutTriggered();
void copyTriggered();
void pasteTriggered();
void pastePlainTriggered();
void deleteTriggered();
void clearTriggered();
void selectAllTriggered();
public slots:
void popup();
private:
bool m_showPastePlain;
bool m_readOnly;
};
#endif // QMLEDITMENU_H

View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) 2025 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmlextension.h"
#include "Logger.h"
#include "qmltypes/qmlutilities.h"
#include "settings.h"
#include <QDir>
#include <QQmlComponent>
const QString QmlExtension::WHISPER_ID = QStringLiteral("whispermodel");
QmlExtensionFile::QmlExtensionFile(QObject *parent)
: QObject(parent)
, m_standard(false)
{}
QmlExtension *QmlExtension::load(const QString &id)
{
QString filePath = appDir(id).absoluteFilePath(extensionFileName(id));
if (!QFile::exists(filePath)) {
filePath = installDir(id).absoluteFilePath(extensionFileName(id));
}
if (!QFile::exists(filePath)) {
LOG_ERROR() << filePath << "does not exist";
return nullptr;
}
QQmlComponent component(QmlUtilities::sharedEngine(), filePath);
QmlExtension *extension = qobject_cast<QmlExtension *>(component.create());
if (!extension) {
LOG_ERROR() << component.errorString();
}
return extension;
}
QString QmlExtension::extensionFileName(const QString &id)
{
return id + ".qml";
}
QDir QmlExtension::installDir(const QString &id)
{
QDir dir = QmlUtilities::qmlDir();
dir.mkdir("extensions");
dir.cd("extensions");
return dir;
}
QDir QmlExtension::appDir(const QString &id)
{
QDir dir = Settings.appDataLocation();
dir.mkdir("extensions");
dir.cd("extensions");
dir.mkdir(id);
dir.cd(id);
return dir;
}
QmlExtension::QmlExtension(QObject *parent)
: QObject(parent)
{}
void QmlExtension::setId(const QString &id)
{
m_id = id;
emit changed();
}
void QmlExtension::setName(const QString &name)
{
m_name = name;
emit changed();
}
void QmlExtension::setVersion(const QString &version)
{
m_version = version;
emit changed();
}
QString QmlExtension::localPath(int index)
{
if (index < 0 || index >= fileCount()) {
LOG_ERROR() << "Invalid Index" << index;
return QString();
}
QDir localPath = appDir(m_id);
return localPath.absoluteFilePath(m_files[index]->file());
}
bool QmlExtension::downloaded(int index)
{
return QFile(localPath(index)).exists();
}

100
src/qmltypes/qmlextension.h Normal file
View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2025 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QMLEXTENSION_H
#define QMLEXTENSION_H
#include <QDir>
#include <QObject>
#include <QQmlListProperty>
#include <QString>
class QmlExtensionFile : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name MEMBER m_name NOTIFY changed)
Q_PROPERTY(QString description MEMBER m_description NOTIFY changed)
Q_PROPERTY(QString file MEMBER m_file NOTIFY changed)
Q_PROPERTY(QString url MEMBER m_url NOTIFY changed)
Q_PROPERTY(QString size MEMBER m_size NOTIFY changed)
Q_PROPERTY(bool standard MEMBER m_standard NOTIFY changed)
public:
explicit QmlExtensionFile(QObject *parent = 0);
QString name() const { return m_name; }
QString description() const { return m_description; }
QString file() const { return m_file; }
QString url() const { return m_url; }
QString size() const { return m_size; }
bool standard() const { return m_standard; }
signals:
void changed();
private:
QString m_name;
QString m_description;
QString m_file;
QString m_url;
QString m_size;
bool m_standard;
};
class QmlExtension : public QObject
{
Q_OBJECT
Q_PROPERTY(QString id READ id WRITE setId NOTIFY changed)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY changed)
Q_PROPERTY(QString version READ version WRITE setVersion NOTIFY changed)
Q_PROPERTY(QQmlListProperty<QmlExtensionFile> files READ files NOTIFY changed)
public:
static QmlExtension *load(const QString &id);
static QString extensionFileName(const QString &id);
static QDir installDir(const QString &id);
static QDir appDir(const QString &id);
static const QString WHISPER_ID;
explicit QmlExtension(QObject *parent = 0);
QString id() const { return m_id; }
void setId(const QString &);
QString name() const { return m_name; }
void setName(const QString &);
QString version() const { return m_version; }
void setVersion(const QString &);
QQmlListProperty<QmlExtensionFile> files()
{
return QQmlListProperty<QmlExtensionFile>(this, &m_files);
}
int fileCount() const { return m_files.count(); }
QmlExtensionFile *file(int index) const { return m_files[index]; }
QString localPath(int index);
bool downloaded(int index);
signals:
void changed();
private:
QString m_id;
QString m_name;
QString m_version;
QList<QmlExtensionFile *> m_files;
};
#endif // QMLEXTENSION_H

137
src/qmltypes/qmlfile.cpp Normal file
View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2014-2022 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmlfile.h"
#include "Logger.h"
#include <QDir>
#include <QFile>
#include <QFileInfo>
QmlFile::QmlFile(QObject *parent)
: QObject(parent)
, m_url()
{}
QString QmlFile::getUrl()
{
auto s = QUrl::fromPercentEncoding(m_url.toString().toUtf8());
#ifdef Q_OS_WIN
if (s.size() > 2 && s[1] == ':' && s[2] == '/') {
s[0] = s[0].toUpper();
}
#endif
return s;
}
void QmlFile::setUrl(QString text)
{
QUrl url = text.replace('\\', "/");
QString s = url.toString();
;
QUrl::FormattingOptions options = QUrl::RemoveScheme | QUrl::RemovePassword
| QUrl::RemoveUserInfo | QUrl::RemovePort
| QUrl::RemoveAuthority | QUrl::RemoveQuery;
if (s.startsWith("file://") && s.size() > 9 && s[9] != ':') {
// QUrl removes the host from a UNC path when removing the scheme.
options ^= QUrl::RemoveScheme;
options ^= QUrl::RemoveAuthority;
}
#ifdef Q_OS_WIN
// If the scheme is a drive letter, do not remove it.
if (url.scheme().size() == 1) {
options ^= QUrl::RemoveScheme;
}
#endif
s = url.adjusted(options).toString();
#ifdef Q_OS_WIN
// If there is a slash before a drive letter.
// On Windows, file URLs look like file:///C:/Users/....
// The scheme is removed but only "://" (not 3 slashes) between scheme and path.
if (s.size() > 2 && s[0] == '/' && s[2] == ':') {
// Remove the leading slash.
s = s.mid(1);
}
#endif
if (s.startsWith("file://")) { // UNC path
// Remove the scheme.
s = s.mid(5);
}
if (s.startsWith("///")) {
// Linux leaves 3 leading slashes sometimes
s = s.mid(2);
}
QUrl adj = s;
if (m_url != adj) {
m_url = adj;
emit urlChanged(m_url);
}
}
QString QmlFile::getFileName()
{
return QFileInfo(getUrl()).fileName();
}
QString QmlFile::getPath()
{
return QDir::toNativeSeparators(QFileInfo(getUrl()).path());
}
QString QmlFile::getFilePath()
{
return QDir::toNativeSeparators(getUrl());
}
void QmlFile::copyFromFile(QString source)
{
if (QFile::exists(m_url.toString())) {
QFile::remove(m_url.toString());
}
QFile inFile(source);
QFile outfile(m_url.toString());
inFile.open(QFile::ReadOnly);
outfile.open(QFile::WriteOnly);
outfile.write(inFile.readAll());
outfile.close();
}
bool QmlFile::exists()
{
return QFileInfo(m_url.toString()).exists();
}
QString QmlFile::suffix()
{
return QFileInfo(m_url.toString()).suffix();
}
void QmlFile::watch()
{
m_watcher.reset(new QFileSystemWatcher({getUrl()}));
connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &QmlFile::fileChanged);
}

58
src/qmltypes/qmlfile.h Normal file
View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2014-2022 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QMLFILE_H
#define QMLFILE_H
#include <QFileSystemWatcher>
#include <QObject>
#include <QUrl>
#include <memory>
class QmlFile : public QObject
{
Q_OBJECT
Q_PROPERTY(QString url READ getUrl WRITE setUrl NOTIFY urlChanged)
Q_PROPERTY(QString fileName READ getFileName)
Q_PROPERTY(QString path READ getPath)
Q_PROPERTY(QString filePath READ getFilePath)
public:
explicit QmlFile(QObject *parent = 0);
QString getUrl();
void setUrl(QString text);
QString getFileName();
QString getPath();
QString getFilePath();
Q_INVOKABLE void copyFromFile(QString source);
Q_INVOKABLE bool exists();
Q_INVOKABLE QString suffix();
public slots:
void watch();
signals:
void urlChanged(const QUrl &url);
void fileChanged(const QString &path);
private:
QUrl m_url;
std::unique_ptr<QFileSystemWatcher> m_watcher;
};
#endif // QMLFILE_H

1253
src/qmltypes/qmlfilter.cpp Normal file

File diff suppressed because it is too large Load Diff

197
src/qmltypes/qmlfilter.h Normal file
View File

@@ -0,0 +1,197 @@
/*
* Copyright (c) 2013-2024 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FILTER_H
#define FILTER_H
#include "qmlmetadata.h"
#include "shotcut_mlt_properties.h"
#include <MltAnimation.h>
#include <MltProducer.h>
#include <MltService.h>
#include <QColor>
#include <QObject>
#include <QRectF>
#include <QString>
#include <QUuid>
#include <QVariant>
class AbstractJob;
class EncodeJob;
class QUndoCommand;
class FilterController;
class QmlFilter : public QObject
{
Q_OBJECT
Q_PROPERTY(bool isNew READ isNew CONSTANT)
Q_PROPERTY(QString path READ path CONSTANT)
Q_PROPERTY(QStringList presets READ presets NOTIFY presetsChanged)
Q_PROPERTY(int in READ in NOTIFY inChanged)
Q_PROPERTY(int out READ out NOTIFY outChanged)
Q_PROPERTY(int animateIn READ animateIn WRITE setAnimateIn NOTIFY animateInChanged)
Q_PROPERTY(int animateOut READ animateOut WRITE setAnimateOut NOTIFY animateOutChanged)
Q_PROPERTY(int duration READ duration NOTIFY durationChanged)
Q_PROPERTY(bool blockSignals READ signalsBlocked WRITE blockSignals)
public:
enum CurrentFilterIndex { NoCurrentFilter = -1, DeselectCurrentFilter = -2 };
Q_ENUM(CurrentFilterIndex)
explicit QmlFilter();
explicit QmlFilter(Mlt::Service &mltService,
const QmlMetadata *metadata,
QObject *parent = nullptr);
~QmlFilter();
bool isNew() const { return m_isNew; }
void setIsNew(bool isNew) { m_isNew = isNew; }
Q_INVOKABLE QString get(QString name, int position = -1);
Q_INVOKABLE QColor getColor(QString name, int position = -1);
Q_INVOKABLE double getDouble(QString name, int position = -1);
Q_INVOKABLE QRectF getRect(QString name, int position = -1);
Q_INVOKABLE void removeRectPercents(QString name);
Q_INVOKABLE QStringList getGradient(QString name);
Q_INVOKABLE void set(QString name, QString value, int position = -1);
Q_INVOKABLE void set(QString name,
const QColor &value,
int position = -1,
mlt_keyframe_type keyframeType = mlt_keyframe_type(-1));
Q_INVOKABLE void set(QString name,
double value,
int position = -1,
mlt_keyframe_type keyframeType = mlt_keyframe_type(-1));
Q_INVOKABLE void set(QString name,
int value,
int position = -1,
mlt_keyframe_type keyframeType = mlt_keyframe_type(-1));
Q_INVOKABLE void set(QString name,
bool value,
int position = -1,
mlt_keyframe_type keyframeType = mlt_keyframe_type(-1));
Q_INVOKABLE void set(QString name,
double x,
double y,
double width,
double height,
double opacity = 1.0,
int position = -1,
mlt_keyframe_type keyframeType = mlt_keyframe_type(-1));
Q_INVOKABLE void set(QString name,
const QRectF &rect,
int position = -1,
mlt_keyframe_type keyframeType = mlt_keyframe_type(-1));
Q_INVOKABLE void setGradient(QString name, const QStringList &gradient);
QString path() const { return m_path; }
Q_INVOKABLE void loadPresets();
QStringList presets() const { return m_presets; }
/// returns the index of the new preset
Q_INVOKABLE int savePreset(const QStringList &propertyNames, const QString &name = QString());
Q_INVOKABLE void deletePreset(const QString &name);
Q_INVOKABLE void analyze(bool isAudio = false, bool deferJob = true);
Q_INVOKABLE static int framesFromTime(const QString &time);
Q_INVOKABLE void getHash();
Mlt::Producer &producer() { return m_producer; }
int in();
int out();
Mlt::Service &service() { return m_service; }
int animateIn();
void setAnimateIn(int value);
int animateOut();
void setAnimateOut(int value);
void clearAnimateInOut();
int duration();
Q_INVOKABLE void resetProperty(const QString &name);
Q_INVOKABLE void clearSimpleAnimation(const QString &name);
Mlt::Animation getAnimation(const QString &name);
Q_INVOKABLE int keyframeCount(const QString &name);
mlt_keyframe_type getKeyframeType(Mlt::Animation &animation,
int position,
mlt_keyframe_type defaultType);
Q_INVOKABLE int getKeyFrameType(const QString &name, int keyIndex);
Q_INVOKABLE void setKeyFrameType(const QString &name, int keyIndex, int type);
Q_INVOKABLE int getNextKeyframePosition(const QString &name, int position);
Q_INVOKABLE int getPrevKeyframePosition(const QString &name, int position);
Q_INVOKABLE bool isAtLeastVersion(const QString &version);
Q_INVOKABLE static void deselect();
bool allowTrim() const;
bool allowAnimateIn() const;
bool allowAnimateOut() const;
Q_INVOKABLE void crop(const QRectF &rect);
QString objectNameOrService();
Q_INVOKABLE void copyParameters();
Q_INVOKABLE void pasteParameters(const QStringList &propertyNames);
// Functions for undo/redo
void startUndoTracking();
void stopUndoTracking();
Q_INVOKABLE void startUndoParameterCommand(const QString &desc = QString());
void startUndoAddKeyframeCommand();
void startUndoRemoveKeyframeCommand();
void startUndoModifyKeyframeCommand(int paramIndex, int keyframeIndex);
void updateUndoCommand(const QString &name);
Q_INVOKABLE void endUndoCommand();
public slots:
void preset(const QString &name);
signals:
void presetsChanged();
void analyzeFinished(bool isSuccess);
void changed(QString name = QString());
void inChanged(int delta);
void outChanged(int delta);
void animateInChanged();
void animateOutChanged();
void animateInOutChanged();
void durationChanged();
void propertyChanged(QString name); // Use to let QML know when a specific property has changed
private:
const QmlMetadata *m_metadata;
Mlt::Service m_service;
Mlt::Producer m_producer;
QString m_path;
bool m_isNew;
QStringList m_presets;
Mlt::Properties m_previousState;
int m_changeInProgress;
int keyframeIndex(Mlt::Animation &animation, int position);
};
class AnalyzeDelegate : public QObject
{
Q_OBJECT
public:
explicit AnalyzeDelegate(Mlt::Filter &filter);
public slots:
void onAnalyzeFinished(AbstractJob *job, bool isSuccess);
private:
QString resultsFromXml(const QString &fileName);
void updateFilter(Mlt::Filter &filter, const QString &results);
void updateJob(EncodeJob *job, const QString &results);
QUuid m_uuid;
};
#endif // FILTER_H

View File

@@ -0,0 +1,117 @@
/*
* Copyright (c) 2022-2025 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmlmarkermenu.h"
#include "actions.h"
#include "docks/timelinedock.h"
#include "qmltypes/colordialog.h"
#include "qmltypes/qmlapplication.h"
#include <QLabel>
#include <QMenu>
#include <QToolButton>
#include <QWidgetAction>
QmlMarkerMenu::QmlMarkerMenu(QObject *parent)
: QObject(parent)
, m_timeline(nullptr)
, m_index(-1)
{}
QObject *QmlMarkerMenu::target()
{
return m_timeline;
}
void QmlMarkerMenu::setTarget(QObject *target)
{
m_timeline = dynamic_cast<TimelineDock *>(target);
}
int QmlMarkerMenu::index()
{
return m_index;
}
void QmlMarkerMenu::setIndex(int index)
{
m_index = index;
}
void QmlMarkerMenu::popup()
{
if (!m_timeline || m_index < 0)
return;
QMenu menu;
QAction editAction(tr("Edit..."));
editAction.setShortcut(Actions["timelineMarkerAction"]->shortcut());
connect(&editAction, &QAction::triggered, this, [&]() { m_timeline->editMarker(m_index); });
menu.addAction(&editAction);
QAction deleteAction(tr("Delete"));
deleteAction.setShortcut(Actions["timelineDeleteMarkerAction"]->shortcut());
connect(&deleteAction, &QAction::triggered, this, [&]() { m_timeline->deleteMarker(m_index); });
menu.addAction(&deleteAction);
QAction colorAction(tr("Choose Color..."));
connect(&colorAction, &QAction::triggered, this, [&]() {
QColor markerColor = m_timeline->markersModel()->getMarker(m_index).color;
auto newColor = ColorDialog::getColor(markerColor, nullptr, QString(), false);
if (newColor.isValid()) {
m_timeline->markersModel()->setColor(m_index, newColor);
}
});
menu.addAction(&colorAction);
QMenu *recentColorMenu = menu.addMenu(tr("Choose Recent Color"));
QStringList colors = m_timeline->markersModel()->recentColors();
QString highlightColor = QApplication::palette().highlight().color().name();
for (int c = 0; c < colors.size(); c++) {
QWidgetAction *widgetAction = new QWidgetAction(recentColorMenu);
QToolButton *colorButton = new QToolButton();
colorButton->setText(colors[c]);
QString textColor = QmlApplication::contrastingColor(colors[c]).name();
QString styleSheet = QString("QToolButton {"
" background-color: %1;"
" border-style: solid;"
" border-width: 3px;"
" border-color: %1;"
" color: %2"
"}"
"QToolButton:hover {"
" background-color: %1;"
" border-style: solid;"
" border-width: 3px;"
" border-color: %3;"
" color: %2"
"}")
.arg(colors[c])
.arg(textColor)
.arg(highlightColor);
colorButton->setStyleSheet(styleSheet);
connect(colorButton, &QToolButton::clicked, this, [&, colorButton]() {
m_timeline->markersModel()->setColor(m_index, colorButton->text());
menu.close();
});
widgetAction->setDefaultWidget(colorButton);
recentColorMenu->addAction(widgetAction);
}
menu.exec(QCursor::pos());
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2022 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QMLMARKERMENU_H
#define QMLMARKERMENU_H
#include <QObject>
class TimelineDock;
class QmlMarkerMenu : public QObject
{
Q_OBJECT
Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged)
Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged)
public:
explicit QmlMarkerMenu(QObject *parent = 0);
QObject *target();
void setTarget(QObject *timeline);
int index();
void setIndex(int index);
signals:
void targetChanged();
void indexChanged();
public slots:
void popup();
private:
TimelineDock *m_timeline;
int m_index;
};
#endif // QMLMARKERMENU_H

View File

@@ -0,0 +1,229 @@
/*
* Copyright (c) 2013-2024 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmlmetadata.h"
#include "Logger.h"
#include "settings.h"
#include <QVersionNumber>
QmlMetadata::QmlMetadata(QObject *parent)
: QObject(parent)
, m_type(Filter)
, m_needsGPU(false)
, m_qmlFileName("")
, m_vuiFileName("")
, m_isAudio(false)
, m_isHidden(false)
, m_isFavorite(false)
, m_gpuAlt("")
, m_allowMultiple(true)
, m_isClipOnly(false)
, m_isTrackOnly(false)
, m_isOutputOnly(false)
, m_isGpuCompatible(true)
, m_isDeprecated(false)
, m_seekReverse(false)
{}
void QmlMetadata::loadSettings()
{
//Override the default favorite setting if it has been set by the user.
QString favorite = Settings.filterFavorite(uniqueId());
if (favorite == "yes") {
m_isFavorite = true;
} else if (favorite == "no") {
m_isFavorite = false;
}
}
void QmlMetadata::setType(QmlMetadata::PluginType type)
{
m_type = type;
}
void QmlMetadata::setName(const QString &name)
{
m_name = name;
emit changed();
}
void QmlMetadata::set_mlt_service(const QString &service)
{
m_mlt_service = service;
}
QString QmlMetadata::uniqueId() const
{
if (!objectName().isEmpty()) {
return objectName();
} else if (m_type == FilterSet) {
return m_name;
} else {
return mlt_service();
}
}
void QmlMetadata::setNeedsGPU(bool needs)
{
m_needsGPU = needs;
emit changed();
}
void QmlMetadata::setQmlFileName(const QString &fileName)
{
m_qmlFileName = fileName;
}
void QmlMetadata::setVuiFileName(const QString &fileName)
{
m_vuiFileName = fileName;
}
void QmlMetadata::setPath(const QDir &path)
{
m_path = path;
}
QUrl QmlMetadata::qmlFilePath() const
{
QUrl retVal = QUrl();
if (!m_qmlFileName.isEmpty()) {
retVal = QUrl::fromLocalFile(m_path.absoluteFilePath(m_qmlFileName));
}
return retVal;
}
QUrl QmlMetadata::vuiFilePath() const
{
QUrl retVal = QUrl();
if (!m_vuiFileName.isEmpty()) {
retVal = QUrl::fromLocalFile(m_path.absoluteFilePath(m_vuiFileName));
}
return retVal;
}
void QmlMetadata::setIconFileName(const QString &fileName)
{
m_icon = fileName;
}
void QmlMetadata::setIsAudio(bool isAudio)
{
m_isAudio = isAudio;
emit changed();
}
void QmlMetadata::setIsHidden(bool isHidden)
{
m_isHidden = isHidden;
emit changed();
}
void QmlMetadata::setIsFavorite(bool isFavorite)
{
m_isFavorite = isFavorite;
if (!uniqueId().isEmpty()) {
if (isFavorite) {
Settings.setFilterFavorite(uniqueId(), "yes");
} else {
Settings.setFilterFavorite(uniqueId(), "no");
}
}
emit changed();
}
void QmlMetadata::setGpuAlt(const QString &gpuAlt)
{
m_gpuAlt = gpuAlt;
emit changed();
}
void QmlMetadata::setAllowMultiple(bool allowMultiple)
{
m_allowMultiple = allowMultiple;
}
void QmlMetadata::setIsClipOnly(bool isClipOnly)
{
m_isClipOnly = isClipOnly;
}
void QmlMetadata::setIsTrackOnly(bool isTrackOnly)
{
m_isTrackOnly = isTrackOnly;
}
void QmlMetadata::setIsOutputOnly(bool isOutputOnly)
{
m_isOutputOnly = isOutputOnly;
}
bool QmlMetadata::isMltVersion(const QString &version)
{
if (!m_minimumVersion.isEmpty()) {
LOG_DEBUG() << "MLT version:" << version << "Shotcut minimumVersion:" << m_minimumVersion;
if (QVersionNumber::fromString(version) < QVersionNumber::fromString(m_minimumVersion))
return false;
}
return true;
}
QmlKeyframesMetadata::QmlKeyframesMetadata(QObject *parent)
: QObject(parent)
, m_allowTrim(true)
, m_allowAnimateIn(false)
, m_allowAnimateOut(false)
, m_enabled(true)
, m_allowOvershoot(true)
{}
QmlKeyframesParameter *QmlKeyframesMetadata::parameter(const QString &propertyName) const
{
for (const auto &p : m_parameters) {
if (propertyName == p->property())
return p;
}
return nullptr;
}
void QmlKeyframesMetadata::checkVersion(const QString &version)
{
if (!m_minimumVersion.isEmpty()) {
LOG_DEBUG() << "MLT version:" << version << "Shotcut minimumVersion:" << m_minimumVersion;
if (QVersionNumber::fromString(version) < QVersionNumber::fromString(m_minimumVersion))
setDisabled();
}
}
void QmlKeyframesMetadata::setDisabled()
{
m_enabled = m_allowAnimateIn = m_allowAnimateOut = false;
}
QmlKeyframesParameter::QmlKeyframesParameter(QObject *parent)
: QObject(parent)
, m_isCurve(false)
, m_minimum(0.0)
, m_maximum(0.0)
, m_units("")
, m_isRectangle(false)
, m_rangeType(MinMax)
, m_isColor(false)
{}

246
src/qmltypes/qmlmetadata.h Normal file
View File

@@ -0,0 +1,246 @@
/*
* Copyright (c) 2013-2026 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QMLMETADATA_H
#define QMLMETADATA_H
#include <QDir>
#include <QObject>
#include <QQmlListProperty>
#include <QString>
#include <QUrl>
class QmlKeyframesParameter : public QObject
{
Q_OBJECT
Q_PROPERTY(RangeType rangeType MEMBER m_rangeType NOTIFY changed)
Q_PROPERTY(QString name MEMBER m_name NOTIFY changed)
Q_PROPERTY(QString property MEMBER m_property NOTIFY changed)
Q_PROPERTY(QStringList gangedProperties MEMBER m_gangedProperties NOTIFY changed)
Q_PROPERTY(QStringList gangedRectProperties MEMBER m_gangedRectProperties NOTIFY changed)
Q_PROPERTY(bool isCurve MEMBER m_isCurve NOTIFY changed)
Q_PROPERTY(double minimum MEMBER m_minimum NOTIFY changed)
Q_PROPERTY(double maximum MEMBER m_maximum NOTIFY changed)
Q_PROPERTY(QString units MEMBER m_units NOTIFY changed)
Q_PROPERTY(bool isRectangle MEMBER m_isRectangle NOTIFY changed)
Q_PROPERTY(bool isColor MEMBER m_isColor NOTIFY changed)
public:
enum RangeType {
MinMax,
ClipLength,
};
Q_ENUM(RangeType)
explicit QmlKeyframesParameter(QObject *parent = 0);
QString name() const { return m_name; }
QString property() const { return m_property; }
QStringList gangedProperties() const { return m_gangedProperties; }
QStringList gangedRectProperties() const { return m_gangedRectProperties; }
bool isCurve() const { return m_isCurve; }
double minimum() const { return m_minimum; }
double maximum() const { return m_maximum; }
QString units() const { return m_units; }
bool isRectangle() const { return m_isRectangle; }
RangeType rangeType() const { return m_rangeType; }
bool isColor() const { return m_isColor; }
signals:
void changed();
private:
QString m_name;
QString m_property;
QStringList m_gangedProperties;
QStringList m_gangedRectProperties;
bool m_isCurve;
double m_minimum;
double m_maximum;
QString m_units;
bool m_isRectangle;
RangeType m_rangeType;
bool m_isColor;
};
class QmlKeyframesMetadata : public QObject
{
Q_OBJECT
Q_PROPERTY(bool allowTrim MEMBER m_allowTrim NOTIFY changed)
Q_PROPERTY(bool allowAnimateIn MEMBER m_allowAnimateIn NOTIFY changed)
Q_PROPERTY(bool allowAnimateOut MEMBER m_allowAnimateOut NOTIFY changed)
Q_PROPERTY(QQmlListProperty<QmlKeyframesParameter> parameters READ parameters NOTIFY changed)
/// simpleProperties identifies a list of properties whose keyframe position must be updated when trimming.
Q_PROPERTY(QList<QString> simpleProperties MEMBER m_simpleProperties NOTIFY changed)
Q_PROPERTY(QString minimumVersion MEMBER m_minimumVersion NOTIFY changed)
Q_PROPERTY(bool enabled MEMBER m_enabled NOTIFY changed)
Q_PROPERTY(bool allowOvershoot MEMBER m_allowOvershoot NOTIFY changed)
public:
explicit QmlKeyframesMetadata(QObject *parent = 0);
bool allowTrim() const { return m_allowTrim; }
bool allowAnimateIn() const { return m_allowAnimateIn; }
bool allowAnimateOut() const { return m_allowAnimateOut; }
QList<QString> simpleProperties() const { return m_simpleProperties; }
bool allowOvershoot() const { return m_allowOvershoot; }
QQmlListProperty<QmlKeyframesParameter> parameters()
{
return QQmlListProperty<QmlKeyframesParameter>(this, &m_parameters);
}
int parameterCount() const { return m_parameters.count(); }
QmlKeyframesParameter *parameter(int index) const { return m_parameters[index]; }
Q_INVOKABLE QmlKeyframesParameter *parameter(const QString &propertyName) const;
void checkVersion(const QString &version);
void setDisabled();
signals:
void changed();
private:
bool m_allowTrim;
bool m_allowAnimateIn;
bool m_allowAnimateOut;
QList<QmlKeyframesParameter *> m_parameters;
QList<QString> m_simpleProperties;
QString m_minimumVersion;
bool m_enabled;
bool m_allowOvershoot;
};
class QmlMetadata : public QObject
{
Q_OBJECT
Q_PROPERTY(PluginType type READ type WRITE setType NOTIFY changed)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY changed)
Q_PROPERTY(QString mlt_service READ mlt_service WRITE set_mlt_service NOTIFY changed)
Q_PROPERTY(bool needsGPU READ needsGPU WRITE setNeedsGPU NOTIFY changed)
Q_PROPERTY(QString qml READ qmlFileName WRITE setQmlFileName NOTIFY changed)
Q_PROPERTY(QString vui READ vuiFileName WRITE setVuiFileName NOTIFY changed)
Q_PROPERTY(QUrl qmlFilePath READ qmlFilePath NOTIFY changed)
Q_PROPERTY(QUrl vuiFilePath READ vuiFilePath NOTIFY changed)
Q_PROPERTY(bool isAudio READ isAudio WRITE setIsAudio NOTIFY changed)
Q_PROPERTY(bool isHidden READ isHidden WRITE setIsHidden NOTIFY changed)
Q_PROPERTY(bool isFavorite READ isFavorite WRITE setIsFavorite NOTIFY changed)
Q_PROPERTY(QString gpuAlt READ gpuAlt WRITE setGpuAlt NOTIFY changed)
Q_PROPERTY(bool allowMultiple READ allowMultiple WRITE setAllowMultiple NOTIFY changed)
Q_PROPERTY(bool isClipOnly READ isClipOnly WRITE setIsClipOnly NOTIFY changed)
Q_PROPERTY(bool isTrackOnly READ isTrackOnly WRITE setIsTrackOnly NOTIFY changed)
Q_PROPERTY(bool isOutputOnly READ isOutputOnly WRITE setIsOutputOnly NOTIFY changed)
Q_PROPERTY(bool isGpuCompatible READ isGpuCompatible() WRITE setIsGpuCompatible NOTIFY changed)
Q_PROPERTY(QmlKeyframesMetadata *keyframes READ keyframes NOTIFY changed)
Q_PROPERTY(bool isDeprecated READ isDeprecated WRITE setIsDeprecated NOTIFY changed)
Q_PROPERTY(QString minimumVersion MEMBER m_minimumVersion NOTIFY changed)
Q_PROPERTY(QString keywords MEMBER m_keywords NOTIFY changed)
Q_PROPERTY(QString icon READ iconFilePath WRITE setIconFileName NOTIFY changed)
Q_PROPERTY(bool seekReverse MEMBER m_seekReverse NOTIFY changed)
Q_PROPERTY(QString help MEMBER m_helpText NOTIFY changed)
public:
enum PluginType {
Filter,
Producer,
Transition,
Link,
FilterSet,
};
Q_ENUM(PluginType)
unsigned filterMask;
explicit QmlMetadata(QObject *parent = 0);
void loadSettings();
PluginType type() const { return m_type; }
void setType(PluginType);
QString name() const { return m_name; }
void setName(const QString &);
QString mlt_service() const { return m_mlt_service; }
void set_mlt_service(const QString &);
QString uniqueId() const;
bool needsGPU() const { return m_needsGPU; }
void setNeedsGPU(bool);
QString qmlFileName() const { return m_qmlFileName; }
void setQmlFileName(const QString &);
QString vuiFileName() const { return m_vuiFileName; }
void setVuiFileName(const QString &);
QDir path() const { return m_path; }
void setPath(const QDir &path);
QUrl qmlFilePath() const;
QUrl vuiFilePath() const;
QString iconFilePath() const
{
return (m_icon.isEmpty() || m_icon.startsWith("qrc:"))
? m_icon
: QUrl::fromLocalFile(m_path.absoluteFilePath(m_icon)).toString();
}
void setIconFileName(const QString &);
bool isAudio() const { return m_isAudio; }
void setIsAudio(bool isAudio);
bool isHidden() const { return m_isHidden; }
void setIsHidden(bool isHidden);
bool isFavorite() const { return m_isFavorite; }
void setIsFavorite(bool isFavorite);
QString gpuAlt() const { return m_gpuAlt; }
void setGpuAlt(const QString &);
bool allowMultiple() const { return m_allowMultiple; }
void setAllowMultiple(bool allowMultiple);
bool isClipOnly() const { return m_isClipOnly; }
void setIsClipOnly(bool isClipOnly);
bool isTrackOnly() const { return m_isTrackOnly; }
void setIsTrackOnly(bool isTrackOnly);
bool isOutputOnly() const { return m_isOutputOnly; }
void setIsOutputOnly(bool isOutputOnly);
bool isGpuCompatible() const { return m_isGpuCompatible; }
void setIsGpuCompatible(bool isCompatible) { m_isGpuCompatible = isCompatible; }
QmlKeyframesMetadata *keyframes() { return &m_keyframes; }
const QmlKeyframesMetadata *keyframes() const { return &m_keyframes; }
bool isDeprecated() const { return m_isDeprecated; }
void setIsDeprecated(bool deprecated) { m_isDeprecated = deprecated; }
bool isMltVersion(const QString &version);
QString keywords() const { return m_keywords; }
bool seekReverse() const { return m_seekReverse; }
QString helpText() const { return m_helpText; }
signals:
void changed();
private:
PluginType m_type;
QString m_name;
QString m_mlt_service;
bool m_needsGPU;
QString m_qmlFileName;
QString m_vuiFileName;
QDir m_path;
bool m_isAudio;
bool m_isHidden;
bool m_isFavorite;
QString m_gpuAlt;
bool m_allowMultiple;
bool m_isClipOnly;
bool m_isTrackOnly;
bool m_isOutputOnly;
bool m_isGpuCompatible;
QmlKeyframesMetadata m_keyframes;
bool m_isDeprecated;
QString m_minimumVersion;
QString m_keywords;
QString m_icon;
bool m_seekReverse;
QString m_helpText;
};
#endif // QMLMETADATA_H

View File

@@ -0,0 +1,276 @@
/*
* Copyright (c) 2016-2023 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmlproducer.h"
#include "Logger.h"
#include "mainwindow.h"
#include "mltcontroller.h"
#include "models/audiolevelstask.h"
#include "qmltypes/qmlapplication.h"
#include "settings.h"
#include "util.h"
#include "widgets/glaxnimateproducerwidget.h"
static const char *kWidthProperty = "meta.media.width";
static const char *kHeightProperty = "meta.media.height";
static const char *kAspectNumProperty = "meta.media.sample_aspect_num";
static const char *kAspectDenProperty = "meta.media.sample_aspect_den";
QmlProducer::QmlProducer(QObject *parent)
: QObject(parent)
{
connect(this, SIGNAL(inChanged(int)), this, SIGNAL(durationChanged()));
connect(this, SIGNAL(outChanged(int)), this, SIGNAL(durationChanged()));
}
int QmlProducer::in()
{
if (!m_producer.is_valid())
return 0;
if (m_producer.get(kFilterInProperty))
// Shots on the timeline will set the producer to the cut parent.
// However, we want time-based filters such as fade in/out to use
// the cut's in/out and not the parent's.
return m_producer.get_int(kFilterInProperty);
else
return m_producer.get_in();
}
int QmlProducer::out()
{
if (!m_producer.is_valid())
return 0;
if (m_producer.get(kFilterOutProperty))
// Shots on the timeline will set the producer to the cut parent.
// However, we want time-based filters such as fade in/out to use
// the cut's in/out and not the parent's.
return m_producer.get_int(kFilterOutProperty);
else
return m_producer.get_out();
}
double QmlProducer::aspectRatio()
{
if (!m_producer.is_valid())
return 1.0;
if (m_producer.get(kHeightProperty)) {
double sar = 1.0;
if (m_producer.get(kAspectDenProperty)) {
sar = m_producer.get_double(kAspectNumProperty)
/ m_producer.get_double(kAspectDenProperty);
}
return sar * m_producer.get_double(kWidthProperty) / m_producer.get_double(kHeightProperty);
}
return MLT.profile().dar();
}
QString QmlProducer::resource()
{
if (!m_producer.is_valid())
return QString();
QString result = QString::fromUtf8(m_producer.get("resource"));
if (result == "<producer>" && m_producer.get("mlt_service"))
result = QString::fromUtf8(m_producer.get("mlt_service"));
return result;
}
QString QmlProducer::name()
{
return Util::producerTitle(m_producer);
}
QVariant QmlProducer::audioLevels()
{
if (!m_producer.is_valid())
return QVariant();
if (m_producer.get_data(kAudioLevelsProperty))
return QVariant::fromValue(*((QVariantList *) m_producer.get_data(kAudioLevelsProperty)));
else
return QVariant();
}
int QmlProducer::fadeIn()
{
if (!m_producer.is_valid())
return 0;
QScopedPointer<Mlt::Filter> filter(MLT.getFilter("fadeInVolume", &m_producer));
if (!filter || !filter->is_valid())
filter.reset(MLT.getFilter("fadeInBrightness", &m_producer));
if (!filter || !filter->is_valid())
filter.reset(MLT.getFilter("fadeInMovit", &m_producer));
return (filter && filter->is_valid()) ? filter->get_length() : 0;
}
int QmlProducer::fadeOut()
{
if (!m_producer.is_valid())
return 0;
QScopedPointer<Mlt::Filter> filter(MLT.getFilter("fadeOutVolume", &m_producer));
if (!filter || !filter->is_valid())
filter.reset(MLT.getFilter("fadeOutBrightness", &m_producer));
if (!filter || !filter->is_valid())
filter.reset(MLT.getFilter("fadeOutMovit", &m_producer));
return (filter && filter->is_valid()) ? filter->get_length() : 0;
}
double QmlProducer::speed()
{
double result = 1.0;
if (!m_producer.is_valid())
return result;
if (m_producer.is_valid()) {
if (!qstrcmp("timewarp", m_producer.get("mlt_service")))
result = m_producer.get_double("warp_speed");
}
return result;
}
void QmlProducer::setPosition(int position)
{
if (!m_producer.is_valid())
return;
int length = duration();
if (position < length) {
if (MLT.isMultitrack())
emit seeked(m_producer.get_int(kPlaylistStartProperty) + qMax(0, position));
else
emit seeked(in() + qMax(0, position));
} else if (m_position != length - 1) {
m_position = length - 1;
emit positionChanged(m_position);
}
}
void QmlProducer::seek(int position)
{
if (m_producer.is_valid() && m_position != position) {
m_position = position;
emit positionChanged(qBound(0, position, duration()));
}
}
Q_INVOKABLE bool QmlProducer::outOfBounds()
{
return m_position < 0 || m_position > duration();
}
void QmlProducer::newGlaxnimateFile(const QString &filename)
{
GlaxnimateIpcServer::instance().newFile(filename, duration());
}
void QmlProducer::launchGlaxnimate(const QString &filename) const
{
if (!filename.isEmpty()) {
GlaxnimateIpcServer::instance().launch(m_producer, filename, false);
}
}
void QmlProducer::audioLevelsReady(const QPersistentModelIndex &index)
{
Q_UNUSED(index)
emit audioLevelsChanged();
}
void QmlProducer::remakeAudioLevels()
{
AudioLevelsTask::start(m_producer, this, QModelIndex(), true);
}
void QmlProducer::remakeAudioLevels(bool isKeyframesVisible)
{
if (isKeyframesVisible)
AudioLevelsTask::start(m_producer, this, QModelIndex());
}
double QmlProducer::displayAspectRatio()
{
if (m_producer.is_valid() && m_producer.get(kHeightProperty)) {
double sar = 1.0;
if (m_producer.get(kAspectDenProperty)) {
sar = m_producer.get_double(kAspectNumProperty)
/ m_producer.get_double(kAspectDenProperty);
}
return sar * m_producer.get_double(kWidthProperty) / m_producer.get_double(kHeightProperty);
}
return MLT.profile().dar();
}
QString QmlProducer::get(QString name, int position)
{
if (m_producer.is_valid()) {
if (position < 0)
return QString::fromUtf8(m_producer.get(name.toUtf8().constData()));
else
return QString::fromUtf8(
m_producer.anim_get(name.toUtf8().constData(), position, duration()));
} else {
return QString();
}
}
double QmlProducer::getDouble(QString name, int position)
{
if (m_producer.is_valid()) {
if (position < 0)
return m_producer.get_double(name.toUtf8().constData());
else
return m_producer.anim_get_double(name.toUtf8().constData(), position, duration());
} else {
return 0.0;
}
}
QRectF QmlProducer::getRect(QString name, int position)
{
if (!m_producer.is_valid())
return QRectF();
QString s = QString::fromUtf8(m_producer.get(name.toUtf8().constData()));
if (!s.isEmpty()) {
mlt_rect rect;
if (position < 0) {
rect = m_producer.get_rect(name.toUtf8().constData());
} else {
rect = m_producer.anim_get_rect(name.toUtf8().constData(), position, duration());
}
if (s.contains('%')) {
return QRectF(qRound(rect.x * MLT.profile().width()),
qRound(rect.y * MLT.profile().height()),
qRound(rect.w * MLT.profile().width()),
qRound(rect.h * MLT.profile().height()));
} else {
return QRectF(rect.x, rect.y, rect.w, rect.h);
}
} else {
return QRectF(0.0, 0.0, 0.0, 0.0);
}
}
void QmlProducer::setProducer(Mlt::Producer &producer)
{
m_producer = producer;
if (m_producer.is_valid()) {
remakeAudioLevels(MAIN.keyframesDockIsVisible());
} else {
GlaxnimateIpcServer::instance().reset();
}
emit producerChanged();
emit inChanged(0);
emit outChanged(0);
emit lengthChanged();
}

103
src/qmltypes/qmlproducer.h Normal file
View File

@@ -0,0 +1,103 @@
/*
* Copyright (c) 2016-2023 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QMLPRODUCER_H
#define QMLPRODUCER_H
#include "shotcut_mlt_properties.h"
#include <MltProducer.h>
#include <QObject>
#include <QRectF>
#include <QString>
#include <QVariant>
class QmlProducer : public QObject
{
Q_OBJECT
Q_PROPERTY(int in READ in() NOTIFY inChanged)
Q_PROPERTY(int out READ out() NOTIFY outChanged)
Q_PROPERTY(int aspectRatio READ aspectRatio() NOTIFY producerChanged)
Q_PROPERTY(int duration READ duration() NOTIFY durationChanged)
Q_PROPERTY(int length READ length() NOTIFY lengthChanged)
Q_PROPERTY(QString resource READ resource() NOTIFY producerChanged)
Q_PROPERTY(QString mlt_service READ mlt_service() NOTIFY producerChanged)
Q_PROPERTY(QString hash READ hash() NOTIFY producerChanged)
Q_PROPERTY(QString name READ name() NOTIFY producerChanged)
Q_PROPERTY(QVariant audioLevels READ audioLevels NOTIFY audioLevelsChanged)
Q_PROPERTY(int fadeIn READ fadeIn NOTIFY producerChanged)
Q_PROPERTY(int fadeOut READ fadeOut NOTIFY producerChanged)
Q_PROPERTY(double speed READ speed NOTIFY producerChanged)
Q_PROPERTY(int position READ position WRITE setPosition NOTIFY positionChanged)
Q_PROPERTY(double displayAspectRatio READ displayAspectRatio NOTIFY producerChanged)
public:
explicit QmlProducer(QObject *parent = 0);
int in();
int out();
double aspectRatio();
int duration() { return m_producer.is_valid() ? out() - in() + 1 : 0; }
int length() { return m_producer.is_valid() ? m_producer.get_length() : 0; }
QString resource();
QString mlt_service()
{
return m_producer.is_valid() ? m_producer.get("mlt_service") : QString();
}
QString hash()
{
return m_producer.is_valid() ? m_producer.get(kShotcutHashProperty) : QString();
}
QString name();
QVariant audioLevels();
int fadeIn();
int fadeOut();
double speed();
int position() const { return m_position; }
void setPosition(int position);
void seek(int position);
Mlt::Producer &producer() { return m_producer; }
Q_INVOKABLE void audioLevelsReady(const QPersistentModelIndex &index);
Q_INVOKABLE void remakeAudioLevels();
double displayAspectRatio();
Q_INVOKABLE QString get(QString name, int position = -1);
Q_INVOKABLE double getDouble(QString name, int position = -1);
Q_INVOKABLE QRectF getRect(QString name, int position = -1);
Q_INVOKABLE bool outOfBounds();
Q_INVOKABLE void newGlaxnimateFile(const QString &filename);
Q_INVOKABLE void launchGlaxnimate(const QString &filename = QString()) const;
signals:
void producerChanged();
void positionChanged(int position);
void seeked(int position);
void inChanged(int delta);
void outChanged(int delta);
void audioLevelsChanged();
void durationChanged();
void lengthChanged();
public slots:
void setProducer(Mlt::Producer &producer);
void remakeAudioLevels(bool isKeyframesVisible);
private:
Mlt::Producer m_producer;
int m_position;
};
#endif // QMLPRODUCER_H

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2014 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmlprofile.h"
#include "mltcontroller.h"
QmlProfile &QmlProfile::singleton()
{
static QmlProfile instance;
return instance;
}
QmlProfile::QmlProfile()
: QObject()
{}
int QmlProfile::width() const
{
return MLT.profile().width();
}
int QmlProfile::height() const
{
return MLT.profile().height();
}
double QmlProfile::aspectRatio() const
{
return MLT.profile().dar();
}
double QmlProfile::fps() const
{
return MLT.profile().fps();
}
double QmlProfile::sar() const
{
return MLT.profile().sar();
}

50
src/qmltypes/qmlprofile.h Normal file
View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2014-2018 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROFILE_H
#define PROFILE_H
#include <QObject>
class QmlProfile : public QObject
{
Q_OBJECT
Q_PROPERTY(int width READ width CONSTANT)
Q_PROPERTY(int height READ height CONSTANT)
Q_PROPERTY(double aspectRatio READ aspectRatio CONSTANT)
Q_PROPERTY(double fps READ fps CONSTANT)
Q_PROPERTY(double sar READ sar CONSTANT)
public:
static QmlProfile &singleton();
int width() const;
int height() const;
double aspectRatio() const;
double fps() const;
double sar() const;
signals:
void profileChanged();
private:
explicit QmlProfile();
QmlProfile(QmlProfile const &);
void operator=(QmlProfile const &);
};
#endif // PROFILE_H

View File

@@ -0,0 +1,408 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (c) 2020-2024 Meltytech, LLC
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
****************************************************************************/
#include "qmlrichtext.h"
#include "Logger.h"
#include <QClipboard>
#include <QGuiApplication>
#include <QStringBuilder>
#include <QStringConverter>
#include <QtCore/QFileInfo>
#include <QtGui/QFontDatabase>
#include <QtGui/QTextCursor>
#include <QtGui/QTextDocument>
QmlRichText::QmlRichText()
: m_target(0)
, m_doc(0)
, m_cursorPosition(-1)
, m_selectionStart(0)
, m_selectionEnd(0)
{}
void QmlRichText::setTarget(QQuickItem *target)
{
m_doc = 0;
m_target = target;
if (!m_target)
return;
QVariant doc = m_target->property("textDocument");
if (doc.canConvert<QQuickTextDocument *>()) {
QQuickTextDocument *qqdoc = doc.value<QQuickTextDocument *>();
if (qqdoc) {
m_doc = qqdoc->textDocument();
connect(m_doc, &QTextDocument::contentsChanged, this, &QmlRichText::sizeChanged);
}
}
emit targetChanged();
}
void QmlRichText::setFileUrl(const QUrl &arg)
{
if (m_fileUrl != arg) {
m_fileUrl = arg;
QString fileName = QQmlFile::urlToLocalFileOrQrc(arg);
if (QFile::exists(fileName)) {
QFile file(fileName);
if (file.open(QFile::ReadOnly)) {
QByteArray data = file.readAll();
if (Qt::mightBeRichText(data)) {
auto decoder = QStringDecoder(
QStringConverter::encodingForHtml(data).value_or(QStringConverter::Utf8));
setText(decoder(data));
} else {
auto decoder = QStringDecoder(
QStringConverter::encodingForData(data).value_or(QStringConverter::Utf8));
setText(QStringLiteral("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
"\"http://www.w3.org/TR/REC-html40/strict.dtd\">"
"<html><head><meta name=\"qrichtext\" content=\"1\" "
"/><style type=\"text/css\">"
"p, li { white-space: pre-wrap; }"
#if defined(Q_OS_WIN)
"body { font-family:Verdana; font-size:72pt; "
"font-weight:normal; font-style:normal; color:#ffffff; }"
#elif defined(Q_OS_MAC)
"body { font-family:Helvetica; font-size:72pt; "
"font-weight:normal; font-style:normal; color:#ffffff; }"
#else
"body { font-family:sans-serif; font-size:72pt; "
"font-weight:normal; font-style:normal; color:#ffffff; }"
#endif
"</style></head><body>")
% QString(decoder(data)) % QStringLiteral("</body></html>"));
}
if (m_doc)
m_doc->setModified(false);
if (fileName.isEmpty())
m_documentTitle = QStringLiteral("untitled.txt");
else
m_documentTitle = QFileInfo(fileName).fileName();
emit textChanged();
reset();
}
}
emit fileUrlChanged();
}
}
void QmlRichText::setText(const QString &arg)
{
if (m_text != arg) {
m_text = arg;
emit textChanged();
}
}
void QmlRichText::saveAs(const QUrl &arg, QString fileType)
{
if (fileType.isEmpty())
fileType = QFileInfo(arg.toString()).suffix();
bool isHtml = fileType.contains(QLatin1String("htm"));
QLatin1String ext(isHtml ? ".html" : ".txt");
QString localPath = arg.toLocalFile();
if (!localPath.endsWith(ext))
localPath += ext;
QFile f(localPath);
if (!f.open(QFile::WriteOnly | QFile::Truncate | (isHtml ? QFile::NotOpen : QFile::Text))) {
emit error(tr("Cannot save: ") + f.errorString());
return;
}
QString s = isHtml ? m_doc->toHtml() : m_doc->toPlainText();
f.write(s.toUtf8());
f.close();
setFileUrl(QUrl::fromLocalFile(localPath));
}
void QmlRichText::insertTable(int rows, int columns, int border)
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return;
QString color = textColor().name(QColor::HexArgb);
QString html = QString("<style>"
"table { border-style: solid; border-color: %1 }"
"td { font: %2 %3 %4pt %5;"
"color: %1; vertical-align: top; }"
"</style>"
"<table width=100% cellspacing=0 cellpadding=%6 border=%6>")
.arg(color)
.arg(italic() ? "italic" : "normal")
.arg(bold() ? "bold" : "normal")
.arg(fontSize())
.arg(fontFamily())
.arg(border);
for (auto i = 0; i < rows; ++i) {
html += "<tr>";
for (auto j = 0; j < columns; ++j) {
if (j == 0) {
html += QStringLiteral("<td>%1 %2</td>").arg(tr("Row")).arg(i + 1);
} else {
html += QStringLiteral("<td>%1 %2</td>").arg(tr("Column")).arg(j + 1);
}
if (border == 0 && j + 1 < columns) {
html += "<td width=5%></td>";
}
}
html += "</tr>";
}
html += "</table>";
cursor.insertHtml(html);
}
void QmlRichText::indentLess()
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return;
auto indent = cursor.blockFormat().indent();
QTextBlockFormat format;
format.setIndent(qMax(indent - 1, 0));
cursor.mergeBlockFormat(format);
}
void QmlRichText::indentMore()
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return;
auto indent = cursor.blockFormat().indent();
QTextBlockFormat format;
format.setIndent(indent + 1);
cursor.mergeBlockFormat(format);
}
void QmlRichText::pastePlain()
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return;
cursor.insertText(QGuiApplication::clipboard()->text());
}
QUrl QmlRichText::fileUrl() const
{
return m_fileUrl;
}
QString QmlRichText::text() const
{
return m_text;
}
void QmlRichText::setCursorPosition(int position)
{
if (position == m_cursorPosition)
return;
m_cursorPosition = position;
reset();
}
void QmlRichText::reset()
{
emit fontFamilyChanged();
emit alignmentChanged();
emit boldChanged();
emit italicChanged();
emit underlineChanged();
emit fontSizeChanged();
emit textColorChanged();
}
QTextCursor QmlRichText::textCursor() const
{
if (!m_doc)
return QTextCursor();
QTextCursor cursor = QTextCursor(m_doc);
if (m_selectionStart != m_selectionEnd) {
cursor.setPosition(m_selectionStart);
cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
} else {
cursor.setPosition(m_cursorPosition);
}
return cursor;
}
void QmlRichText::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
{
QTextCursor cursor = textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
cursor.mergeCharFormat(format);
}
void QmlRichText::setSelectionStart(int position)
{
m_selectionStart = position;
}
void QmlRichText::setSelectionEnd(int position)
{
m_selectionEnd = position;
}
void QmlRichText::setAlignment(Qt::Alignment a)
{
if (!m_doc)
return;
QTextBlockFormat fmt;
fmt.setAlignment((Qt::Alignment) a);
QTextCursor cursor = QTextCursor(m_doc);
cursor.setPosition(m_selectionStart, QTextCursor::MoveAnchor);
cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
cursor.mergeBlockFormat(fmt);
emit alignmentChanged();
}
Qt::Alignment QmlRichText::alignment() const
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return Qt::AlignLeft;
return textCursor().blockFormat().alignment();
}
bool QmlRichText::bold() const
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return false;
return textCursor().charFormat().fontWeight() == QFont::Bold;
}
bool QmlRichText::italic() const
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return false;
return textCursor().charFormat().fontItalic();
}
bool QmlRichText::underline() const
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return false;
return textCursor().charFormat().fontUnderline();
}
void QmlRichText::setBold(bool arg)
{
QTextCharFormat fmt;
fmt.setFontWeight(arg ? QFont::Bold : QFont::Normal);
mergeFormatOnWordOrSelection(fmt);
emit boldChanged();
}
void QmlRichText::setItalic(bool arg)
{
QTextCharFormat fmt;
fmt.setFontItalic(arg);
mergeFormatOnWordOrSelection(fmt);
emit italicChanged();
}
void QmlRichText::setUnderline(bool arg)
{
QTextCharFormat fmt;
fmt.setFontUnderline(arg);
mergeFormatOnWordOrSelection(fmt);
emit underlineChanged();
}
int QmlRichText::fontSize() const
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return 0;
QTextCharFormat format = cursor.charFormat();
return format.font().pointSize();
}
void QmlRichText::setFontSize(int arg)
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return;
QTextCharFormat format;
format.setFontPointSize(arg);
mergeFormatOnWordOrSelection(format);
emit fontSizeChanged();
}
QColor QmlRichText::textColor() const
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return QColor(Qt::black);
QTextCharFormat format = cursor.charFormat();
return format.foreground().color();
}
void QmlRichText::setTextColor(const QColor &c)
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return;
QTextCharFormat format;
format.setForeground(QBrush(c));
mergeFormatOnWordOrSelection(format);
emit textColorChanged();
}
QString QmlRichText::fontFamily() const
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return QString();
QTextCharFormat format = cursor.charFormat();
return format.font().family();
}
void QmlRichText::setFontFamily(const QString &arg)
{
QTextCursor cursor = textCursor();
if (cursor.isNull())
return;
QTextCharFormat format;
format.setFontFamilies({arg});
mergeFormatOnWordOrSelection(format);
emit fontFamilyChanged();
}

139
src/qmltypes/qmlrichtext.h Normal file
View File

@@ -0,0 +1,139 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (c) 2020-2023 Meltytech, LLC
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
****************************************************************************/
#ifndef QMLRICHTEXT_H
#define QMLRICHTEXT_H
#include <QQuickTextDocument>
#include <QtGui/QTextCharFormat>
#include <qqmlfile.h>
QT_BEGIN_NAMESPACE
class QTextDocument;
QT_END_NAMESPACE
class QmlRichText : public QObject
{
Q_OBJECT
Q_ENUMS(HAlignment)
Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged)
Q_PROPERTY(
int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
Q_PROPERTY(
int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor NOTIFY textColorChanged)
Q_PROPERTY(QString fontFamily READ fontFamily WRITE setFontFamily NOTIFY fontFamilyChanged)
Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged)
Q_PROPERTY(bool bold READ bold WRITE setBold NOTIFY boldChanged)
Q_PROPERTY(bool italic READ italic WRITE setItalic NOTIFY italicChanged)
Q_PROPERTY(bool underline READ underline WRITE setUnderline NOTIFY underlineChanged)
Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged)
Q_PROPERTY(QUrl fileUrl READ fileUrl WRITE setFileUrl NOTIFY fileUrlChanged)
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(QSizeF size READ size NOTIFY sizeChanged)
public:
QmlRichText();
QQuickItem *target() { return m_target; }
void setTarget(QQuickItem *target);
void setCursorPosition(int position);
void setSelectionStart(int position);
void setSelectionEnd(int position);
int cursorPosition() const { return m_cursorPosition; }
int selectionStart() const { return m_selectionStart; }
int selectionEnd() const { return m_selectionEnd; }
QString fontFamily() const;
QColor textColor() const;
Qt::Alignment alignment() const;
void setAlignment(Qt::Alignment a);
bool bold() const;
bool italic() const;
bool underline() const;
int fontSize() const;
QUrl fileUrl() const;
QString text() const;
QSizeF size() const { return m_doc->size(); }
public slots:
void setBold(bool arg);
void setItalic(bool arg);
void setUnderline(bool arg);
void setFontSize(int arg);
void setTextColor(const QColor &arg);
void setFontFamily(const QString &arg);
void setFileUrl(const QUrl &arg);
void setText(const QString &arg);
void saveAs(const QUrl &arg, QString fileType = QString());
void insertTable(int rows = 1, int columns = 2, int border = 0);
void indentLess();
void indentMore();
void pastePlain();
void reset();
signals:
void targetChanged();
void cursorPositionChanged();
void selectionStartChanged();
void selectionEndChanged();
void fontFamilyChanged();
void textColorChanged();
void alignmentChanged();
void boldChanged();
void italicChanged();
void underlineChanged();
void fontSizeChanged();
void fileUrlChanged();
void textChanged();
void error(QString message);
void sizeChanged();
private:
QTextCursor textCursor() const;
void mergeFormatOnWordOrSelection(const QTextCharFormat &format);
QQuickItem *m_target;
QTextDocument *m_doc;
int m_cursorPosition;
int m_selectionStart;
int m_selectionEnd;
QFont m_font;
int m_fontSize;
QUrl m_fileUrl;
QString m_text;
QString m_documentTitle;
};
#endif // QMLRICHTEXT_H

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 2022 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmlrichtextmenu.h"
#include <QMenu>
QmlRichTextMenu::QmlRichTextMenu(QObject *parent)
: QObject(parent)
{}
void QmlRichTextMenu::popup()
{
QMenu menu;
QMenu *fileMenu = menu.addMenu(tr("File"));
QAction openAction(tr("Open..."));
connect(&openAction, &QAction::triggered, this, &QmlRichTextMenu::openTriggered);
fileMenu->addAction(&openAction);
QAction saveAsAction(tr("Save As..."));
connect(&saveAsAction, &QAction::triggered, this, &QmlRichTextMenu::saveAsTriggered);
fileMenu->addAction(&saveAsAction);
QMenu *editMenu = menu.addMenu(tr("Edit"));
QAction undoAction(tr("Undo"));
undoAction.setShortcut(QKeySequence::Undo);
connect(&undoAction, &QAction::triggered, this, &QmlRichTextMenu::undoTriggered);
editMenu->addAction(&undoAction);
QAction redoAction(tr("Redo"));
redoAction.setShortcut(QKeySequence::Redo);
connect(&redoAction, &QAction::triggered, this, &QmlRichTextMenu::redoTriggered);
editMenu->addAction(&redoAction);
editMenu->addSeparator();
QAction cutAction(tr("Cut"));
cutAction.setShortcut(QKeySequence::Cut);
connect(&cutAction, &QAction::triggered, this, &QmlRichTextMenu::cutTriggered);
editMenu->addAction(&cutAction);
QAction copyAction(tr("Copy"));
copyAction.setShortcut(QKeySequence::Copy);
connect(&copyAction, &QAction::triggered, this, &QmlRichTextMenu::copyTriggered);
editMenu->addAction(&copyAction);
QAction pasteAction(tr("Paste"));
pasteAction.setShortcut(QKeySequence::Paste);
connect(&pasteAction, &QAction::triggered, this, &QmlRichTextMenu::pasteTriggered);
editMenu->addAction(&pasteAction);
QAction pastePlainAction(tr("Paste Text Only"));
pastePlainAction.setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_V));
connect(&pastePlainAction, &QAction::triggered, this, &QmlRichTextMenu::pastePlainTriggered);
editMenu->addAction(&pastePlainAction);
QAction selectAllAction(tr("Select All"));
selectAllAction.setShortcut(QKeySequence::SelectAll);
connect(&selectAllAction, &QAction::triggered, this, &QmlRichTextMenu::selectAllTriggered);
menu.addAction(&selectAllAction);
QAction tableAction(tr("Insert Table"));
connect(&tableAction, &QAction::triggered, this, &QmlRichTextMenu::insertTableTriggered);
menu.addAction(&tableAction);
menu.exec(QCursor::pos());
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2022 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QMLRICHTEXTMENU_H
#define QMLRICHTEXTMENU_H
#include <QObject>
class QmlRichTextMenu : public QObject
{
Q_OBJECT
public:
explicit QmlRichTextMenu(QObject *parent = 0);
signals:
void openTriggered();
void saveAsTriggered();
void undoTriggered();
void redoTriggered();
void cutTriggered();
void copyTriggered();
void pasteTriggered();
void pastePlainTriggered();
void selectAllTriggered();
void insertTableTriggered();
public slots:
void popup();
};
#endif // QMLRICHTEXTMENU_H

View File

@@ -0,0 +1,120 @@
/*
* Copyright (c) 2013-2023 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmltypes/qmlutilities.h"
#include "models/keyframesmodel.h"
#include "models/metadatamodel.h"
#include "models/subtitlesmodel.h"
#include "models/subtitlesselectionmodel.h"
#include "qmltypes/colordialog.h"
#include "qmltypes/colorpickeritem.h"
#include "qmltypes/colorwheelitem.h"
#include "qmltypes/filedialog.h"
#include "qmltypes/fontdialog.h"
#include "qmltypes/messagedialog.h"
#include "qmltypes/qmlapplication.h"
#include "qmltypes/qmleditmenu.h"
#include "qmltypes/qmlextension.h"
#include "qmltypes/qmlfile.h"
#include "qmltypes/qmlfilter.h"
#include "qmltypes/qmlmarkermenu.h"
#include "qmltypes/qmlmetadata.h"
#include "qmltypes/qmlprofile.h"
#include "qmltypes/qmlrichtext.h"
#include "qmltypes/qmlrichtextmenu.h"
#include "qmltypes/timelineitems.h"
#include "settings.h"
#include <QCoreApplication>
#include <QCursor>
#include <QQmlContext>
#include <QQmlEngine>
#include <QSysInfo>
#include <QtQml>
QmlUtilities::QmlUtilities(QObject *parent)
: QObject(parent)
{}
void QmlUtilities::registerCommonTypes()
{
qmlRegisterType<QmlExtension>("org.shotcut.qml", 1, 0, "Extension");
qmlRegisterType<QmlExtensionFile>("org.shotcut.qml", 1, 0, "ExtensionFile");
qmlRegisterType<QmlFile>("org.shotcut.qml", 1, 0, "File");
qmlRegisterType<QmlFilter>("org.shotcut.qml", 1, 0, "Filter");
qmlRegisterType<QmlMetadata>("org.shotcut.qml", 1, 0, "Metadata");
qmlRegisterAnonymousType<QmlKeyframesMetadata>("org.shotcut.qml", 1);
qmlRegisterType<QmlKeyframesParameter>("org.shotcut.qml", 1, 0, "Parameter");
qmlRegisterType<QmlRichText>("org.shotcut.qml", 1, 0, "RichText");
qmlRegisterType<KeyframesModel>("org.shotcut.qml", 1, 0, "KeyframesModel");
qmlRegisterType<SubtitlesModel>("org.shotcut.qml", 1, 0, "SubtitlesModel");
qmlRegisterType<SubtitlesSelectionModel>("org.shotcut.qml", 1, 0, "SubtitlesSelectionModel");
qmlRegisterType<QmlUtilities>("org.shotcut.qml", 1, 0, "Utilities");
// MetadataModel is registered to access its MetadataFilter enum.
qmlRegisterUncreatableType<MetadataModel>("org.shotcut.qml",
1,
0,
"MetadataModel",
"You cannot create a MetadataModel from QML.");
qmlRegisterUncreatableType<ShotcutSettings>("org.shotcut.qml",
1,
0,
"Settings",
"You cannot create a Settings from QML.");
qmlRegisterType<ColorPickerItem>("Shotcut.Controls", 1, 0, "ColorPickerItem");
qmlRegisterType<ColorWheelItem>("Shotcut.Controls", 1, 0, "ColorWheelItem");
qmlRegisterType<QmlMarkerMenu>("Shotcut.Controls", 1, 0, "MarkerMenu");
qmlRegisterType<QmlEditMenu>("Shotcut.Controls", 1, 0, "EditContextMenu");
qmlRegisterType<QmlRichTextMenu>("Shotcut.Controls", 1, 0, "RichTextMenu");
qmlRegisterType<ColorDialog>("Shotcut.Controls", 1, 0, "ColorDialog");
qmlRegisterType<FontDialog>("Shotcut.Controls", 1, 0, "FontDialog");
qmlRegisterType<MessageDialog>("Shotcut.Controls", 1, 0, "MessageDialog");
qmlRegisterType<FileDialog>("Shotcut.Controls", 1, 0, "FileDialog");
registerTimelineItems();
}
void QmlUtilities::setCommonProperties(QQmlContext *context)
{
context->setContextProperty("settings", &ShotcutSettings::singleton());
context->setContextProperty("application", &QmlApplication::singleton());
context->setContextProperty("profile", &QmlProfile::singleton());
}
QDir QmlUtilities::qmlDir()
{
QDir dir = QmlApplication::dataDir();
dir.cd("shotcut");
dir.cd("qml");
return dir;
}
QQmlEngine *QmlUtilities::sharedEngine()
{
static QQmlEngine *s_engine = 0;
if (!s_engine)
s_engine = new QQmlEngine;
return s_engine;
}
QUrl QmlUtilities::blankVui()
{
QDir dir = qmlDir();
dir.cd("modules");
dir.cd("Shotcut");
dir.cd("Controls");
return QUrl::fromLocalFile(dir.absoluteFilePath("VuiBase.qml"));
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2013-2014 Meltytech, LLC
* Author: Dan Dennedy <dan@dennedy.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QMLUTILITIES_H
#define QMLUTILITIES_H
#include <QDir>
#include <QObject>
#include <QPoint>
#include <QUrl>
class QQmlContext;
class QQmlEngine;
class QmlUtilities : public QObject
{
Q_OBJECT
public:
explicit QmlUtilities(QObject *parent = 0);
static void registerCommonTypes();
static void setCommonProperties(QQmlContext *context);
static QDir qmlDir();
static QUrl blankVui();
static QQmlEngine *sharedEngine();
};
#endif // QMLUTILITIES_H

32
src/qmltypes/qmlview.cpp Normal file
View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2014-2016 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qmlview.h"
#include "Logger.h"
#include <QWidget>
QmlView::QmlView(QWidget *qview)
: QObject(qview)
, m_qview(qview)
{}
QPoint QmlView::pos()
{
return m_qview->mapToGlobal(QPoint(0, 0));
}

38
src/qmltypes/qmlview.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2014-2021 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QMLVIEW_H
#define QMLVIEW_H
#include <QObject>
#include <QPoint>
class QWidget;
class QmlView : public QObject
{
Q_OBJECT
public:
explicit QmlView(QWidget *qview);
Q_INVOKABLE QPoint pos();
private:
QWidget *m_qview;
};
#endif // QMLVIEW_H

View File

@@ -0,0 +1,133 @@
/*
* Copyright (c) 2013-2023 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "thumbnailprovider.h"
#include "Logger.h"
#include "database.h"
#include "mltcontroller.h"
#include "models/playlistmodel.h"
#include "settings.h"
#include "util.h"
#include <QCryptographicHash>
#include <QQuickImageProvider>
ThumbnailProvider::ThumbnailProvider()
: QQuickImageProvider(QQmlImageProviderBase::Image,
QQmlImageProviderBase::ForceAsynchronousImageLoading)
, m_profile("atsc_720p_60")
{}
QImage ThumbnailProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
QImage result;
// id is [hash]/mlt_service/resource#frameNumber[!]
// optional trailing '!' means to force update
int index = id.lastIndexOf('#');
if (index != -1) {
QString myId = id;
bool force = id.endsWith('!');
if (force)
myId = id.left(id.size() - 1);
QString hash = myId.section('/', 0, 0);
QString service = myId.section('/', 1, 1);
QString resource = myId.section('/', 2);
int frameNumber = myId.mid(index + 1).toInt();
Mlt::Properties properties;
// Scale the frameNumber to ThumbnailProvider profile's fps.
frameNumber = qRound(frameNumber / MLT.profile().fps() * m_profile.fps());
resource = resource.left(resource.lastIndexOf('#'));
resource = Util::removeQueryString(resource);
properties.set("_profile", m_profile.get_profile(), 0);
QString key = cacheKey(properties, service, resource, hash, frameNumber);
result = DB.getThumbnail(key);
if (force || result.isNull()) {
if (service == "avformat-novalidate")
service = "avformat";
else if (service.startsWith("xml"))
service = "xml-nogl";
Mlt::Producer producer;
if (service == "count") {
producer = Mlt::Producer(m_profile, service.toUtf8().constData(), "loader-nogl");
} else if (!Settings.playerGPU() || (service != "xml-nogl" && service != "consumer")) {
producer = Mlt::Producer(m_profile,
service.toUtf8().constData(),
resource.toUtf8().constData());
}
if (producer.is_valid()) {
result = makeThumbnail(producer, frameNumber, requestedSize);
DB.putThumbnail(key, result);
}
}
}
if (result.isNull()) {
result = QImage(1, 1, QImage::Format_Alpha8);
result.fill(0);
}
if (size)
*size = result.size();
return result;
}
QString ThumbnailProvider::cacheKey(Mlt::Properties &properties,
const QString &service,
const QString &resource,
const QString &hash,
int frameNumber)
{
QString time = properties.frames_to_time(frameNumber, mlt_time_clock);
// Reduce the precision to centiseconds to increase chance for cache hit
// without much loss of accuracy.
time = time.left(time.size() - 1);
QString key;
if (hash.isEmpty()) {
key = QStringLiteral("%1 %2 %3").arg(service).arg(resource).arg(time);
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(key.toUtf8());
key = hash.result().toHex();
} else {
key = QStringLiteral("%1 %2").arg(hash).arg(time);
}
return key;
}
QImage ThumbnailProvider::makeThumbnail(Mlt::Producer &producer,
int frameNumber,
const QSize &requestedSize)
{
Mlt::Filter scaler(m_profile, "swscale");
Mlt::Filter padder(m_profile, "resize");
Mlt::Filter converter(m_profile, "avcolor_space");
int height = PlaylistModel::THUMBNAIL_HEIGHT * 2;
int width = PlaylistModel::THUMBNAIL_WIDTH * 2;
if (!requestedSize.isEmpty()) {
width = requestedSize.width();
height = requestedSize.height();
}
producer.attach(scaler);
producer.attach(padder);
producer.attach(converter);
return MLT.image(producer, frameNumber, width, height);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2013-2016 Meltytech, LLC
* Author: Dan Dennedy <dan@dennedy.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef THUMBNAILPROVIDER_H
#define THUMBNAILPROVIDER_H
#include <MltProducer.h>
#include <MltProfile.h>
#include <QQuickImageProvider>
class ThumbnailProvider : public QQuickImageProvider
{
public:
explicit ThumbnailProvider();
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
private:
QString cacheKey(Mlt::Properties &properties,
const QString &service,
const QString &resource,
const QString &hash,
int frameNumber);
QImage makeThumbnail(Mlt::Producer &, int frameNumber, const QSize &requestedSize);
Mlt::Profile m_profile;
};
#endif // THUMBNAILPROVIDER_H

View File

@@ -0,0 +1,228 @@
/*
* Copyright (c) 2015-2020 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "timelineitems.h"
#include "Logger.h"
#include "mltcontroller.h"
#include "settings.h"
#include <QLinearGradient>
#include <QPainter>
#include <QPainterPath>
#include <QPalette>
#include <QQuickPaintedItem>
class TimelineTransition : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QColor colorA MEMBER m_colorA NOTIFY propertyChanged)
Q_PROPERTY(QColor colorB MEMBER m_colorB NOTIFY propertyChanged)
public:
TimelineTransition()
{
setAntialiasing(true);
connect(this, SIGNAL(propertyChanged()), this, SLOT(update()));
}
void paint(QPainter *painter)
{
QLinearGradient gradient(0, 0, 0, height());
gradient.setColorAt(0, m_colorA);
gradient.setColorAt(1, m_colorB);
QPainterPath path;
path.moveTo(0, 0);
path.lineTo(width(), height());
path.lineTo(width(), 0);
path.lineTo(0, height());
painter->fillPath(path, gradient);
painter->strokePath(path, painter->pen());
}
signals:
void propertyChanged();
private:
QColor m_colorA;
QColor m_colorB;
};
class TimelinePlayhead : public QQuickPaintedItem
{
void paint(QPainter *painter)
{
QPainterPath path;
path.moveTo(width(), 0);
path.lineTo(width() / 2.0, height());
path.lineTo(0, 0);
QPalette p;
painter->fillPath(path, p.color(QPalette::WindowText));
}
};
class TimelineTriangle : public QQuickPaintedItem
{
public:
TimelineTriangle() { setAntialiasing(true); }
void paint(QPainter *painter)
{
QPainterPath path;
path.moveTo(0, 0);
path.lineTo(width(), 0);
path.lineTo(0, height());
painter->fillPath(path, Qt::black);
}
};
class TimelineWaveform : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QVariant levels MEMBER m_audioLevels NOTIFY propertyChanged)
Q_PROPERTY(QColor fillColor MEMBER m_color NOTIFY propertyChanged)
Q_PROPERTY(int inPoint MEMBER m_inPoint NOTIFY inPointChanged)
Q_PROPERTY(int outPoint MEMBER m_outPoint NOTIFY outPointChanged)
Q_PROPERTY(bool active MEMBER m_isActive NOTIFY propertyChanged)
public:
TimelineWaveform()
{
setAntialiasing(false);
setOpaquePainting(true);
if (Settings.timelineFramebufferWaveform())
setRenderTarget(QQuickPaintedItem::FramebufferObject);
connect(this, SIGNAL(propertyChanged()), this, SLOT(update()));
}
void paint(QPainter *painter)
{
if (!m_isActive)
return;
QVariantList data = m_audioLevels.toList();
if (data.isEmpty())
return;
// In and out points are # frames at current fps,
// but audio levels are created at 25 fps.
// Scale in and out point to 25 fps.
const int inPoint = qRound(m_inPoint / MLT.profile().fps() * 25.0);
const int outPoint = qRound(m_outPoint / MLT.profile().fps() * 25.0);
const qreal indicesPrPixel = qreal(outPoint - inPoint) / width();
// LOG_DEBUG() << "In/out points" << inPoint << "/" << outPoint;
QPainterPath path;
path.moveTo(-1, height());
int i = 0;
for (; i < width(); ++i) {
int idx = inPoint + int(i * indicesPrPixel);
if ((idx < 0) || (idx + 2 >= data.length()))
break;
qreal level = qMax(data.at(idx).toReal(), data.at(idx + 1).toReal()) / 256;
path.lineTo(i, height() - level * height());
}
path.lineTo(i, height());
painter->fillPath(path, m_color.lighter());
QPen pen(painter->pen());
pen.setColor(m_color.darker());
painter->strokePath(path, pen);
}
signals:
void propertyChanged();
void inPointChanged();
void outPointChanged();
private:
QVariant m_audioLevels;
int m_inPoint;
int m_outPoint;
QColor m_color;
bool m_isActive{true};
};
class MarkerStart : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QColor fillColor MEMBER m_color NOTIFY propertyChanged)
public:
MarkerStart()
{
setAntialiasing(true);
connect(this, SIGNAL(propertyChanged()), this, SLOT(update()));
}
void paint(QPainter *painter)
{
QPainterPath path;
path.moveTo(0, 0);
path.lineTo(0, 10);
path.lineTo(7, 17);
path.lineTo(7, 0);
path.lineTo(0, 0);
painter->fillPath(path, m_color);
}
signals:
void propertyChanged();
private:
QColor m_color;
};
class MarkerEnd : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QColor fillColor MEMBER m_color NOTIFY propertyChanged)
public:
MarkerEnd()
{
setAntialiasing(true);
connect(this, SIGNAL(propertyChanged()), this, SLOT(update()));
}
void paint(QPainter *painter)
{
QPainterPath path;
path.moveTo(0, 17);
path.lineTo(7, 10);
path.lineTo(7, 0);
path.lineTo(0, 0);
painter->fillPath(path, m_color);
}
signals:
void propertyChanged();
private:
QColor m_color;
};
void registerTimelineItems()
{
qmlRegisterType<TimelineTransition>("Shotcut.Controls", 1, 0, "TimelineTransition");
qmlRegisterType<TimelinePlayhead>("Shotcut.Controls", 1, 0, "TimelinePlayhead");
qmlRegisterType<TimelineTriangle>("Shotcut.Controls", 1, 0, "TimelineTriangle");
qmlRegisterType<TimelineWaveform>("Shotcut.Controls", 1, 0, "TimelineWaveform");
qmlRegisterType<MarkerStart>("Shotcut.Controls", 1, 0, "MarkerStart");
qmlRegisterType<MarkerEnd>("Shotcut.Controls", 1, 0, "MarkerEnd");
}
#include "timelineitems.moc"

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2015-2016 Meltytech, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _TIMELINEITEMS_H
#define _TIMELINEITEMS_H
void registerTimelineItems();
#endif