Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtresourceview.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant reason:default
4
8#include "iconloader_p.h"
9
10#include <QtDesigner/abstractformeditor.h>
11#include <QtDesigner/abstractsettings.h>
12
13#include <QtWidgets/qtoolbar.h>
14#include <QtWidgets/qsplitter.h>
15#include <QtWidgets/qtreewidget.h>
16#include <QtWidgets/qlistwidget.h>
17#include <QtWidgets/qheaderview.h>
18#include <QtWidgets/qboxlayout.h>
19#include <QtWidgets/qdialogbuttonbox.h>
20#include <QtWidgets/qpushbutton.h>
21#include <QtWidgets/qmessagebox.h>
22#include <QtWidgets/qapplication.h>
23#include <QtWidgets/qmenu.h>
24#include <QtWidgets/qlineedit.h>
25
26#include <QtGui/qaction.h>
27#if QT_CONFIG(clipboard)
28# include <QtGui/qclipboard.h>
29#endif
30#include <QtGui/qdrag.h>
31#include <QtGui/qpainter.h>
32
33#include <QtCore/qmimedata.h>
34#include <QtCore/qfileinfo.h>
35#include <QtCore/qdir.h>
36#include <QtCore/qhash.h>
37#include <QtCore/qqueue.h>
38
39#include <QtXml/qdom.h>
40
41#include <algorithm>
42
43QT_BEGIN_NAMESPACE
44
45using namespace Qt::StringLiterals;
46
47static constexpr auto elementResourceData = "resource"_L1;
48static constexpr auto typeAttribute = "type"_L1;
49static constexpr auto typeImage = "image"_L1;
50static constexpr auto typeStyleSheet = "stylesheet"_L1;
51static constexpr auto typeOther = "other"_L1;
52static constexpr auto fileAttribute = "file"_L1;
53static constexpr auto qrvSplitterPosition = "SplitterPosition"_L1;
54static constexpr auto qrvGeometry = "Geometry"_L1;
55static constexpr auto ResourceViewDialogC = "ResourceDialog"_L1;
56
57// ---------------- ResourceListWidget: A list widget that has drag enabled
59public:
60 ResourceListWidget(QWidget *parent = nullptr);
61
62protected:
63 void startDrag(Qt::DropActions supportedActions) override;
64};
65
68{
69 setDragEnabled(true);
70}
71
72void ResourceListWidget::startDrag(Qt::DropActions supportedActions)
73{
74 if (supportedActions == Qt::MoveAction)
75 return;
76
77 QListWidgetItem *item = currentItem();
78 if (!item)
79 return;
80
81 const QString filePath = item->data(Qt::UserRole).toString();
82 const QIcon icon = item->icon();
83
84 QMimeData *mimeData = new QMimeData;
85 const QtResourceView::ResourceType type = icon.isNull() ? QtResourceView::ResourceOther : QtResourceView::ResourceImage;
86 mimeData->setText(QtResourceView::encodeMimeData(type , filePath));
87
88 QDrag *drag = new QDrag(this);
89 if (!icon.isNull()) {
90 const QSize size = icon.actualSize(iconSize());
91 drag->setPixmap(icon.pixmap(size));
92 drag->setHotSpot(QPoint(size.width() / 2, size.height() / 2));
93 }
94
95 drag->setMimeData(mimeData);
96 drag->exec(Qt::CopyAction);
97}
98
99/* TODO
100
101 1) load the icons in separate thread...Hmm..if Qt is configured with threads....
102*/
103
104// ---------------------------- QtResourceViewPrivate
106{
107 QtResourceView *q_ptr = nullptr;
108 Q_DECLARE_PUBLIC(QtResourceView)
109public:
111
112 void slotResourceSetActivated(QtResourceSet *resourceSet);
113 void slotCurrentPathChanged(QTreeWidgetItem *);
114 void slotCurrentResourceChanged(QListWidgetItem *);
115 void slotResourceActivated(QListWidgetItem *);
118#if QT_CONFIG(clipboard)
120#endif
121 void slotListWidgetContextMenuRequested(const QPoint &pos);
122 void slotFilterChanged(const QString &pattern);
124 QTreeWidgetItem *createPath(const QString &path, QTreeWidgetItem *parent);
125 void createResources(const QString &path);
132
133 QPixmap makeThumbnail(const QPixmap &pix) const;
134
135 QDesignerFormEditorInterface *m_core;
136 QtResourceModel *m_resourceModel = nullptr;
142 QMap<QString, QStringList> m_pathToContents; // full path to contents file names (full path to its resource filenames)
143 QMap<QString, QString> m_pathToParentPath; // full path to full parent path
144 QMap<QString, QStringList> m_pathToSubPaths; // full path to full sub paths
152
154
157 bool m_ignoreGuiSignals = false;
159};
160
161QtResourceViewPrivate::QtResourceViewPrivate(QDesignerFormEditorInterface *core) :
162 m_core(core),
163 m_toolBar(new QToolBar),
164 m_treeWidget(new QTreeWidget),
165 m_listWidget(new ResourceListWidget)
166{
167 m_toolBar->setIconSize(QSize(22, 22));
168}
169
170void QtResourceViewPrivate::restoreSettings()
171{
172 if (m_settingsKey.isEmpty())
173 return;
174
175 QDesignerSettingsInterface *settings = m_core->settingsManager();
176 settings->beginGroup(m_settingsKey);
177
178 m_splitter->restoreState(settings->value(qrvSplitterPosition).toByteArray());
179 settings->endGroup();
180}
181
182void QtResourceViewPrivate::saveSettings()
183{
184 if (m_settingsKey.isEmpty())
185 return;
186
187 QDesignerSettingsInterface *settings = m_core->settingsManager();
188 settings->beginGroup(m_settingsKey);
189
190 settings->setValue(qrvSplitterPosition, m_splitter->saveState());
191 settings->endGroup();
192}
193
194void QtResourceViewPrivate::slotEditResources()
195{
196 const QString selectedResource
197 = QtResourceEditorDialog::editResources(m_core, m_resourceModel,
198 m_core->dialogGui(), q_ptr);
199 if (!selectedResource.isEmpty())
200 q_ptr->selectResource(selectedResource);
201}
202
203void QtResourceViewPrivate::slotReloadResources()
204{
205 if (m_resourceModel) {
206 int errorCount;
207 QString errorMessages;
208 m_resourceModel->reload(&errorCount, &errorMessages);
209 if (errorCount)
210 QtResourceEditorDialog::displayResourceFailures(errorMessages, m_core->dialogGui(), q_ptr);
211 }
212}
213
214#if QT_CONFIG(clipboard)
215void QtResourceViewPrivate::slotCopyResourcePath()
216{
217 const QString path = q_ptr->selectedResource();
218 QClipboard *clipboard = QApplication::clipboard();
219 clipboard->setText(path);
220}
221#endif
222
223void QtResourceViewPrivate::slotListWidgetContextMenuRequested(const QPoint &pos)
224{
225 QMenu menu(q_ptr);
226 menu.addAction(m_copyResourcePathAction);
227 menu.exec(m_listWidget->mapToGlobal(pos));
228}
229
230void QtResourceViewPrivate::slotFilterChanged(const QString &pattern)
231{
232 m_filterPattern = pattern;
233 filterOutResources();
234}
235
236void QtResourceViewPrivate::storeExpansionState()
237{
238 for (auto it = m_pathToItem.cbegin(), end = m_pathToItem.cend(); it != end; ++it)
239 m_expansionState.insert(it.key(), it.value()->isExpanded());
240}
241
242void QtResourceViewPrivate::applyExpansionState()
243{
244 for (auto it = m_pathToItem.cbegin(), end = m_pathToItem.cend(); it != end; ++it)
245 it.value()->setExpanded(m_expansionState.value(it.key(), true));
246}
247
248QPixmap QtResourceViewPrivate::makeThumbnail(const QPixmap &pix) const
249{
250 int w = qMax(48, pix.width());
251 int h = qMax(48, pix.height());
252 QRect imgRect(0, 0, w, h);
253 QImage img(w, h, QImage::Format_ARGB32_Premultiplied);
254 img.fill(0);
255 if (!pix.isNull()) {
256 QRect r(0, 0, pix.width(), pix.height());
257 r.moveCenter(imgRect.center());
258 QPainter p(&img);
259 p.drawPixmap(r.topLeft(), pix);
260 }
261 return QPixmap::fromImage(img);
262}
263
264void QtResourceViewPrivate::updateActions()
265{
266 bool resourceActive = false;
267 if (m_resourceModel)
268 resourceActive = m_resourceModel->currentResourceSet();
269
270 m_editResourcesAction->setVisible(m_resourceEditingEnabled);
271 m_editResourcesAction->setEnabled(resourceActive);
272 m_reloadResourcesAction->setEnabled(resourceActive);
273 m_filterWidget->setEnabled(resourceActive);
274}
275
276void QtResourceViewPrivate::slotResourceSetActivated(QtResourceSet *resourceSet)
277{
278 Q_UNUSED(resourceSet);
279
280 updateActions();
281
282 storeExpansionState();
283 const QString currentPath = m_itemToPath.value(m_treeWidget->currentItem());
284 const QString currentResource = m_itemToResource.value(m_listWidget->currentItem());
285 m_treeWidget->clear();
286 m_pathToContents.clear();
287 m_pathToParentPath.clear();
288 m_pathToSubPaths.clear();
289 m_pathToItem.clear();
290 m_itemToPath.clear();
291 m_listWidget->clear();
292 m_resourceToItem.clear();
293 m_itemToResource.clear();
294
295 createPaths();
296 applyExpansionState();
297
298 if (!currentResource.isEmpty())
299 q_ptr->selectResource(currentResource);
300 else if (!currentPath.isEmpty())
301 q_ptr->selectResource(currentPath);
302 filterOutResources();
303}
304
305void QtResourceViewPrivate::slotCurrentPathChanged(QTreeWidgetItem *item)
306{
307 if (m_ignoreGuiSignals)
308 return;
309
310 m_listWidget->clear();
311 m_resourceToItem.clear();
312 m_itemToResource.clear();
313
314 if (!item)
315 return;
316
317 const QString currentPath = m_itemToPath.value(item);
318 createResources(currentPath);
319}
320
321void QtResourceViewPrivate::slotCurrentResourceChanged(QListWidgetItem *item)
322{
323 m_copyResourcePathAction->setEnabled(item);
324 if (m_ignoreGuiSignals)
325 return;
326
327 emit q_ptr->resourceSelected(m_itemToResource.value(item));
328}
329
330void QtResourceViewPrivate::slotResourceActivated(QListWidgetItem *item)
331{
332 if (m_ignoreGuiSignals)
333 return;
334
335 emit q_ptr->resourceActivated(m_itemToResource.value(item));
336}
337
338void QtResourceViewPrivate::createPaths()
339{
340 if (!m_resourceModel)
341 return;
342
343 // Resource root up until 4.6 was ':', changed to ":/" as of 4.7
344 const QString root(u":/"_s);
345
346 QMap<QString, QString> contents = m_resourceModel->contents();
347 for (auto it = contents.cbegin(), end = contents.cend(); it != end; ++it) {
348 const QFileInfo fi(it.key());
349 QString dirPath = fi.absolutePath();
350 m_pathToContents[dirPath].append(fi.fileName());
351 while (!m_pathToParentPath.contains(dirPath) && dirPath != root) { // create all parent paths
352 const QFileInfo fd(dirPath);
353 const QString parentDirPath = fd.absolutePath();
354 m_pathToParentPath[dirPath] = parentDirPath;
355 m_pathToSubPaths[parentDirPath].append(dirPath);
356 dirPath = parentDirPath;
357 }
358 }
359
360 QQueue<std::pair<QString, QTreeWidgetItem *>> pathToParentItemQueue;
361 pathToParentItemQueue.enqueue(std::make_pair(root, static_cast<QTreeWidgetItem *>(nullptr)));
362 while (!pathToParentItemQueue.isEmpty()) {
363 std::pair<QString, QTreeWidgetItem *> pathToParentItem = pathToParentItemQueue.dequeue();
364 const QString path = pathToParentItem.first;
365 QTreeWidgetItem *item = createPath(path, pathToParentItem.second);
366 const QStringList subPaths = m_pathToSubPaths.value(path);
367 for (const QString &subPath : subPaths)
368 pathToParentItemQueue.enqueue(std::make_pair(subPath, item));
369 }
370}
371
372void QtResourceViewPrivate::filterOutResources()
373{
374 QMap<QString, bool> pathToMatchingContents; // true means the path has any matching contents
375 QMap<QString, bool> pathToVisible; // true means the path has to be shown
376
377 // 1) we go from root path recursively.
378 // 2) we check every path if it contains at least one matching resource - if empty we add it
379 // to pathToMatchingContents and pathToVisible with false, if non empty
380 // we add it with true and change every parent path in pathToVisible to true.
381 // 3) we hide these items which has pathToVisible value false.
382
383 const bool matchAll = m_filterPattern.isEmpty();
384 const QString root(u":/"_s);
385
386 QQueue<QString> pathQueue;
387 pathQueue.enqueue(root);
388 while (!pathQueue.isEmpty()) {
389 const QString path = pathQueue.dequeue();
390
391 bool hasContents = matchAll;
392 if (!matchAll) { // the case filter is not empty - we check if the path contains anything
393 // the path contains at least one resource which matches the filter
394 const QStringList fileNames = m_pathToContents.value(path);
395 hasContents =
396 std::any_of(fileNames.cbegin(), fileNames.cend(),
397 [this] (const QString &f) { return f.contains(this->m_filterPattern, Qt::CaseInsensitive); });
398 }
399
400 pathToMatchingContents[path] = hasContents;
401 pathToVisible[path] = hasContents;
402
403 if (hasContents) { // if the path is going to be shown we need to show all its parent paths
404 QString parentPath = m_pathToParentPath.value(path);
405 while (!parentPath.isEmpty()) {
406 QString p = parentPath;
407 if (pathToVisible.value(p)) // parent path is already shown, we break the loop
408 break;
409 pathToVisible[p] = true;
410 parentPath = m_pathToParentPath.value(p);
411 }
412 }
413
414 const QStringList subPaths = m_pathToSubPaths.value(path); // we do the same for children paths
415 for (const QString &subPath : subPaths)
416 pathQueue.enqueue(subPath);
417 }
418
419 // we setup here new path and resource to be activated
420 const QString currentPath = m_itemToPath.value(m_treeWidget->currentItem());
421 QString newCurrentPath = currentPath;
422 QString currentResource = m_itemToResource.value(m_listWidget->currentItem());
423 if (!matchAll) {
424 bool searchForNewPathWithContents = true;
425
426 if (!currentPath.isEmpty()) { // if the currentPath is empty we will search for a new path too
427 const auto it = pathToMatchingContents.constFind(currentPath);
428 if (it != pathToMatchingContents.constEnd() && it.value()) // the current item has contents, we don't need to search for another path
429 searchForNewPathWithContents = false;
430 }
431
432 if (searchForNewPathWithContents) {
433 // we find the first path with the matching contents
434 for (auto itContents = pathToMatchingContents.cbegin(), cend = pathToMatchingContents.cend(); itContents != cend; ++itContents) {
435 if (itContents.value()) {
436 newCurrentPath = itContents.key(); // the new path will be activated
437 break;
438 }
439 }
440 }
441
442 QFileInfo fi(currentResource);
443 if (!fi.fileName().contains(m_filterPattern, Qt::CaseInsensitive)) { // the case when the current resource is filtered out
444 const QStringList fileNames = m_pathToContents.value(newCurrentPath);
445 // we try to select the first matching resource from the newCurrentPath
446 for (const QString &fileName : fileNames) {
447 if (fileName.contains(m_filterPattern, Qt::CaseInsensitive)) {
448 QDir dirPath(newCurrentPath);
449 currentResource = dirPath.absoluteFilePath(fileName); // the new resource inside newCurrentPath will be activated
450 break;
451 }
452 }
453 }
454 }
455
456 QTreeWidgetItem *newCurrentItem = m_pathToItem.value(newCurrentPath);
457 if (currentPath != newCurrentPath)
458 m_treeWidget->setCurrentItem(newCurrentItem);
459 else
460 slotCurrentPathChanged(newCurrentItem); // trigger filtering on the current path
461
462 QListWidgetItem *currentResourceItem = m_resourceToItem.value(currentResource);
463 if (currentResourceItem) {
464 m_listWidget->setCurrentItem(currentResourceItem);
465 m_listWidget->scrollToItem(currentResourceItem);
466 }
467
468 // hide all paths filtered out
469 for (auto it = pathToVisible.cbegin(), end = pathToVisible.cend(); it != end; ++it) {
470 if (QTreeWidgetItem *item = m_pathToItem.value(it.key()))
471 item->setHidden(!it.value());
472 }
473}
474
475QTreeWidgetItem *QtResourceViewPrivate::createPath(const QString &path, QTreeWidgetItem *parent)
476{
477 QTreeWidgetItem *item = nullptr;
478 if (parent)
479 item = new QTreeWidgetItem(parent);
480 else
481 item = new QTreeWidgetItem(m_treeWidget);
482 m_pathToItem[path] = item;
483 m_itemToPath[item] = path;
484 QString substPath;
485 if (parent) {
486 QFileInfo di(path);
487 substPath = di.fileName();
488 } else {
489 substPath = u"<resource root>"_s;
490 }
491 item->setText(0, substPath);
492 item->setToolTip(0, path);
493 return item;
494}
495
496void QtResourceViewPrivate::createResources(const QString &path)
497{
498 const bool matchAll = m_filterPattern.isEmpty();
499
500 QDir dir(path);
501 const QStringList fileNames = m_pathToContents.value(path);
502 for (const QString &fileName : fileNames) {
503 const bool showProperty = matchAll || fileName.contains(m_filterPattern, Qt::CaseInsensitive);
504 if (showProperty) {
505 QString filePath = dir.absoluteFilePath(fileName);
506 QFileInfo fi(filePath);
507 if (fi.isFile()) {
508 QListWidgetItem *item = new QListWidgetItem(fi.fileName(), m_listWidget);
509 const QPixmap pix = QPixmap(filePath);
510 if (pix.isNull()) {
511 item->setToolTip(filePath);
512 } else {
513 item->setIcon(QIcon(makeThumbnail(pix)));
514 const QSize size = pix.size();
515 item->setToolTip(QtResourceView::tr("Size: %1 x %2\n%3").arg(size.width()).arg(size.height()).arg(filePath));
516 }
517 item->setFlags(item->flags() | Qt::ItemIsDragEnabled);
518 item->setData(Qt::UserRole, filePath);
519 m_itemToResource[item] = filePath;
520 m_resourceToItem[filePath] = item;
521 }
522 }
523 }
524}
525
526// -------------- QtResourceView
527
528QtResourceView::QtResourceView(QDesignerFormEditorInterface *core, QWidget *parent) :
529 QWidget(parent),
530 d_ptr(new QtResourceViewPrivate(core))
531{
532 d_ptr->q_ptr = this;
533
534 QIcon editIcon = qdesigner_internal::createIconSet(QIcon::ThemeIcon::DocumentProperties,
535 "edit.png"_L1);
536 d_ptr->m_editResourcesAction = new QAction(editIcon, tr("Edit Resources..."), this);
537 d_ptr->m_toolBar->addAction(d_ptr->m_editResourcesAction);
538 connect(d_ptr->m_editResourcesAction, &QAction::triggered,
539 this, [this] { d_ptr->slotEditResources(); });
540 d_ptr->m_editResourcesAction->setEnabled(false);
541
542 QIcon refreshIcon = qdesigner_internal::createIconSet(QIcon::ThemeIcon::ViewRefresh,
543 "reload.png"_L1);
544 d_ptr->m_reloadResourcesAction = new QAction(refreshIcon, tr("Reload"), this);
545
546 d_ptr->m_toolBar->addAction(d_ptr->m_reloadResourcesAction);
547 connect(d_ptr->m_reloadResourcesAction, &QAction::triggered,
548 this, [this] { d_ptr->slotReloadResources(); });
549 d_ptr->m_reloadResourcesAction->setEnabled(false);
550
551#if QT_CONFIG(clipboard)
552 QIcon copyIcon = qdesigner_internal::createIconSet(QIcon::ThemeIcon::EditCopy,
553 "editcopy.png"_L1);
554 d_ptr->m_copyResourcePathAction = new QAction(copyIcon, tr("Copy Path"), this);
555 connect(d_ptr->m_copyResourcePathAction, &QAction::triggered,
556 this, [this] { d_ptr->slotCopyResourcePath(); });
557 d_ptr->m_copyResourcePathAction->setEnabled(false);
558#endif
559
560 d_ptr->m_filterWidget = new QWidget(d_ptr->m_toolBar);
561 QHBoxLayout *filterLayout = new QHBoxLayout(d_ptr->m_filterWidget);
562 filterLayout->setContentsMargins(0, 0, 0, 0);
563 QLineEdit *filterLineEdit = new QLineEdit(d_ptr->m_filterWidget);
564 connect(filterLineEdit, &QLineEdit::textChanged,
565 this, [this](const QString &text) { d_ptr->slotFilterChanged(text); });
566 filterLineEdit->setPlaceholderText(tr("Filter"));
567 filterLineEdit->setClearButtonEnabled(true);
568 filterLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
569 filterLayout->addWidget(filterLineEdit);
570 d_ptr->m_toolBar->addWidget(d_ptr->m_filterWidget);
571
572 d_ptr->m_splitter = new QSplitter;
573 d_ptr->m_splitter->setChildrenCollapsible(false);
574 d_ptr->m_splitter->addWidget(d_ptr->m_treeWidget);
575 d_ptr->m_splitter->addWidget(d_ptr->m_listWidget);
576
577 QLayout *layout = new QVBoxLayout(this);
578 layout->setContentsMargins(QMargins());
579 layout->setSpacing(0);
580 layout->addWidget(d_ptr->m_toolBar);
581 layout->addWidget(d_ptr->m_splitter);
582
583 d_ptr->m_treeWidget->setColumnCount(1);
584 d_ptr->m_treeWidget->header()->hide();
585 d_ptr->m_treeWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
586
587 d_ptr->m_listWidget->setViewMode(QListView::IconMode);
588 d_ptr->m_listWidget->setResizeMode(QListView::Adjust);
589 d_ptr->m_listWidget->setIconSize(QSize(48, 48));
590 d_ptr->m_listWidget->setGridSize(QSize(64, 64));
591
592 connect(d_ptr->m_treeWidget, &QTreeWidget::currentItemChanged,
593 this, [this](QTreeWidgetItem *item) { d_ptr->slotCurrentPathChanged(item); });
594 connect(d_ptr->m_listWidget, &QListWidget::currentItemChanged,
595 this, [this](QListWidgetItem *item) { d_ptr->slotCurrentResourceChanged(item); });
596 connect(d_ptr->m_listWidget, &QListWidget::itemActivated,
597 this, [this](QListWidgetItem *item) { d_ptr->slotResourceActivated(item); });
598 d_ptr->m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
599 connect(d_ptr->m_listWidget, &QListWidget::customContextMenuRequested,
600 this, [this](const QPoint &point) { d_ptr->slotListWidgetContextMenuRequested(point); });
601}
602
603QtResourceView::~QtResourceView()
604{
605 if (!d_ptr->m_settingsKey.isEmpty())
606 d_ptr->saveSettings();
607}
608
609bool QtResourceView::event(QEvent *event)
610{
611 if (event->type() == QEvent::Show) {
612 d_ptr->m_listWidget->scrollToItem(d_ptr->m_listWidget->currentItem());
613 d_ptr->m_treeWidget->scrollToItem(d_ptr->m_treeWidget->currentItem());
614 }
615 return QWidget::event(event);
616}
617
618QtResourceModel *QtResourceView::model() const
619{
620 return d_ptr->m_resourceModel;
621}
622
623QString QtResourceView::selectedResource() const
624{
625 QListWidgetItem *item = d_ptr->m_listWidget->currentItem();
626 return d_ptr->m_itemToResource.value(item);
627}
628
629void QtResourceView::selectResource(const QString &resource)
630{
631 if (resource.isEmpty())
632 return;
633 QFileInfo fi(resource);
634 QDir dir = fi.absoluteDir();
635 if (fi.isDir())
636 dir = QDir(resource);
637 QString dirPath = dir.absolutePath();
638 const auto cend = d_ptr->m_pathToItem.constEnd();
639 auto it = cend;
640 while ((it = d_ptr->m_pathToItem.constFind(dirPath)) == cend) {
641 if (!dir.cdUp())
642 break;
643 dirPath = dir.absolutePath();
644 }
645 if (it != cend) {
646 QTreeWidgetItem *treeItem = it.value();
647 d_ptr->m_treeWidget->setCurrentItem(treeItem);
648 d_ptr->m_treeWidget->scrollToItem(treeItem);
649 // expand all up to current one is done by qt
650 // list widget is already propagated (currrent changed was sent by qt)
651 QListWidgetItem *item = d_ptr->m_resourceToItem.value(resource);
652 if (item) {
653 d_ptr->m_listWidget->setCurrentItem(item);
654 d_ptr->m_listWidget->scrollToItem(item);
655 }
656 }
657}
658
659QString QtResourceView::settingsKey() const
660{
661 return d_ptr->m_settingsKey;
662}
663
664void QtResourceView::setSettingsKey(const QString &key)
665{
666 if (d_ptr->m_settingsKey == key)
667 return;
668
669 d_ptr->m_settingsKey = key;
670
671 if (key.isEmpty())
672 return;
673
674 d_ptr->restoreSettings();
675}
676
677void QtResourceView::setResourceModel(QtResourceModel *model)
678{
679 if (d_ptr->m_resourceModel)
680 disconnect(d_ptr->m_resourceModel, &QtResourceModel::resourceSetActivated, this, nullptr);
681
682 // clear here
683 d_ptr->m_treeWidget->clear();
684 d_ptr->m_listWidget->clear();
685
686 d_ptr->m_resourceModel = model;
687
688 if (!d_ptr->m_resourceModel)
689 return;
690
691 connect(d_ptr->m_resourceModel, &QtResourceModel::resourceSetActivated,
692 this, [this](QtResourceSet *resource) { d_ptr->slotResourceSetActivated(resource); });
693
694 // fill new here
695 d_ptr->slotResourceSetActivated(d_ptr->m_resourceModel->currentResourceSet());
696}
697
698bool QtResourceView::isResourceEditingEnabled() const
699{
700 return d_ptr->m_resourceEditingEnabled;
701}
702
703void QtResourceView::setResourceEditingEnabled(bool enable)
704{
705 d_ptr->m_resourceEditingEnabled = enable;
706 d_ptr->updateActions();
707}
708
709void QtResourceView::setDragEnabled(bool dragEnabled)
710{
711 d_ptr->m_listWidget->setDragEnabled(dragEnabled);
712}
713
714bool QtResourceView::dragEnabled() const
715{
716 return d_ptr->m_listWidget->dragEnabled();
717}
718
719QString QtResourceView::encodeMimeData(ResourceType resourceType, const QString &path)
720{
721 QDomDocument doc;
722 QDomElement elem = doc.createElement(elementResourceData);
723 switch (resourceType) {
724 case ResourceImage:
725 elem.setAttribute(typeAttribute, typeImage);
726 break;
727 case ResourceStyleSheet:
728 elem.setAttribute(typeAttribute, typeStyleSheet);
729 break;
730 case ResourceOther:
731 elem.setAttribute(typeAttribute, typeOther);
732 break;
733 }
734 elem.setAttribute(fileAttribute, path);
735 doc.appendChild(elem);
736 return doc.toString();
737}
738
739bool QtResourceView::decodeMimeData(const QMimeData *md, ResourceType *t, QString *file)
740{
741 return md->hasText() ? decodeMimeData(md->text(), t, file) : false;
742}
743
744bool QtResourceView::decodeMimeData(const QString &text, ResourceType *t, QString *file)
745{
746
747 static auto docElementName = elementResourceData;
748 static const QString docElementString = u'<' + docElementName;
749
750 if (text.isEmpty() || text.indexOf(docElementString) == -1)
751 return false;
752
753 QDomDocument doc;
754 if (!doc.setContent(text))
755 return false;
756
757 const QDomElement domElement = doc.documentElement();
758 if (domElement.tagName() != docElementName)
759 return false;
760
761 if (t) {
762 const QString typeAttr = typeAttribute;
763 if (domElement.hasAttribute (typeAttr)) {
764 const QString typeValue = domElement.attribute(typeAttr, typeOther);
765 if (typeValue == typeImage) {
766 *t = ResourceImage;
767 } else {
768 *t = typeValue == typeStyleSheet ? ResourceStyleSheet : ResourceOther;
769 }
770 }
771 }
772 if (file) {
773 const QString fileAttr = fileAttribute;
774 if (domElement.hasAttribute(fileAttr)) {
775 *file = domElement.attribute(fileAttr, QString());
776 } else {
777 file->clear();
778 }
779 }
780 return true;
781}
782
783// ---------------------------- QtResourceViewDialogPrivate
784
785class QtResourceViewDialogPrivate
786{
787 QtResourceViewDialog *q_ptr;
788 Q_DECLARE_PUBLIC(QtResourceViewDialog)
789public:
790 QtResourceViewDialogPrivate(QDesignerFormEditorInterface *core);
791
792 void slotResourceSelected(const QString &resource) { setOkButtonEnabled(!resource.isEmpty()); }
793 void setOkButtonEnabled(bool v) { m_box->button(QDialogButtonBox::Ok)->setEnabled(v); }
794
795 QDesignerFormEditorInterface *m_core;
796 QtResourceView *m_view;
797 QDialogButtonBox *m_box;
798};
799
800QtResourceViewDialogPrivate::QtResourceViewDialogPrivate(QDesignerFormEditorInterface *core) :
801 q_ptr(nullptr),
802 m_core(core),
803 m_view(new QtResourceView(core)),
804 m_box(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel))
805{
806 m_view->setSettingsKey(ResourceViewDialogC);
807}
808
809// ------------ QtResourceViewDialog
810QtResourceViewDialog::QtResourceViewDialog(QDesignerFormEditorInterface *core, QWidget *parent) :
811 QDialog(parent),
812 d_ptr(new QtResourceViewDialogPrivate(core))
813{
814 setWindowTitle(tr("Select Resource"));
815 d_ptr->q_ptr = this;
816 QVBoxLayout *layout = new QVBoxLayout(this);
817 layout->addWidget(d_ptr->m_view);
818 layout->addWidget(d_ptr->m_box);
819 connect(d_ptr->m_box, &QDialogButtonBox::accepted, this, &QDialog::accept);
820 connect(d_ptr->m_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
821 connect(d_ptr->m_view, &QtResourceView::resourceActivated, this, &QDialog::accept);
822 connect(d_ptr->m_view, &QtResourceView::resourceSelected,
823 this, [this](const QString &resource) { d_ptr->slotResourceSelected(resource); });
824 d_ptr->setOkButtonEnabled(false);
825 d_ptr->m_view->setResourceModel(core->resourceModel());
826
827 QDesignerSettingsInterface *settings = core->settingsManager();
828 settings->beginGroup(ResourceViewDialogC);
829
830 const QVariant geometry = settings->value(qrvGeometry);
831 if (geometry.metaType().id() == QMetaType::QByteArray) // Used to be a QRect up until 5.4.0, QTBUG-43374.
832 restoreGeometry(geometry.toByteArray());
833
834 settings->endGroup();
835}
836
837QtResourceViewDialog::~QtResourceViewDialog()
838{
839 QDesignerSettingsInterface *settings = d_ptr->m_core->settingsManager();
840 settings->beginGroup(ResourceViewDialogC);
841
842 settings->setValue(qrvGeometry, saveGeometry());
843
844 settings->endGroup();
845}
846
847QString QtResourceViewDialog::selectedResource() const
848{
849 return d_ptr->m_view->selectedResource();
850}
851
852void QtResourceViewDialog::selectResource(const QString &path)
853{
854 d_ptr->m_view->selectResource(path);
855}
856
857bool QtResourceViewDialog::isResourceEditingEnabled() const
858{
859 return d_ptr->m_view->isResourceEditingEnabled();
860}
861
862void QtResourceViewDialog::setResourceEditingEnabled(bool enable)
863{
864 d_ptr->m_view->setResourceEditingEnabled(enable);
865}
866
867QT_END_NAMESPACE
868
869#include "moc_qtresourceview_p.cpp"
QMap< QString, bool > m_expansionState
QDesignerFormEditorInterface * m_core
QMap< QString, QStringList > m_pathToSubPaths
void slotFilterChanged(const QString &pattern)
QPixmap makeThumbnail(const QPixmap &pix) const
QTreeWidgetItem * createPath(const QString &path, QTreeWidgetItem *parent)
void slotCurrentResourceChanged(QListWidgetItem *)
void slotResourceSetActivated(QtResourceSet *resourceSet)
QHash< QListWidgetItem *, QString > m_itemToResource
QMap< QString, QTreeWidgetItem * > m_pathToItem
QMap< QString, QListWidgetItem * > m_resourceToItem
QHash< QTreeWidgetItem *, QString > m_itemToPath
QMap< QString, QString > m_pathToParentPath
void createResources(const QString &path)
void slotCurrentPathChanged(QTreeWidgetItem *)
QtResourceModel * m_resourceModel
QMap< QString, QStringList > m_pathToContents
void slotResourceActivated(QListWidgetItem *)
void slotListWidgetContextMenuRequested(const QPoint &pos)
ResourceListWidget(QWidget *parent=nullptr)
void startDrag(Qt::DropActions supportedActions) override
static constexpr auto ResourceViewDialogC
static constexpr auto qrvSplitterPosition
static constexpr auto elementResourceData
static constexpr auto typeStyleSheet
static constexpr auto typeAttribute
static constexpr auto typeOther
static constexpr auto qrvGeometry
static constexpr auto fileAttribute
static constexpr auto typeImage