übernahme Code Shortcut
This commit is contained in:
89
src/qmltypes/colordialog.cpp
Normal file
89
src/qmltypes/colordialog.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
62
src/qmltypes/colordialog.h
Normal file
62
src/qmltypes/colordialog.h
Normal 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
|
||||
152
src/qmltypes/colorpickeritem.cpp
Normal file
152
src/qmltypes/colorpickeritem.cpp
Normal 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
|
||||
50
src/qmltypes/colorpickeritem.h
Normal file
50
src/qmltypes/colorpickeritem.h
Normal 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
|
||||
381
src/qmltypes/colorwheelitem.cpp
Normal file
381
src/qmltypes/colorwheelitem.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
88
src/qmltypes/colorwheelitem.h
Normal file
88
src/qmltypes/colorwheelitem.h
Normal 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
|
||||
86
src/qmltypes/filedialog.cpp
Normal file
86
src/qmltypes/filedialog.cpp
Normal 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
60
src/qmltypes/filedialog.h
Normal 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
|
||||
47
src/qmltypes/fontdialog.cpp
Normal file
47
src/qmltypes/fontdialog.cpp
Normal 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
46
src/qmltypes/fontdialog.h
Normal 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
|
||||
81
src/qmltypes/messagedialog.cpp
Normal file
81
src/qmltypes/messagedialog.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
61
src/qmltypes/messagedialog.h
Normal file
61
src/qmltypes/messagedialog.h
Normal 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
|
||||
283
src/qmltypes/qmlapplication.cpp
Normal file
283
src/qmltypes/qmlapplication.cpp
Normal 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);
|
||||
}
|
||||
83
src/qmltypes/qmlapplication.h
Normal file
83
src/qmltypes/qmlapplication.h
Normal 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
|
||||
90
src/qmltypes/qmleditmenu.cpp
Normal file
90
src/qmltypes/qmleditmenu.cpp
Normal 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(©Action, &QAction::triggered, this, &QmlEditMenu::copyTriggered);
|
||||
menu.addAction(©Action);
|
||||
|
||||
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());
|
||||
}
|
||||
54
src/qmltypes/qmleditmenu.h
Normal file
54
src/qmltypes/qmleditmenu.h
Normal 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
|
||||
110
src/qmltypes/qmlextension.cpp
Normal file
110
src/qmltypes/qmlextension.cpp
Normal 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
100
src/qmltypes/qmlextension.h
Normal 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
137
src/qmltypes/qmlfile.cpp
Normal 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
58
src/qmltypes/qmlfile.h
Normal 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
1253
src/qmltypes/qmlfilter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
197
src/qmltypes/qmlfilter.h
Normal file
197
src/qmltypes/qmlfilter.h
Normal 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
|
||||
117
src/qmltypes/qmlmarkermenu.cpp
Normal file
117
src/qmltypes/qmlmarkermenu.cpp
Normal 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());
|
||||
}
|
||||
51
src/qmltypes/qmlmarkermenu.h
Normal file
51
src/qmltypes/qmlmarkermenu.h
Normal 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
|
||||
229
src/qmltypes/qmlmetadata.cpp
Normal file
229
src/qmltypes/qmlmetadata.cpp
Normal 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
246
src/qmltypes/qmlmetadata.h
Normal 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
|
||||
276
src/qmltypes/qmlproducer.cpp
Normal file
276
src/qmltypes/qmlproducer.cpp
Normal 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
103
src/qmltypes/qmlproducer.h
Normal 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
|
||||
55
src/qmltypes/qmlprofile.cpp
Normal file
55
src/qmltypes/qmlprofile.cpp
Normal 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
50
src/qmltypes/qmlprofile.h
Normal 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
|
||||
408
src/qmltypes/qmlrichtext.cpp
Normal file
408
src/qmltypes/qmlrichtext.cpp
Normal 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
139
src/qmltypes/qmlrichtext.h
Normal 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
|
||||
84
src/qmltypes/qmlrichtextmenu.cpp
Normal file
84
src/qmltypes/qmlrichtextmenu.cpp
Normal 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(©Action, &QAction::triggered, this, &QmlRichTextMenu::copyTriggered);
|
||||
editMenu->addAction(©Action);
|
||||
|
||||
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());
|
||||
}
|
||||
46
src/qmltypes/qmlrichtextmenu.h
Normal file
46
src/qmltypes/qmlrichtextmenu.h
Normal 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
|
||||
120
src/qmltypes/qmlutilities.cpp
Normal file
120
src/qmltypes/qmlutilities.cpp
Normal 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"));
|
||||
}
|
||||
44
src/qmltypes/qmlutilities.h
Normal file
44
src/qmltypes/qmlutilities.h
Normal 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
32
src/qmltypes/qmlview.cpp
Normal 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
38
src/qmltypes/qmlview.h
Normal 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
|
||||
133
src/qmltypes/thumbnailprovider.cpp
Normal file
133
src/qmltypes/thumbnailprovider.cpp
Normal 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);
|
||||
}
|
||||
42
src/qmltypes/thumbnailprovider.h
Normal file
42
src/qmltypes/thumbnailprovider.h
Normal 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
|
||||
228
src/qmltypes/timelineitems.cpp
Normal file
228
src/qmltypes/timelineitems.cpp
Normal 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"
|
||||
23
src/qmltypes/timelineitems.h
Normal file
23
src/qmltypes/timelineitems.h
Normal 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
|
||||
Reference in New Issue
Block a user