File indexing completed on 2024-05-12 16:01:52

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2005 Peter Simonsson <psn@linux.se>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "KisOpenPane.h"
0008 
0009 #include <QLayout>
0010 #include <QLabel>
0011 #include <QImage>
0012 #include <QPainter>
0013 #include <QPen>
0014 #include <QPixmap>
0015 #include <QSize>
0016 #include <QString>
0017 #include <QTreeWidget>
0018 #include <QTreeWidgetItem>
0019 #include <QStyledItemDelegate>
0020 #include <QLinearGradient>
0021 #include <QStandardPaths>
0022 #include <QDragEnterEvent>
0023 #include <QDropEvent>
0024 #include <QMimeData>
0025 
0026 #include <klocalizedstring.h>
0027 #include <ksharedconfig.h>
0028 #include <kis_debug.h>
0029 #include <QUrl>
0030 
0031 
0032 #include <KoFileDialog.h>
0033 #include <KoIcon.h>
0034 #include "KisTemplateTree.h"
0035 #include "KisTemplateGroup.h"
0036 #include "KisTemplate.h"
0037 #include "KisDetailsPane.h"
0038 #include "KisTemplatesPane.h"
0039 #include "ui_KisOpenPaneBase.h"
0040 
0041 #include <limits.h>
0042 #include <kconfiggroup.h>
0043 
0044 #include <kis_icon.h>
0045 
0046 class KoSectionListItem : public QTreeWidgetItem
0047 {
0048 public:
0049     KoSectionListItem(QTreeWidget* treeWidget, const QString& name, QString untranslatedName, int sortWeight, int widgetIndex = -1)
0050         : QTreeWidgetItem(treeWidget, QStringList() << name)
0051         , m_sortWeight(sortWeight)
0052         , m_widgetIndex(widgetIndex)
0053         , m_untranslatedName(untranslatedName)
0054     {
0055         Qt::ItemFlags newFlags = Qt::NoItemFlags;
0056 
0057         if(m_widgetIndex >= 0)
0058             newFlags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable;
0059 
0060         setFlags(newFlags);
0061     }
0062 
0063     bool operator<(const QTreeWidgetItem & other) const override {
0064         const KoSectionListItem* item = dynamic_cast<const KoSectionListItem*>(&other);
0065 
0066         if (!item)
0067             return 0;
0068 
0069         return ((item->sortWeight() - sortWeight()) < 0);
0070     }
0071 
0072     int sortWeight() const {
0073         return m_sortWeight;
0074     }
0075 
0076     int widgetIndex() const {
0077         return m_widgetIndex;
0078     }
0079 
0080     QString untranslatedName() const {
0081         return m_untranslatedName;
0082     }
0083 
0084 private:
0085     int m_sortWeight {0};
0086     int m_widgetIndex {-1};
0087     QString m_untranslatedName;
0088 };
0089 
0090 class KisOpenPanePrivate : public Ui_KisOpenPaneBase
0091 {
0092 public:
0093     KisOpenPanePrivate() :
0094         Ui_KisOpenPaneBase()
0095     { }
0096 
0097     int m_freeCustomWidgetIndex {0};
0098     KoSectionListItem* m_templatesSeparator {nullptr};
0099 
0100 
0101 };
0102 
0103 KisOpenPane::KisOpenPane(QWidget *parent, const QStringList& mimeFilter, const QString& templatesResourcePath)
0104     : QDialog(parent)
0105     , d(new KisOpenPanePrivate)
0106 {
0107     d->setupUi(this);
0108 
0109     m_mimeFilter = mimeFilter;
0110 
0111     QStyledItemDelegate* delegate = new QStyledItemDelegate(d->m_sectionList);
0112     d->m_sectionList->setItemDelegate(delegate);
0113 
0114     connect(d->m_sectionList, SIGNAL(itemSelectionChanged()),
0115             this, SLOT(updateSelectedWidget()));
0116     connect(d->m_sectionList, SIGNAL(itemClicked(QTreeWidgetItem*,int)),
0117             this, SLOT(itemClicked(QTreeWidgetItem*)));
0118     connect(d->m_sectionList, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
0119             this, SLOT(itemClicked(QTreeWidgetItem*)));
0120 
0121     initTemplates(templatesResourcePath);
0122 
0123     d->m_freeCustomWidgetIndex = 4;
0124 
0125     if (!d->m_sectionList->selectedItems().isEmpty())
0126     {
0127         KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(d->m_sectionList->selectedItems().first());
0128 
0129         if (selectedItem) {
0130             d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
0131         }
0132     }
0133 
0134     QList<int> sizes;
0135 
0136     // Set the sizes of the details pane splitters
0137     KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); sizes = cfgGrp.readEntry("DetailsPaneSplitterSizes", sizes);
0138 
0139     if (!sizes.isEmpty())
0140         emit splitterResized(0, sizes);
0141 
0142     connect(this, SIGNAL(splitterResized(KisDetailsPane*,QList<int>)),
0143             this, SLOT(saveSplitterSizes(KisDetailsPane*,QList<int>)));
0144 
0145     setAcceptDrops(true);
0146 }
0147 
0148 KisOpenPane::~KisOpenPane()
0149 {
0150     if (!d->m_sectionList->selectedItems().isEmpty()) {
0151         KoSectionListItem* item = dynamic_cast<KoSectionListItem*>(d->m_sectionList->selectedItems().first());
0152 
0153         if (item) {
0154             if (!qobject_cast<KisDetailsPane*>(d->m_widgetStack->widget(item->widgetIndex()))) {
0155                 KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog");
0156                 cfgGrp.writeEntry("LastReturnType", item->untranslatedName());
0157             }
0158         }
0159     }
0160 
0161     delete d;
0162 }
0163 
0164 
0165 
0166 void KisOpenPane::openFileDialog()
0167 {
0168 
0169     KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocument");
0170     dialog.setCaption(i18n("Open Existing Document"));
0171     dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
0172     dialog.setMimeTypeFilters(m_mimeFilter);
0173     Q_FOREACH (const QString &filename, dialog.filenames()) {
0174         emit openExistingFile(QUrl::fromUserInput(filename));
0175     }
0176 }
0177 
0178 void KisOpenPane::slotOpenTemplate(const QUrl &url)
0179 {
0180     emit openTemplate(url);
0181     accept();
0182 }
0183 
0184 void KisOpenPane::initTemplates(const QString& templatesResourcePath)
0185 {
0186     QTreeWidgetItem* selectItem = 0;
0187     QTreeWidgetItem* firstItem = 0;
0188     const int templateOffset = 1000;
0189 
0190     if (!templatesResourcePath.isEmpty()) {
0191         KisTemplateTree templateTree(templatesResourcePath, true);
0192 
0193         Q_FOREACH (KisTemplateGroup *group, templateTree.groups()) {
0194             if (group->isHidden()) {
0195                 continue;
0196             }
0197 
0198             if (!d->m_templatesSeparator) {
0199                 d->m_templatesSeparator = new KoSectionListItem(d->m_sectionList, "", "", 999);
0200             }
0201 
0202             KisTemplatesPane* pane = new KisTemplatesPane(this, group->name(),
0203                                                           group, templateTree.defaultTemplate());
0204             connect(pane, SIGNAL(openUrl(QUrl)), this, SLOT(slotOpenTemplate(QUrl)));
0205             connect(pane, SIGNAL(alwaysUseChanged(KisTemplatesPane*,QString)),
0206                     this, SIGNAL(alwaysUseChanged(KisTemplatesPane*,QString)));
0207             connect(this, SIGNAL(alwaysUseChanged(KisTemplatesPane*,QString)),
0208                     pane, SLOT(changeAlwaysUseTemplate(KisTemplatesPane*,QString)));
0209             connect(pane, SIGNAL(splitterResized(KisDetailsPane*,QList<int>)),
0210                     this, SIGNAL(splitterResized(KisDetailsPane*,QList<int>)));
0211             connect(this, SIGNAL(splitterResized(KisDetailsPane*,QList<int>)),
0212                     pane, SLOT(resizeSplitter(KisDetailsPane*,QList<int>)));
0213             QTreeWidgetItem* item = addPane(group->name(), "Template", group->templates().first()->loadPicture(),
0214                                             pane, group->sortingWeight() + templateOffset);
0215 
0216 
0217 
0218             if (!firstItem) {
0219                 firstItem = item;
0220             }
0221 
0222             if (group == templateTree.defaultGroup()) {
0223                 firstItem = item;
0224             }
0225 
0226             if (pane->isSelected()) {
0227                 selectItem = item;
0228             }
0229         }
0230     } else {
0231         firstItem = d->m_sectionList->topLevelItem(0);
0232     }
0233 
0234     KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog");
0235 
0236     if (selectItem && (cfgGrp.readEntry("LastReturnType") == "Template")) {
0237         d->m_sectionList->setCurrentItem(selectItem, 0, QItemSelectionModel::ClearAndSelect);
0238     }
0239     else if (d->m_sectionList->selectedItems().isEmpty() && firstItem) {
0240         d->m_sectionList->setCurrentItem(firstItem, 0, QItemSelectionModel::ClearAndSelect);
0241     }
0242 }
0243 
0244 void KisOpenPane::dragEnterEvent(QDragEnterEvent *event)
0245 {
0246     if (event->mimeData()->hasUrls()) {
0247         event->accept();
0248     }
0249 }
0250 
0251 void KisOpenPane::dropEvent(QDropEvent *event)
0252 {
0253     if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) {
0254         // XXX: when the MVC refactoring is done, this can open a bunch of
0255         //      urls, but since the part/document combination is still 1:1
0256         //      that won't work for now.
0257         emit openExistingFile(event->mimeData()->urls().first());
0258 
0259     }
0260 }
0261 
0262 void KisOpenPane::addCustomDocumentWidget(QWidget *widget, const QString& title, const QString &untranslatedName, const QString& icon)
0263 {
0264     Q_ASSERT(widget);
0265 
0266     QTreeWidgetItem* item = addPane(title, untranslatedName, icon, widget, d->m_freeCustomWidgetIndex);
0267     ++d->m_freeCustomWidgetIndex;
0268     KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog");
0269 
0270     QString lastActiveItem = cfgGrp.readEntry("LastReturnType");
0271     bool showCustomItemByDefault = cfgGrp.readEntry("ShowCustomDocumentWidgetByDefault", false);
0272     if (lastActiveItem == untranslatedName || (lastActiveItem.isEmpty() && showCustomItemByDefault)) {
0273         d->m_sectionList->setCurrentItem(item, 0, QItemSelectionModel::ClearAndSelect);
0274         KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(item);
0275         d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
0276     }
0277 }
0278 
0279 
0280 QTreeWidgetItem* KisOpenPane::addPane(const QString &title, const QString &untranslatedName, const QString &iconName, QWidget *widget, int sortWeight)
0281 {
0282     if (!widget) {
0283         return 0;
0284     }
0285 
0286     int id = d->m_widgetStack->addWidget(widget);
0287     KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, untranslatedName, sortWeight, id);
0288 
0289     // resizes icons so they are a bit smaller
0290     QIcon icon = KisIconUtils::loadIcon(iconName);
0291     QPixmap iconPixmap = icon.pixmap(32, 32);
0292 
0293     QIcon finalIcon(iconPixmap);
0294     listItem->setIcon(0, finalIcon);
0295 
0296     return listItem;
0297 }
0298 
0299 QTreeWidgetItem* KisOpenPane::addPane(const QString &title, const QString &untranslatedName, const QPixmap& icon, QWidget* widget, int sortWeight)
0300 {
0301     if (!widget) {
0302         return 0;
0303     }
0304 
0305     int id = d->m_widgetStack->addWidget(widget);
0306 
0307     int iconSize = 32;
0308 
0309     KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, untranslatedName, sortWeight, id);
0310 
0311     if (!icon.isNull()) {
0312         QImage image = icon.toImage();
0313 
0314         if (!image.isNull() && ((image.width() > iconSize) || (image.height() > iconSize))) {
0315             image = image.scaled(iconSize, iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
0316         }
0317 
0318         image = image.convertToFormat(QImage::Format_ARGB32);
0319         image = image.copy((image.width() - iconSize) / 2, (image.height() - iconSize) / 2, iconSize, iconSize);
0320         listItem->setIcon(0, QIcon(QPixmap::fromImage(image)));
0321     }
0322 
0323     return listItem;
0324 }
0325 
0326 void KisOpenPane::updateSelectedWidget()
0327 {
0328     if(!d->m_sectionList->selectedItems().isEmpty())
0329     {
0330         KoSectionListItem* section = dynamic_cast<KoSectionListItem*>(d->m_sectionList->selectedItems().first());
0331 
0332         if (!section)
0333             return;
0334 
0335         d->m_widgetStack->setCurrentIndex(section->widgetIndex());
0336     }
0337 }
0338 
0339 void KisOpenPane::saveSplitterSizes(KisDetailsPane* sender, const QList<int>& sizes)
0340 {
0341     Q_UNUSED(sender);
0342     KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog");
0343     cfgGrp.writeEntry("DetailsPaneSplitterSizes", sizes);
0344 }
0345 
0346 void KisOpenPane::itemClicked(QTreeWidgetItem* item)
0347 {
0348     KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(item);
0349 
0350     if (selectedItem && selectedItem->widgetIndex() >= 0) {
0351         d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
0352     }
0353 }