Krita Source Code Documentation
Loading...
Searching...
No Matches
KisOpenPane.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2005 Peter Simonsson <psn@linux.se>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "KisOpenPane.h"
8
9#include <QLayout>
10#include <QImage>
11#include <QPainter>
12#include <QPen>
13#include <QPixmap>
14#include <QSize>
15#include <QString>
16#include <QTreeWidget>
17#include <QTreeWidgetItem>
18#include <QStyledItemDelegate>
19#include <QLinearGradient>
20#include <QStandardPaths>
21#include <QDragEnterEvent>
22#include <QDropEvent>
23#include <QMimeData>
24
25#include <klocalizedstring.h>
26#include <ksharedconfig.h>
27#include <kis_debug.h>
28#include <QUrl>
29
30
31#include <KisKineticScroller.h>
32#include <KoFileDialog.h>
33#include <KoIcon.h>
34#include "KisTemplateTree.h"
35#include "KisTemplateGroup.h"
36#include "KisTemplate.h"
37#include "KisDetailsPane.h"
38#include "KisTemplatesPane.h"
39#include "ui_KisOpenPaneBase.h"
40
41#include <limits.h>
42#include <kconfiggroup.h>
43
44#include <kis_icon.h>
45
46class KoSectionListItem : public QTreeWidgetItem
47{
48public:
49 KoSectionListItem(QTreeWidget* treeWidget, const QString& name, QString untranslatedName, int sortWeight, int widgetIndex = -1)
50 : QTreeWidgetItem(treeWidget, QStringList() << name)
54 {
55 Qt::ItemFlags newFlags = Qt::NoItemFlags;
56
57 if(m_widgetIndex >= 0)
58 newFlags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable;
59
60 setFlags(newFlags);
61 }
62
63 bool operator<(const QTreeWidgetItem & other) const override {
64 const KoSectionListItem* item = dynamic_cast<const KoSectionListItem*>(&other);
65
66 if (!item)
67 return 0;
68
69 return ((item->sortWeight() - sortWeight()) < 0);
70 }
71
72 int sortWeight() const {
73 return m_sortWeight;
74 }
75
76 int widgetIndex() const {
77 return m_widgetIndex;
78 }
79
80 QString untranslatedName() const {
81 return m_untranslatedName;
82 }
83
84private:
85 int m_sortWeight {0};
86 int m_widgetIndex {-1};
88};
89
90class KisOpenPanePrivate : public Ui_KisOpenPaneBase
91{
92public:
94 Ui_KisOpenPaneBase()
95 {
96 m_itemMap = new QMap<QString, QTreeWidgetItem *>();
97 }
98
100 {
101 delete m_itemMap;
102 }
103
106
107 //Stores pointers to items in the pane, accessible by their untranslated name
108 QMap<QString, QTreeWidgetItem *> *m_itemMap;
109};
110
111KisOpenPane::KisOpenPane(QWidget *parent, const QStringList& mimeFilter, const QString& templatesResourcePath)
112 : QDialog(parent)
113 , d(new KisOpenPanePrivate)
114{
115 d->setupUi(this);
116
117 m_mimeFilter = mimeFilter;
118
119 QStyledItemDelegate* delegate = new QStyledItemDelegate(d->m_sectionList);
120 d->m_sectionList->setItemDelegate(delegate);
121 QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(d->m_sectionList);
122 if (scroller) {
123 connect(scroller, &QScroller::stateChanged, this, [&](QScroller::State state) {
125 });
126 }
127
128 connect(d->m_sectionList, SIGNAL(itemSelectionChanged()),
129 this, SLOT(updateSelectedWidget()));
130 connect(d->m_sectionList, SIGNAL(itemClicked(QTreeWidgetItem*,int)),
131 this, SLOT(itemClicked(QTreeWidgetItem*)));
132 connect(d->m_sectionList, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
133 this, SLOT(itemClicked(QTreeWidgetItem*)));
134
135 initTemplates(templatesResourcePath);
136
138
139 if (!d->m_sectionList->selectedItems().isEmpty())
140 {
141 KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(d->m_sectionList->selectedItems().first());
142
143 if (selectedItem) {
144 d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
145 }
146 }
147
148 QList<int> sizes;
149
150 // Set the sizes of the details pane splitters
151 KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); sizes = cfgGrp.readEntry("DetailsPaneSplitterSizes", sizes);
152
153 if (!sizes.isEmpty())
154 Q_EMIT splitterResized(0, sizes);
155
156 connect(this, SIGNAL(splitterResized(KisDetailsPane*,QList<int>)),
158
159 setAcceptDrops(true);
160}
161
163{
164 if (!d->m_sectionList->selectedItems().isEmpty()) {
165 KoSectionListItem* item = dynamic_cast<KoSectionListItem*>(d->m_sectionList->selectedItems().first());
166
167 if (item) {
168 if (!qobject_cast<KisDetailsPane*>(d->m_widgetStack->widget(item->widgetIndex()))) {
169 KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog");
170 cfgGrp.writeEntry("LastReturnType", item->untranslatedName());
171 }
172 }
173 }
174
175 delete d;
176}
177
178
179
181{
182
183 KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocument");
184 dialog.setCaption(i18n("Open Existing Document"));
185 dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
186 dialog.setMimeTypeFilters(m_mimeFilter);
187 Q_FOREACH (const QString &filename, dialog.filenames()) {
188 Q_EMIT openExistingFile(QUrl::fromUserInput(filename));
189 }
190}
191
192void KisOpenPane::slotOpenTemplate(const QUrl &url)
193{
194 Q_EMIT openTemplate(url);
195 accept();
196}
197
198void KisOpenPane::initTemplates(const QString& templatesResourcePath)
199{
200 QTreeWidgetItem* selectItem = 0;
201 QTreeWidgetItem* firstItem = 0;
202 const int templateOffset = 1000;
203
204 if (!templatesResourcePath.isEmpty()) {
205 KisTemplateTree templateTree(templatesResourcePath, true);
206
207 Q_FOREACH (KisTemplateGroup *group, templateTree.groups()) {
208 if (group->isHidden()) {
209 continue;
210 }
211
212 if (!d->m_templatesSeparator) {
213 d->m_templatesSeparator = new KoSectionListItem(d->m_sectionList, "", "", 999);
214 }
215
216 KisTemplatesPane* pane = new KisTemplatesPane(this, group->name(),
217 group, templateTree.defaultTemplate());
218 connect(pane, SIGNAL(openUrl(QUrl)), this, SLOT(slotOpenTemplate(QUrl)));
219 connect(pane, SIGNAL(alwaysUseChanged(KisTemplatesPane*,QString)),
220 this, SIGNAL(alwaysUseChanged(KisTemplatesPane*,QString)));
221 connect(this, SIGNAL(alwaysUseChanged(KisTemplatesPane*,QString)),
222 pane, SLOT(changeAlwaysUseTemplate(KisTemplatesPane*,QString)));
223 connect(pane, SIGNAL(splitterResized(KisDetailsPane*,QList<int>)),
225 connect(this, SIGNAL(splitterResized(KisDetailsPane*,QList<int>)),
226 pane, SLOT(resizeSplitter(KisDetailsPane*,QList<int>)));
227 QTreeWidgetItem* item = addPane(group->name(), "Template", group->templates().first()->loadPicture(),
228 pane, group->sortingWeight() + templateOffset);
229
230
231
232 if (!firstItem) {
233 firstItem = item;
234 }
235
236 if (group == templateTree.defaultGroup()) {
237 firstItem = item;
238 }
239
240 if (pane->isSelected()) {
241 selectItem = item;
242 }
243 }
244 } else {
245 firstItem = d->m_sectionList->topLevelItem(0);
246 }
247
248 KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog");
249
250 if (selectItem && (cfgGrp.readEntry("LastReturnType") == "Template")) {
251 d->m_sectionList->setCurrentItem(selectItem, 0, QItemSelectionModel::ClearAndSelect);
252 }
253 else if (d->m_sectionList->selectedItems().isEmpty() && firstItem) {
254 d->m_sectionList->setCurrentItem(firstItem, 0, QItemSelectionModel::ClearAndSelect);
255 }
256}
257
258void KisOpenPane::dragEnterEvent(QDragEnterEvent *event)
259{
260 if (event->mimeData()->hasUrls()) {
261 event->accept();
262 }
263}
264
265void KisOpenPane::dropEvent(QDropEvent *event)
266{
267 if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) {
268 // XXX: when the MVC refactoring is done, this can open a bunch of
269 // urls, but since the part/document combination is still 1:1
270 // that won't work for now.
271 Q_EMIT openExistingFile(event->mimeData()->urls().first());
272
273 }
274}
275
276void KisOpenPane::addCustomDocumentWidget(QWidget *widget, const QString& title, const QString &untranslatedName, const QString& icon)
277{
278 Q_ASSERT(widget);
279
280 QTreeWidgetItem* item = addPane(title, untranslatedName, icon, widget, d->m_freeCustomWidgetIndex);
282 KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog");
283
284 QString lastActiveItem = cfgGrp.readEntry("LastReturnType");
285 bool showCustomItemByDefault = cfgGrp.readEntry("ShowCustomDocumentWidgetByDefault", false);
286 if (lastActiveItem == untranslatedName || (lastActiveItem.isEmpty() && showCustomItemByDefault)) {
287 d->m_sectionList->setCurrentItem(item, 0, QItemSelectionModel::ClearAndSelect);
288 KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(item);
289 d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
290 }
291}
292
293bool KisOpenPane::selectItem(const QString &name)
294{
295 QTreeWidgetItem* item = d->m_itemMap->value(name, nullptr);
296 if (!item) return false;
297
298 d->m_sectionList->setCurrentItem(item);
299 return true;
300}
301
302
303QTreeWidgetItem* KisOpenPane::addPane(const QString &title, const QString &untranslatedName, const QString &iconName, QWidget *widget, int sortWeight)
304{
305 if (!widget) {
306 return 0;
307 }
308
309 int id = d->m_widgetStack->addWidget(widget);
310 KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, untranslatedName, sortWeight, id);
311
312 d->m_itemMap->insert(untranslatedName, (QTreeWidgetItem*)listItem);
313
314 // resizes icons so they are a bit smaller
315 QIcon icon = KisIconUtils::loadIcon(iconName);
316 QPixmap iconPixmap = icon.pixmap(32, 32);
317
318 QIcon finalIcon(iconPixmap);
319 listItem->setIcon(0, finalIcon);
320
321 return listItem;
322}
323
324QTreeWidgetItem* KisOpenPane::addPane(const QString &title, const QString &untranslatedName, const QPixmap& icon, QWidget* widget, int sortWeight)
325{
326 if (!widget) {
327 return 0;
328 }
329
330 int id = d->m_widgetStack->addWidget(widget);
331
332 int iconSize = 32;
333
334 KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, untranslatedName, sortWeight, id);
335
336 d->m_itemMap->insert(untranslatedName, listItem);
337
338 if (!icon.isNull()) {
339 QImage image = icon.toImage();
340
341 if (!image.isNull() && ((image.width() > iconSize) || (image.height() > iconSize))) {
342 image = image.scaled(iconSize, iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
343 }
344
345 image.convertTo(QImage::Format_ARGB32);
346 image = image.copy((image.width() - iconSize) / 2, (image.height() - iconSize) / 2, iconSize, iconSize);
347 listItem->setIcon(0, QIcon(QPixmap::fromImage(image)));
348 }
349
350 return listItem;
351}
352
354{
355 if(!d->m_sectionList->selectedItems().isEmpty())
356 {
357 KoSectionListItem* section = dynamic_cast<KoSectionListItem*>(d->m_sectionList->selectedItems().first());
358
359 if (!section)
360 return;
361
362 d->m_widgetStack->setCurrentIndex(section->widgetIndex());
363 }
364}
365
367{
368 Q_UNUSED(sender);
369 KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog");
370 cfgGrp.writeEntry("DetailsPaneSplitterSizes", sizes);
371}
372
373void KisOpenPane::itemClicked(QTreeWidgetItem* item)
374{
375 KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(item);
376
377 if (selectedItem && selectedItem->widgetIndex() >= 0) {
378 d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
379 }
380}
int iconSize(qreal width, qreal height)
QMap< QString, QTreeWidgetItem * > * m_itemMap
KoSectionListItem * m_templatesSeparator
void dropEvent(QDropEvent *event) override
void splitterResized(KisDetailsPane *sender, const QList< int > &sizes)
Emitted when one of the detail panes have changed it's splitter.
~KisOpenPane() override
void initTemplates(const QString &templatesResourcePath)
void openFileDialog()
when clicked "Open Existing Document" button
void saveSplitterSizes(KisDetailsPane *sender, const QList< int > &sizes)
Saves the splitter sizes for KisDetailsPaneBase based panes.
QStringList m_mimeFilter
Definition KisOpenPane.h:95
void openTemplate(const QUrl &)
void updateSelectedWidget()
bool selectItem(const QString &name)
Selects an option out of the dialog based on its untranslated name.
void addCustomDocumentWidget(QWidget *widget, const QString &title, const QString &untranslatedName, const QString &icon=QString())
KisOpenPane(QWidget *parent, const QStringList &mimeFilter, const QString &templatesResourcePath=QString())
void slotOpenTemplate(const QUrl &url)
QTreeWidgetItem * addPane(const QString &title, const QString &untranslatedName, const QString &iconName, QWidget *widget, int sortWeight)
KisOpenPanePrivate *const d
Definition KisOpenPane.h:97
void openExistingFile(const QUrl &)
void itemClicked(QTreeWidgetItem *item)
void alwaysUseChanged(KisTemplatesPane *sender, const QString &alwaysUse)
Emitted when the always use template has changed.
void dragEnterEvent(QDragEnterEvent *event) override
QString name() const
int sortingWeight() const
QList< KisTemplate * > templates() const
bool isHidden() const
If all children are hidden, we are hidden too.
KisTemplateGroup * defaultGroup() const
QList< KisTemplateGroup * > groups() const
KisTemplate * defaultTemplate() const
bool isSelected()
Returns true if a template in this group was the last one selected.
int widgetIndex() const
int sortWeight() const
QString m_untranslatedName
bool operator<(const QTreeWidgetItem &other) const override
QString untranslatedName() const
KoSectionListItem(QTreeWidget *treeWidget, const QString &name, QString untranslatedName, int sortWeight, int widgetIndex=-1)
QIcon loadIcon(const QString &name)
KRITAWIDGETUTILS_EXPORT void updateCursor(QWidget *source, QScroller::State state)
KRITAWIDGETUTILS_EXPORT QScroller * createPreconfiguredScroller(QAbstractScrollArea *target)