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 }