profilesdialog.cpp 15.3 KB
Newer Older
1
/***************************************************************************
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
2
 *   Copyright (C) 2016 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

20
#include "profilesdialog.h"
Nicolas Carion's avatar
Nicolas Carion committed
21
#include "core.h"
22
#include "kdenlivesettings.h"
23
#include "profiles/profilemodel.hpp"
24
#include "profiles/profilerepository.hpp"
25

Nicolas Carion's avatar
Nicolas Carion committed
26
#include "klocalizedstring.h"
27
#include <KMessageBox>
28
29
#include <KMessageWidget>

Nicolas Carion's avatar
Nicolas Carion committed
30
#include "kdenlive_debug.h"
31
#include <QCloseEvent>
Nicolas Carion's avatar
Nicolas Carion committed
32
#include <QDir>
33
#include <QStandardPaths>
34

35
36
ProfilesDialog::ProfilesDialog(const QString &profileDescription, QWidget *parent)
    : QDialog(parent)
Nicolas Carion's avatar
Nicolas Carion committed
37

38
{
39

Nicolas Carion's avatar
Nicolas Carion committed
40
    // ask profile repository for a refresh
41
42
    ProfileRepository::get()->refresh();

43
44
    m_view.setupUi(this);

45
    // Add message widget
Nicolas Carion's avatar
Nicolas Carion committed
46
    auto *lay = (QGridLayout *)layout();
47
48
49
50
51
    m_infoMessage = new KMessageWidget;
    lay->addWidget(m_infoMessage, 2, 0, 1, -1);
    m_infoMessage->setCloseButtonVisible(true);
    m_infoMessage->hide();

52
    // Fill colorspace list (see mlt_profile.h)
53
54
55
56
    m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(601), 601);
    m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(709), 709);
    m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(240), 240);
    m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(0), 0);
57

58
    QStringList profilesFilter;
59
    profilesFilter << QStringLiteral("*");
60

61
    m_view.button_delete->setIcon(QIcon::fromTheme(QStringLiteral("trash-empty")));
62
    m_view.button_delete->setToolTip(i18n("Delete profile"));
63
    m_view.button_save->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
64
    m_view.button_save->setToolTip(i18n("Save profile"));
65
    m_view.button_create->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
66
    m_view.button_create->setToolTip(i18n("Create new profile"));
67

68
    fillList(profileDescription);
69
    slotUpdateDisplay();
70
71
72
73
74
    connectDialog();
}

void ProfilesDialog::connectDialog()
{
75
    connect(m_view.profiles_list, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [&](int ix) { slotUpdateDisplay(m_view.profiles_list->itemData(ix).toString()); });
Laurent Montel's avatar
Laurent Montel committed
76
77
78
79
    connect(m_view.button_create, &QAbstractButton::clicked, this, &ProfilesDialog::slotCreateProfile);
    connect(m_view.button_save, &QAbstractButton::clicked, this, &ProfilesDialog::slotSaveProfile);
    connect(m_view.button_delete, &QAbstractButton::clicked, this, &ProfilesDialog::slotDeleteProfile);
    connect(m_view.button_default, &QAbstractButton::clicked, this, &ProfilesDialog::slotSetDefaultProfile);
80

Laurent Montel's avatar
Laurent Montel committed
81
    connect(m_view.description, &QLineEdit::textChanged, this, &ProfilesDialog::slotProfileEdited);
82
83
84
85
86
87
    connect(m_view.frame_num, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited);
    connect(m_view.frame_den, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited);
    connect(m_view.aspect_num, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited);
    connect(m_view.aspect_den, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited);
    connect(m_view.display_num, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited);
    connect(m_view.display_den, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited);
Laurent Montel's avatar
Laurent Montel committed
88
    connect(m_view.progressive, &QCheckBox::stateChanged, this, &ProfilesDialog::slotProfileEdited);
89
    connect(m_view.size_h, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited);
90
91
    connect(m_view.size_h, &QAbstractSpinBox::editingFinished, this, &ProfilesDialog::slotAdjustHeight);
    m_view.size_h->setSingleStep(2);
92
    connect(m_view.size_w, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited);
93
94
    connect(m_view.size_w, &QAbstractSpinBox::editingFinished, this, &ProfilesDialog::slotAdjustWidth);
    m_view.size_w->setSingleStep(8);
95
96
}

Nicolas Carion's avatar
Nicolas Carion committed
97
ProfilesDialog::ProfilesDialog(const QString &profilePath, bool, QWidget *parent)
98
99
100
101
    : QDialog(parent)
    , m_profileIsModified(false)
    , m_isCustomProfile(true)
    , m_customProfilePath(profilePath)
102
    , m_profilesChanged(false)
103
104
105
{
    m_view.setupUi(this);

106
    // Add message widget
Nicolas Carion's avatar
Nicolas Carion committed
107
    auto *lay = (QGridLayout *)layout();
108
109
110
111
112
    m_infoMessage = new KMessageWidget;
    lay->addWidget(m_infoMessage, 2, 0, 1, -1);
    m_infoMessage->setCloseButtonVisible(true);
    m_infoMessage->hide();

113
    // Fill colorspace list (see mlt_profile.h)
114
115
116
117
    m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(601), 601);
    m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(709), 709);
    m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(240), 240);
    m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(0), 0);
118
119

    QStringList profilesFilter;
120
    profilesFilter << QStringLiteral("*");
121

122
    m_view.button_save->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
123
124
125
126
127
128
129
130
    m_view.button_save->setToolTip(i18n("Save profile"));
    m_view.button_create->setHidden(true);
    m_view.profiles_list->setHidden(true);
    m_view.button_delete->setHidden(true);
    m_view.button_default->setHidden(true);
    m_view.description->setEnabled(false);

    slotUpdateDisplay(profilePath);
131
    connectDialog();
132
133
134
135
136
}

void ProfilesDialog::slotAdjustWidth()
{
    // A profile's width should always be a multiple of 8
137
    QSignalBlocker blk(m_view.size_w);
138
139
140
141
142
143
144
145
146
147
148
149
    int val = m_view.size_w->value();
    int correctedWidth = (val + 7) / 8 * 8;
    if (val == correctedWidth) {
        // Ok, no action required, width is a multiple of 8
        m_infoMessage->animatedHide();
    } else {
        m_view.size_w->setValue(correctedWidth);
        m_infoMessage->setText(i18n("Profile width must be a multiple of 8. It was adjusted to %1", correctedWidth));
        m_infoMessage->setWordWrap(m_infoMessage->text().length() > 35);
        m_infoMessage->setMessageType(KMessageWidget::Warning);
        m_infoMessage->animatedShow();
    }
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
}

void ProfilesDialog::slotAdjustHeight()
{
    // A profile's height should always be a multiple of 2
    QSignalBlocker blk(m_view.size_h);
    int val = m_view.size_h->value();
    int correctedHeight = val + (val % 2);
    if (val == correctedHeight) {
        // Ok, no action required, width is a multiple of 8
        m_infoMessage->animatedHide();
    } else {
        m_view.size_h->setValue(correctedHeight);
        m_infoMessage->setText(i18n("Profile height must be a multiple of 2. It was adjusted to %1", correctedHeight));
        m_infoMessage->setWordWrap(m_infoMessage->text().length() > 35);
        m_infoMessage->setMessageType(KMessageWidget::Warning);
        m_infoMessage->animatedShow();
    }
168
169
}

170
171
void ProfilesDialog::slotProfileEdited()
{
172
    m_profileIsModified = true;
173
174
}

Laurent Montel's avatar
Laurent Montel committed
175
void ProfilesDialog::fillList(const QString &selectedProfile)
176
{
177
    m_view.profiles_list->clear();
178
    // Retrieve the list from the repository
Nicolas Carion's avatar
Nicolas Carion committed
179
    QVector<QPair<QString, QString>> profiles = ProfileRepository::get()->getAllProfiles();
Vincent Pinon's avatar
Vincent Pinon committed
180
    for (const auto &p : qAsConst(profiles)) {
181
        m_view.profiles_list->addItem(p.first, p.second);
182
    }
183

184
    if (!KdenliveSettings::default_profile().isEmpty()) {
185
186
187
188
189
        int ix = m_view.profiles_list->findData(KdenliveSettings::default_profile());
        if (ix > -1) {
            m_view.profiles_list->setCurrentIndex(ix);
        } else {
            // Error, profile not found
190
            qCWarning(KDENLIVE_LOG) << "Project profile not found, disable  editing";
191
        }
192
    }
193
    int ix = m_view.profiles_list->findText(selectedProfile);
194
195
196
    if (ix != -1) {
        m_view.profiles_list->setCurrentIndex(ix);
    }
197
198
199
    m_selectedProfileIndex = m_view.profiles_list->currentIndex();
}

200
201
void ProfilesDialog::accept()
{
202
203
204
    if (askForSave()) {
        QDialog::accept();
    }
205
206
}

207
208
void ProfilesDialog::reject()
{
209
210
211
    if (askForSave()) {
        QDialog::reject();
    }
212
213
}

214
215
void ProfilesDialog::closeEvent(QCloseEvent *event)
{
216
    if (askForSave()) {
217
218
219
220
221
222
        event->accept();
    } else {
        event->ignore();
    }
}

223
224
bool ProfilesDialog::askForSave()
{
225
226
227
228
229
230
    if (!m_profileIsModified) {
        return true;
    }
    if (KMessageBox::questionYesNo(this, i18n("The custom profile was modified, do you want to save it?")) != KMessageBox::Yes) {
        return true;
    }
231
    return slotSaveProfile();
232
233
}

234
235
void ProfilesDialog::slotCreateProfile()
{
236
237
238
239
240
241
    m_view.button_delete->setEnabled(false);
    m_view.button_create->setEnabled(false);
    m_view.button_save->setEnabled(true);
    m_view.properties->setEnabled(true);
}

242
243
void ProfilesDialog::slotSetDefaultProfile()
{
244
245
246
247
248
249
250
    if (m_profileIsModified) {
        m_infoMessage->setText(i18n("Save your profile before setting it to default"));
        m_infoMessage->setWordWrap(m_infoMessage->text().length() > 35);
        m_infoMessage->setMessageType(KMessageWidget::Warning);
        m_infoMessage->animatedShow();
        return;
    }
251
252
    int ix = m_view.profiles_list->currentIndex();
    QString path = m_view.profiles_list->itemData(ix).toString();
253
254
255
    if (!path.isEmpty()) {
        KdenliveSettings::setDefault_profile(path);
    }
256
257
}

258
259
bool ProfilesDialog::slotSaveProfile()
{
260
261
    slotAdjustWidth();

262
263
264
265
    if (!m_customProfilePath.isEmpty()) {
        saveProfile(m_customProfilePath);
        return true;
    }
266
267
    const QString profileDesc = m_view.description->text();
    int ix = m_view.profiles_list->findText(profileDesc);
268
    if (ix != -1) {
269
270
        // this profile name already exists
        const QString path = m_view.profiles_list->itemData(ix).toString();
Laurent Montel's avatar
Laurent Montel committed
271
        if (!path.contains(QLatin1Char('/'))) {
Nicolas Carion's avatar
Nicolas Carion committed
272
273
            KMessageBox::sorry(
                this, i18n("A profile with same name already exists in MLT's default profiles, please choose another description for your custom profile."));
274
            return false;
275
276
277
278
        }
        saveProfile(path);
    } else {
        int i = 0;
279
        QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/"));
280
281
282
        if (!dir.exists()) {
            dir.mkpath(QStringLiteral("."));
        }
283
        QString customName = QStringLiteral("customprofile");
Nicolas Carion's avatar
Nicolas Carion committed
284
        QString profilePath = dir.absoluteFilePath(customName + QString::number(i));
285
        while (QFile::exists(profilePath)) {
286
            ++i;
287
            profilePath = dir.absoluteFilePath(customName + QString::number(i));
288
289
290
        }
        saveProfile(profilePath);
    }
291
    m_profileIsModified = false;
292
    fillList(profileDesc);
293
    m_view.button_create->setEnabled(true);
294
    m_profilesChanged = true;
295
    return true;
296
297
}

Laurent Montel's avatar
Laurent Montel committed
298
void ProfilesDialog::saveProfile(const QString &path)
299
{
Nicolas Carion's avatar
Nicolas Carion committed
300
301
302
303
304
305
306
307
308
309
310
311
312
    std::unique_ptr<ProfileParam> profile(new ProfileParam(pCore->getCurrentProfile().get()));
    profile->m_description = m_view.description->text();
    profile->m_frame_rate_num = m_view.frame_num->value();
    profile->m_frame_rate_den = m_view.frame_den->value();
    profile->m_width = m_view.size_w->value();
    profile->m_height = m_view.size_h->value();
    profile->m_progressive = static_cast<int>(m_view.progressive->isChecked());
    profile->m_sample_aspect_num = m_view.aspect_num->value();
    profile->m_sample_aspect_den = m_view.aspect_den->value();
    profile->m_display_aspect_num = m_view.display_num->value();
    profile->m_display_aspect_den = m_view.display_den->value();
    profile->m_colorspace = m_view.colorspace->itemData(m_view.colorspace->currentIndex()).toInt();
    ProfileRepository::get()->saveProfile(profile.get(), path);
313
314
}

315
316
void ProfilesDialog::slotDeleteProfile()
{
317
    const QString path = m_view.profiles_list->itemData(m_view.profiles_list->currentIndex()).toString();
Nicolas Carion's avatar
Nicolas Carion committed
318
319
    bool success = ProfileRepository::get()->deleteProfile(path);
    if (success) {
320
        m_profilesChanged = true;
321
        fillList();
322
    }
323
324
}

325
void ProfilesDialog::slotUpdateDisplay(QString currentProfilePath)
326
{
327
    qDebug() << "/ / / /UPDATING DISPLAY FOR PROFILE: " << currentProfilePath;
Nicolas Carion's avatar
Nicolas Carion committed
328
    if (!askForSave()) {
329
330
331
332
333
        m_view.profiles_list->blockSignals(true);
        m_view.profiles_list->setCurrentIndex(m_selectedProfileIndex);
        m_view.profiles_list->blockSignals(false);
        return;
    }
334
    QLocale locale; // Used for UI output → OK
335
    locale.setNumberOptions(QLocale::OmitGroupSeparator);
336
    m_selectedProfileIndex = m_view.profiles_list->currentIndex();
337
338
    if (currentProfilePath.isEmpty()) {
        currentProfilePath = m_view.profiles_list->itemData(m_view.profiles_list->currentIndex()).toString();
339
    }
340
    m_isCustomProfile = currentProfilePath.contains(QLatin1Char('/'));
341
    m_view.button_create->setEnabled(true);
342
343
    m_view.button_delete->setEnabled(m_isCustomProfile);
    m_view.properties->setEnabled(m_isCustomProfile);
344
    m_view.button_save->setEnabled(m_isCustomProfile);
345
    std::unique_ptr<ProfileModel> &curProfile = ProfileRepository::get()->getProfile(currentProfilePath);
346
347
348
349
350
351
352
353
354
355
356
    m_view.description->setText(curProfile->description());
    m_view.size_w->setValue(curProfile->width());
    m_view.size_h->setValue(curProfile->height());
    m_view.aspect_num->setValue(curProfile->sample_aspect_num());
    m_view.aspect_den->setValue(curProfile->sample_aspect_den());
    m_view.display_num->setValue(curProfile->display_aspect_num());
    m_view.display_den->setValue(curProfile->display_aspect_den());
    m_view.frame_num->setValue(curProfile->frame_rate_num());
    m_view.frame_den->setValue(curProfile->frame_rate_den());
    m_view.progressive->setChecked(curProfile->progressive() != 0);
    if (curProfile->progressive() != 0) {
Nicolas Carion's avatar
Nicolas Carion committed
357
        m_view.fields->setText(locale.toString((double)curProfile->frame_rate_num() / curProfile->frame_rate_den(), 'f', 2));
358
    } else {
Nicolas Carion's avatar
Nicolas Carion committed
359
        m_view.fields->setText(locale.toString((double)2 * curProfile->frame_rate_num() / curProfile->frame_rate_den(), 'f', 2));
360
    }
361

362
    int colorix = m_view.colorspace->findData(curProfile->colorspace());
363
364
365
    if (colorix > -1) {
        m_view.colorspace->setCurrentIndex(colorix);
    }
366
    m_profileIsModified = false;
367
}
368
369
370
371
372

bool ProfilesDialog::profileTreeChanged() const
{
    return m_profilesChanged;
}