File indexing completed on 2024-04-14 04:47:31

0001 /*
0002 SPDX-FileCopyrightText: 2012 Jean-Baptiste Mardelle <jb@kdenlive.org>
0003 SPDX-FileCopyrightText: 2014 Till Theato <root@ttill.de>
0004 SPDX-FileCopyrightText: 2020 Julius Künzel <jk.kdedev@smartlab.uber.space>
0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "layoutmanagement.h"
0009 #include "core.h"
0010 #include "mainwindow.h"
0011 #include "utils/KMessageBox_KdenliveCompat.h"
0012 #include <KMessageBox>
0013 #include <QButtonGroup>
0014 #include <QDialog>
0015 #include <QDialogButtonBox>
0016 #include <QFileDialog>
0017 #include <QInputDialog>
0018 #include <QListWidget>
0019 #include <QMenu>
0020 #include <QMenuBar>
0021 #include <QVBoxLayout>
0022 
0023 #include "kwidgetsaddons_version.h"
0024 #include <KColorScheme>
0025 #include <KConfigGroup>
0026 #include <KLocalizedString>
0027 #include <KSharedConfig>
0028 #include <KToolBar>
0029 #include <KXMLGUIFactory>
0030 
0031 LayoutManagement::LayoutManagement(QObject *parent)
0032     : QObject(parent)
0033 {
0034     m_translatedNames = {{QStringLiteral("kdenlive_logging"), i18n("Logging")},
0035                          {QStringLiteral("kdenlive_editing"), i18n("Editing")},
0036                          {QStringLiteral("kdenlive_audio"), i18n("Audio")},
0037                          {QStringLiteral("kdenlive_effects"), i18n("Effects")},
0038                          {QStringLiteral("kdenlive_color"), i18n("Color")}};
0039 
0040     // Prepare layout actions
0041     KActionCategory *layoutActions = new KActionCategory(i18n("Layouts"), pCore->window()->actionCollection());
0042     m_loadLayout = new KSelectAction(i18n("Load Layout"), pCore->window()->actionCollection());
0043     pCore->window()->actionCollection()->setShortcutsConfigurable(m_loadLayout, false);
0044 
0045     // Required to enable user to add the load layout action to toolbar
0046     layoutActions->addAction(QStringLiteral("load_layouts"), m_loadLayout);
0047 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 240, 0)
0048     connect(m_loadLayout, &KSelectAction::actionTriggered, this, &LayoutManagement::slotLoadLayout);
0049 #else
0050     connect(m_loadLayout, static_cast<void (KSelectAction::*)(QAction *)>(&KSelectAction::triggered), this, &LayoutManagement::slotLoadLayout);
0051 #endif
0052 
0053     QAction *saveLayout = new QAction(i18n("Save Layout…"), pCore->window()->actionCollection());
0054     layoutActions->addAction(QStringLiteral("save_layout"), saveLayout);
0055     connect(saveLayout, &QAction::triggered, this, &LayoutManagement::slotSaveLayout);
0056 
0057     QAction *manageLayout = new QAction(i18n("Manage Layouts…"), pCore->window()->actionCollection());
0058     layoutActions->addAction(QStringLiteral("manage_layout"), manageLayout);
0059     connect(manageLayout, &QAction::triggered, this, &LayoutManagement::slotManageLayouts);
0060     // Create 9 layout actions
0061     for (int i = 1; i < 10; i++) {
0062         QAction *load = new QAction(QIcon(), QString(), this);
0063         m_layoutActions << layoutActions->addAction("load_layout" + QString::number(i), load);
0064     }
0065 
0066     // Dock Area Orientation
0067     QAction *rowDockAreaAction = new QAction(QIcon::fromTheme(QStringLiteral("object-rows")), i18n("Arrange Dock Areas In Rows"), this);
0068     layoutActions->addAction(QStringLiteral("horizontal_dockareaorientation"), rowDockAreaAction);
0069     connect(rowDockAreaAction, &QAction::triggered, this, &LayoutManagement::slotDockAreaRows);
0070 
0071     QAction *colDockAreaAction = new QAction(QIcon::fromTheme(QStringLiteral("object-columns")), i18n("Arrange Dock Areas In Columns"), this);
0072     layoutActions->addAction(QStringLiteral("vertical_dockareaorientation"), colDockAreaAction);
0073     connect(colDockAreaAction, &QAction::triggered, this, &LayoutManagement::slotDockAreaColumns);
0074 
0075     // Create layout switcher for the menu bar
0076     MainWindow *main = pCore->window();
0077     m_container = new QWidget(main);
0078     m_containerGrp = new QButtonGroup(m_container);
0079     connect(m_containerGrp, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), this, &LayoutManagement::activateLayout);
0080     auto *l1 = new QVBoxLayout;
0081     l1->addStretch();
0082     m_containerLayout = new QHBoxLayout;
0083     m_containerLayout->setSpacing(0);
0084     m_containerLayout->setContentsMargins(0, 0, 0, 0);
0085     l1->addLayout(m_containerLayout);
0086     m_container->setLayout(l1);
0087     KColorScheme scheme(main->palette().currentColorGroup(), KColorScheme::Button);
0088     QColor bg = scheme.background(KColorScheme::AlternateBackground).color();
0089     QString style = QString("padding-left: %4; padding-right: %4;background-color: rgb(%1,%2,%3);")
0090                         .arg(bg.red())
0091                         .arg(bg.green())
0092                         .arg(bg.blue())
0093                         .arg(main->fontInfo().pixelSize() / 2);
0094     m_container->setStyleSheet(style);
0095     main->menuBar()->setCornerWidget(m_container, Qt::TopRightCorner);
0096     initializeLayouts();
0097 }
0098 
0099 void LayoutManagement::initializeLayouts()
0100 {
0101     if (m_loadLayout == nullptr) {
0102         return;
0103     }
0104     QString current;
0105     if (m_containerGrp->checkedButton()) {
0106         current = m_containerGrp->checkedButton()->text();
0107     }
0108     MainWindow *main = pCore->window();
0109     // Delete existing buttons
0110     while (auto item = m_containerLayout->takeAt(0)) {
0111         delete item->widget();
0112     }
0113 
0114     // Load default base layouts
0115     KConfig defaultConfig(QStringLiteral("kdenlivedefaultlayouts.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
0116     KConfigGroup defaultOrder(&defaultConfig, "Order");
0117     KConfigGroup defaultLayout(&defaultConfig, "Layouts");
0118     QStringList defaultLayouts;
0119 
0120     // Load User defined layouts
0121     KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("kdenlive-layoutsrc"), KConfig::NoCascade);
0122     KConfigGroup layoutGroup(config, "Layouts");
0123     KConfigGroup layoutOrder(config, "Order");
0124     // If we don't have any layout saved, check in main config file
0125     if (!layoutGroup.exists()) {
0126         config = KSharedConfig::openConfig();
0127         KConfigGroup layoutGroup2(config, "Layouts");
0128         if (layoutGroup2.exists()) {
0129             // Migrate to new config file
0130             layoutGroup2.copyTo(&layoutGroup);
0131         }
0132     }
0133     m_loadLayout->removeAllActions();
0134     QStringList entries;
0135     bool addedDefault = false;
0136     if (!layoutOrder.exists()) {
0137         // This is an old or newly created config file, import defaults
0138         defaultLayouts = defaultOrder.entryMap().values();
0139         entries = layoutGroup.keyList();
0140         addedDefault = true;
0141     } else {
0142         // User sorted list
0143         entries = layoutOrder.entryMap().values();
0144     }
0145 
0146     // Add default layouts to user config in they don't exist
0147     for (const QString &lay : qAsConst(defaultLayouts)) {
0148         if (!entries.contains(lay)) {
0149             entries.insert(defaultLayouts.indexOf(lay), lay);
0150             layoutGroup.writeEntry(lay, defaultLayout.readEntry(lay));
0151             addedDefault = true;
0152         }
0153     }
0154     if (addedDefault) {
0155         // Write updated order
0156         layoutOrder.deleteGroup();
0157         int j = 1;
0158         for (const QString &entry : qAsConst(entries)) {
0159             layoutOrder.writeEntry(QString::number(j), entry);
0160             j++;
0161         }
0162         config->reparseConfiguration();
0163     }
0164     for (int i = 1; i < 10; i++) {
0165         QString layoutName;
0166         if (i <= entries.count()) {
0167             layoutName = entries.at(i - 1);
0168         } else {
0169             break;
0170         }
0171         QAction *load = m_layoutActions.at(i - 1);
0172         if (layoutName.isEmpty()) {
0173             load->setText(QString());
0174             load->setIcon(QIcon());
0175         } else {
0176             load->setText(i18n("Layout %1: %2", i, translatedName(layoutName)));
0177             if (i < 6) {
0178                 auto *lab = new QPushButton(translatedName(layoutName), m_container);
0179                 lab->setProperty("layoutid", layoutName);
0180                 lab->setFocusPolicy(Qt::NoFocus);
0181                 lab->setCheckable(true);
0182                 lab->setFlat(true);
0183                 lab->setFont(main->menuBar()->font());
0184                 m_containerGrp->addButton(lab);
0185                 m_containerLayout->addWidget(lab);
0186                 if (!current.isEmpty() && current == layoutName) {
0187                     lab->setChecked(true);
0188                 }
0189             }
0190         }
0191 
0192         load->setData(layoutName);
0193         if (!layoutName.isEmpty()) {
0194             load->setEnabled(true);
0195             m_loadLayout->addAction(load);
0196         } else {
0197             load->setEnabled(false);
0198         }
0199     }
0200     // Required to trigger a refresh of the container buttons
0201     main->menuBar()->resize(main->menuBar()->sizeHint());
0202 }
0203 
0204 void LayoutManagement::activateLayout(QAbstractButton *button)
0205 {
0206     if (!button) {
0207         return;
0208     }
0209     loadLayout(button->property("layoutid").toString(), false);
0210 }
0211 
0212 void LayoutManagement::slotLoadLayout(QAction *action)
0213 {
0214     if (!action) {
0215         return;
0216     }
0217 
0218     QString layoutId = action->data().toString();
0219     if (layoutId.isEmpty()) {
0220         return;
0221     }
0222     loadLayout(layoutId, true);
0223 }
0224 
0225 bool LayoutManagement::loadLayout(const QString &layoutId, bool selectButton)
0226 {
0227     KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("kdenlive-layoutsrc"));
0228     KConfigGroup layouts(config, "Layouts");
0229     if (!layouts.hasKey(layoutId)) {
0230         // Error, layout not found
0231         return false;
0232     }
0233     Q_EMIT connectDocks(false);
0234     QByteArray state = QByteArray::fromBase64(layouts.readEntry(layoutId).toLatin1());
0235     bool timelineVisible = true;
0236     if (state.startsWith("NO-TL")) {
0237         timelineVisible = false;
0238         state.remove(0, 5);
0239     }
0240     pCore->window()->centralWidget()->setHidden(!timelineVisible);
0241     // restore state disables all toolbars, so remember state
0242     QList<KToolBar *> barsList = pCore->window()->toolBars();
0243     QMap<QString, bool> toolbarVisibility;
0244     for (auto &tb : barsList) {
0245         toolbarVisibility.insert(tb->objectName(), tb->isVisible());
0246     }
0247     pCore->window()->processRestoreState(state);
0248     // Restore toolbar status
0249     QMapIterator<QString, bool> i(toolbarVisibility);
0250     while (i.hasNext()) {
0251         i.next();
0252         KToolBar *tb = pCore->window()->toolBar(i.key());
0253         if (tb) {
0254             tb->setVisible(i.value());
0255         }
0256     }
0257     pCore->window()->tabifyBins();
0258     Q_EMIT connectDocks(true);
0259     if (selectButton) {
0260         // Activate layout button
0261         QList<QAbstractButton *> buttons = m_containerGrp->buttons();
0262         bool buttonFound = false;
0263         for (auto *button : qAsConst(buttons)) {
0264             if (button->property("layoutid").toString() == layoutId) {
0265                 QSignalBlocker bk(m_containerGrp);
0266                 button->setChecked(true);
0267                 buttonFound = true;
0268             }
0269         }
0270         if (!buttonFound && m_containerGrp->checkedButton()) {
0271             m_containerGrp->setExclusive(false);
0272             m_containerGrp->checkedButton()->setChecked(false);
0273             m_containerGrp->setExclusive(true);
0274         }
0275     }
0276     Q_EMIT updateTitleBars();
0277     return true;
0278 }
0279 
0280 std::pair<QString, QString> LayoutManagement::saveLayout(const QString &layout, const QString &suggestedName)
0281 {
0282 
0283     QString visibleName = translatedName(suggestedName);
0284 
0285     QString layoutName = QInputDialog::getText(pCore->window(), i18nc("@title:window", "Save Layout"), i18n("Layout name:"), QLineEdit::Normal, visibleName);
0286     if (layoutName.isEmpty()) {
0287         return {nullptr, nullptr};
0288     }
0289 
0290     QString saveName;
0291     if (m_translatedNames.contains(layoutName)) {
0292         saveName = m_translatedNames.key(layoutName);
0293     } else {
0294         saveName = layoutName;
0295     }
0296 
0297     KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("kdenlive-layoutsrc"));
0298     KConfigGroup layouts(config, "Layouts");
0299     KConfigGroup order(config, "Order");
0300 
0301     if (layouts.hasKey(saveName)) {
0302         // Layout already exists
0303         int res = KMessageBox::questionTwoActions(pCore->window(), i18n("The layout %1 already exists. Do you want to replace it?", layoutName), {},
0304                                                   KStandardGuiItem::overwrite(), KStandardGuiItem::cancel());
0305         if (res != KMessageBox::PrimaryAction) {
0306             return {nullptr, nullptr};
0307         }
0308     }
0309 
0310     layouts.writeEntry(saveName, layout);
0311     if (!order.entryMap().values().contains(saveName)) {
0312         int pos = order.keyList().constLast().toInt() + 1;
0313         order.writeEntry(QString::number(pos), saveName);
0314     }
0315     return {layoutName, saveName};
0316 }
0317 
0318 void LayoutManagement::slotSaveLayout()
0319 {
0320     QAbstractButton *button = m_containerGrp->checkedButton();
0321     QString saveName;
0322     if (button) {
0323         saveName = button->text();
0324     }
0325 
0326     QByteArray st = pCore->window()->saveState();
0327     if (!pCore->window()->timelineVisible()) {
0328         st.prepend("NO-TL");
0329     }
0330     std::pair<QString, QString> names = saveLayout(st.toBase64(), saveName);
0331 
0332     // Activate layout button
0333     if (names.first != nullptr) {
0334         QList<QAbstractButton *> buttons = m_containerGrp->buttons();
0335         for (auto *button : qAsConst(buttons)) {
0336             if (button->text() == names.first) {
0337                 QSignalBlocker bk(m_containerGrp);
0338                 button->setChecked(true);
0339             }
0340         }
0341     }
0342 }
0343 
0344 void LayoutManagement::slotManageLayouts()
0345 {
0346     KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("kdenlive-layoutsrc"));
0347     KConfigGroup layouts(config, "Layouts");
0348     KConfigGroup order(config, "Order");
0349     QStringList names = order.entryMap().values();
0350     QString current;
0351     if (m_containerGrp->checkedButton()) {
0352         current = m_containerGrp->checkedButton()->text();
0353     }
0354     QDialog d(pCore->window());
0355     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
0356     auto *l = new QVBoxLayout;
0357     d.setLayout(l);
0358     l->addWidget(new QLabel(i18n("Current Layouts"), &d));
0359     QListWidget list(&d);
0360     list.setAlternatingRowColors(true);
0361     l->addWidget(&list);
0362     // Delete button
0363     QToolButton tb(&d);
0364     tb.setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0365     tb.setAutoRaise(true);
0366     connect(&tb, &QToolButton::clicked, this, [&layouts, &list]() {
0367         if (list.currentItem()) {
0368             layouts.deleteEntry(list.currentItem()->data(Qt::UserRole).toString());
0369             delete list.currentItem();
0370         }
0371     });
0372     tb.setToolTip(i18n("Delete the layout."));
0373     auto *l2 = new QHBoxLayout;
0374     l->addLayout(l2);
0375     l2->addWidget(&tb);
0376     // Up button
0377     QToolButton tb2(&d);
0378     tb2.setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
0379     tb2.setAutoRaise(true);
0380     connect(&tb2, &QToolButton::clicked, this, [&list]() {
0381         if (list.currentItem() && list.currentRow() > 0) {
0382             int updatedRow = list.currentRow() - 1;
0383             QListWidgetItem *item = list.takeItem(list.currentRow());
0384             list.insertItem(updatedRow, item);
0385             list.setCurrentRow(updatedRow);
0386         }
0387     });
0388     l2->addWidget(&tb2);
0389 
0390     // Down button
0391     QToolButton tb3(&d);
0392     tb3.setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
0393     tb3.setAutoRaise(true);
0394     connect(&tb3, &QToolButton::clicked, this, [&list]() {
0395         if (list.currentItem() && list.currentRow() < list.count() - 1) {
0396             int updatedRow = list.currentRow() + 1;
0397             QListWidgetItem *item = list.takeItem(list.currentRow());
0398             list.insertItem(updatedRow, item);
0399             list.setCurrentRow(updatedRow);
0400         }
0401     });
0402     l2->addWidget(&tb3);
0403 
0404     // Reset button
0405     QToolButton tb4(&d);
0406     tb4.setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
0407     tb4.setAutoRaise(true);
0408     tb4.setToolTip(i18n("Reset"));
0409     connect(&tb4, &QToolButton::clicked, this, [this, &config, &list, &layouts, current]() {
0410         // Load default base layouts
0411         KConfig defaultConfig(QStringLiteral("kdenlivedefaultlayouts.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
0412         KConfigGroup defaultOrder(&defaultConfig, "Order");
0413         KConfigGroup defaultLayout(&defaultConfig, "Layouts");
0414         QStringList defaultLayoutNames = defaultOrder.entryMap().values();
0415         // Get list of current layouts
0416         QStringList currentNames;
0417         for (int i = 0; i < list.count(); i++) {
0418             currentNames << list.item(i)->data(Qt::UserRole).toString();
0419         }
0420         int pos = 0;
0421         // Reset selected layout if it is a default one
0422         if (list.currentItem()) {
0423             QString selectedName = list.currentItem()->data(Qt::UserRole).toString();
0424             if (defaultLayoutNames.contains(selectedName)) {
0425                 layouts.writeEntry(selectedName, defaultLayout.readEntry(selectedName));
0426                 if (!current.isEmpty() && selectedName == current) {
0427                     config->reparseConfiguration();
0428                     loadLayout(current, false);
0429                 }
0430             }
0431         }
0432 
0433         // Re-add missing default layouts
0434         for (const QString &name : qAsConst(defaultLayoutNames)) {
0435             if (!currentNames.contains(name) && m_translatedNames.contains(name)) {
0436                 // Insert default layout
0437                 QListWidgetItem *item = new QListWidgetItem(translatedName(name));
0438                 item->setData(Qt::UserRole, name);
0439                 item->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
0440                 list.insertItem(pos, item);
0441                 // Write layout data
0442                 layouts.writeEntry(name, defaultLayout.readEntry(name));
0443             }
0444             pos++;
0445         }
0446     });
0447     l2->addWidget(&tb4);
0448 
0449     // Import button
0450     QToolButton tb5(&d);
0451     tb5.setIcon(QIcon::fromTheme(QStringLiteral("document-import")));
0452     tb5.setAutoRaise(true);
0453     tb5.setToolTip(i18n("Import"));
0454     connect(&tb5, &QToolButton::clicked, this, [this, &d, &list]() {
0455         QScopedPointer<QFileDialog> fd(new QFileDialog(&d, i18nc("@title:window", "Load Layout")));
0456         fd->setMimeTypeFilters(QStringList() << QStringLiteral("application/kdenlivelayout"));
0457         fd->setFileMode(QFileDialog::ExistingFile);
0458         if (fd->exec() != QDialog::Accepted) {
0459             return;
0460         }
0461         QStringList selection = fd->selectedFiles();
0462         QString url;
0463         if (!selection.isEmpty()) {
0464             url = selection.first();
0465         }
0466         if (url.isEmpty()) {
0467             return;
0468         }
0469         QFile file(url);
0470         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
0471             KMessageBox::error(&d, i18n("Cannot open file %1", QUrl::fromLocalFile(url).fileName()));
0472             return;
0473         }
0474         QString state = QString::fromUtf8(file.readAll());
0475         file.close();
0476 
0477         QFileInfo fileInfo(url);
0478         QString suggestedName(fileInfo.baseName());
0479 
0480         std::pair<QString, QString> names = saveLayout(state, suggestedName);
0481 
0482         if (names.first != nullptr && names.second != nullptr && list.findItems(names.first, Qt::MatchFlag::MatchExactly).length() == 0) {
0483             auto *item = new QListWidgetItem(names.first, &list);
0484             item->setData(Qt::UserRole, names.second);
0485             item->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
0486         }
0487     });
0488     l2->addWidget(&tb5);
0489 
0490     // Export selected button
0491     QToolButton tb6(&d);
0492     tb6.setIcon(QIcon::fromTheme(QStringLiteral("document-export")));
0493     tb6.setAutoRaise(true);
0494     tb6.setToolTip(i18n("Export selected"));
0495     connect(&tb6, &QToolButton::clicked, this, [&d, &list]() {
0496         if (!list.currentItem()) {
0497             // Error, no layout selected
0498             KMessageBox::error(&d, i18n("No layout selected"));
0499             return;
0500         }
0501 
0502         QListWidgetItem *item = list.item(list.currentRow());
0503         QString layoutId = item->data(Qt::UserRole).toString();
0504 
0505         KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("kdenlive-layoutsrc"));
0506         KConfigGroup layouts(config, "Layouts");
0507         if (!layouts.hasKey(layoutId)) {
0508             // Error, layout not found
0509             KMessageBox::error(&d, i18n("Cannot find layout %1", layoutId));
0510             return;
0511         }
0512 
0513         QScopedPointer<QFileDialog> fd(new QFileDialog(&d, i18nc("@title:window", "Export Layout")));
0514         fd->setMimeTypeFilters(QStringList() << QStringLiteral("application/kdenlivelayout"));
0515         fd->selectFile(layoutId + ".kdenlivelayout");
0516         fd->setDefaultSuffix(QStringLiteral("kdenlivelayout"));
0517         fd->setFileMode(QFileDialog::AnyFile);
0518         fd->setAcceptMode(QFileDialog::AcceptSave);
0519         if (fd->exec() != QDialog::Accepted) {
0520             return;
0521         }
0522         QStringList selection = fd->selectedFiles();
0523         QString url;
0524         if (!selection.isEmpty()) {
0525             url = selection.first();
0526         }
0527         if (url.isEmpty()) {
0528             return;
0529         }
0530         QFile file(url);
0531         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
0532             KMessageBox::error(&d, i18n("Cannot open file %1", QUrl::fromLocalFile(url).fileName()));
0533             return;
0534         }
0535         file.write(layouts.readEntry(layoutId).toUtf8());
0536         file.close();
0537     });
0538     l2->addWidget(&tb6);
0539 
0540     connect(&list, &QListWidget::currentRowChanged, this, [&list, &tb2, &tb3](int row) {
0541         tb2.setEnabled(row > 0);
0542         tb3.setEnabled(row < list.count() - 1);
0543     });
0544 
0545     l2->addStretch();
0546 
0547     // Add layouts to list
0548     for (const QString &name : qAsConst(names)) {
0549         auto *item = new QListWidgetItem(translatedName(name), &list);
0550         item->setData(Qt::UserRole, name);
0551         item->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
0552     }
0553     int ix = 0;
0554     if (!current.isEmpty()) {
0555         QList<QListWidgetItem *> res = list.findItems(current, Qt::MatchExactly);
0556         if (!res.isEmpty()) {
0557             ix = list.row(res.first());
0558         }
0559     }
0560     list.setCurrentRow(ix);
0561     l->addWidget(buttonBox);
0562     d.connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
0563     d.connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
0564     if (d.exec() != QDialog::Accepted) {
0565         return;
0566     }
0567     // Reset order
0568     order.deleteGroup();
0569     // Update order and new names
0570     for (int i = 0; i < list.count(); i++) {
0571         QListWidgetItem *item = list.item(i);
0572         QString layoutId = item->data(Qt::UserRole).toString();
0573         if (m_translatedNames.contains(layoutId)) {
0574             // This is a default layout, no rename
0575             if (item->text() != translatedName(layoutId)) {
0576                 // A default layout was renamed
0577                 order.writeEntry(QString::number(i + 1), item->text());
0578                 layouts.writeEntry(item->text(), layouts.readEntry(layoutId));
0579                 layouts.deleteEntry(layoutId);
0580             } else {
0581                 order.writeEntry(QString::number(i + 1), layoutId);
0582             }
0583             continue;
0584         }
0585         order.writeEntry(QString::number(i + 1), layoutId);
0586         if (item->text() != layoutId && !item->text().isEmpty()) {
0587             layouts.writeEntry(item->text(), layouts.readEntry(layoutId));
0588             layouts.deleteEntry(layoutId);
0589         }
0590     }
0591     config->reparseConfiguration();
0592     initializeLayouts();
0593 }
0594 
0595 const QString LayoutManagement::translatedName(const QString &name)
0596 {
0597     return m_translatedNames.contains(name) ? m_translatedNames.constFind(name).value() : name;
0598 }
0599 
0600 void LayoutManagement::slotDockAreaRows()
0601 {
0602     // Use the corners for top and bottom DockWidgetArea
0603     pCore->window()->setCorner(Qt::TopRightCorner, Qt::TopDockWidgetArea);
0604     pCore->window()->setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
0605     pCore->window()->setCorner(Qt::TopLeftCorner, Qt::TopDockWidgetArea);
0606     pCore->window()->setCorner(Qt::BottomLeftCorner, Qt::BottomDockWidgetArea);
0607 }
0608 
0609 void LayoutManagement::slotDockAreaColumns()
0610 {
0611     // Use the corners for left and right DockWidgetArea
0612     pCore->window()->setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
0613     pCore->window()->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
0614     pCore->window()->setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
0615     pCore->window()->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
0616 }