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